Ways to toggle spring security SAML on and off - spring-security

I have a pretty standard implementation of spring security saml into my application in addition to other authentication mechanisms. Out of the box SAML will not be configured but can be configured through a form, so by default SAML should be disabled. I'd like to easily be able to toggle SAML on / off but am not sure what the best way to do this would be.
It seems like one approach would be to do a custom FilterChainProxy where if I check if saml is enabled and if so to ignore the samlFilter chain(How to delete one filter from default filter stack in Spring Security?) and also do a similar implementation for the Metadata Generator Filter.
Any advice would be great.
Here is my config:
<http auto-config="false" use-expressions="true"
access-decision-manager-ref="webAccessDecisionManager"
disable-url-rewriting="false"
create-session="never"
authentication-manager-ref="authenticationManager">
<custom-filter before="FIRST" ref="metadataGeneratorFilter"/>
<custom-filter after="BASIC_AUTH_FILTER" ref="samlFilter"/>
</http>
Metadata Generator Filter:
<beans:bean id="metadataGeneratorFilter" class="org.springframework.security.saml.metadata.MetadataGeneratorFilter">
<beans:constructor-arg>
<beans:bean class="org.springframework.security.saml.metadata.MetadataGenerator">
<beans:property name="entityId" value="${saml.entityId}"/>
<beans:property name="signMetadata" value="${saml.signMetadata}"/>
</beans:bean>
</beans:constructor-arg>
</beans:bean>
Saml Filter:
<beans:bean id="samlFilter" class="org.springframework.security.web.FilterChainProxy">
<filter-chain-map request-matcher="ant">
<filter-chain pattern="/saml/login/**" filters="samlEntryPoint"/>
<filter-chain pattern="/saml/logout/**" filters="samlLogoutFilter"/>
<filter-chain pattern="/saml/metadata/**" filters="metadataDisplayFilter"/>
<filter-chain pattern="/saml/SSO/**" filters="samlWebSSOProcessingFilter"/>
<filter-chain pattern="/saml/SSOHoK/**" filters="samlWebSSOHoKProcessingFilter"/>
<filter-chain pattern="/saml/SingleLogout/**" filters="samlLogoutProcessingFilter"/>
<filter-chain pattern="/saml/discovery/**" filters="samlIDPDiscovery"/>
</filter-chain-map>
</beans:bean>
EDIT: Here is my implementation, it is a bit hackish and relies on a deprecated method but it works
The below snippet disables MetadataGeneratorFilter:
public class MyMetadataGeneratorFilter extends MetadataGeneratorFilter {
private boolean isActive = false;
public MyMetadataGeneratorFilter(MetadataGenerator generator) {
super(generator);
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
if (isActive) {
processMetadataInitialization((HttpServletRequest) request);
}
chain.doFilter(request, response);
}
public void setActive(boolean active) {
isActive = active;
}
}
There is also the samlFilter / FilterChainMap which is autowired. If saml is enabled, I leave this chain as is, if it is disabled, I set the chain to an empty map in my service which enables / disables saml.
Upon initialization, I get the filterchainmap values:
private Map<RequestMatcher, List<Filter>> map;
#Override
public void init() throws ServiceException, MetadataProviderException {
SamlConfig samlConfig = getConfig();
map = samlFilter.getFilterChainMap();
applySamlConfig(samlConfig);
}
In the below method, I set the filter chain map to either the original map provided in the spring xml(if enabled) or an empty map (if disabled).
public void applySamlConfig(SamlConfig samlConfig) throws ServiceException, MetadataProviderException {
if (!samlConfig.isEnabled()) {
Map<RequestMatcher, List<Filter>> emptyMap = samlFilter.getFilterChainMap();
emptyMap.clear();
samlFilter.setFilterChainMap(emptyMap);
return;
}
samlFilter.setFilterChainMap(map);

i added a custom filter in the entry-point-ref definition. This filter skips all following filters if the feature is not enabled.
<security:http entry-point-ref="samlEntryPoint">
<security:intercept-url pattern="/**" access="IS_AUTHENTICATED_FULLY" />
<!-- This filter checks if the SSO-Feature is enabled - otherwise all following security filters will be skipped -->
<security:custom-filter before="BASIC_AUTH_FILTER" ref="ssoEnabledFilter"/>
<security:custom-filter before="FIRST" ref="metadataGeneratorFilter" />
<security:custom-filter after="BASIC_AUTH_FILTER" ref="samlFilter" />
The ssoEnabledFilter:
public class SsoEnabledFilter extends GenericFilterBean {
#Override
public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain filterChain) throws IOException, ServletException {
boolean ssoEnabled = isSsoEnabled();
if (ssoEnabled) {
filterChain.doFilter(request, response);
} else {
request.getRequestDispatcher(((HttpServletRequest) request).getServletPath()).forward(request, response);
}
}
}

So far I've been implementing this using a custom Spring namespace which includes or skips certain beans based on the backend configuration and reloading of the Spring context in case the backend configuration changes.

Edit : fixed error signaled by TheTurkish
If you want to be able to switch the use of SAML on a running application, the simpler would be to use a wrapper around samlFilter. For example
public class FilterWrapper extends GenericFilterBean {
private Filter inner;
private boolean active;
private boolean targetFilterLifeCycle = false;
public Filter getInner() {
return inner;
}
public void setInner(Filter inner) {
this.inner = inner;
}
public boolean isActive() {
return active;
}
public void setActive(boolean active) {
this.active = active;
}
#Override
public void doFilter(ServletRequest sr, ServletResponse sr1, FilterChain fc) throws IOException, ServletException {
if (active) {
inner.doFilter(sr, sr1, fc);
}
else {
fc.doFilter(str,sr1);
}
}
#Override
protected void initFilterBean() throws ServletException {
super.initFilterBean();
if (inner == null) {
throw new ServletException("Inner cannot be null");
}
if (targetFilterLifeCycle) {
inner.init(getFilterConfig());
}
}
#Override
public void destroy() {
super.destroy();
if (inner != null && targetFilterLifeCycle) {
inner.destroy();
}
}
}
You can use it that way :
<bean id="samlFilter" class="...FilterWrapper" p:active="false">
<property name=inner>
<!-- the real samlFilter bean -->
<bean class="org.springframework.security.web.FilterChainProxy">
...
</bean>
</property>
</bean>
As it is a bean, you inject it where you want to activate/deactivate Saml and simple call :
samlFilter.setActive(active);

Related

Spring security Kerberos Authentication Failure in IBM Websphere Application server 8.5

I am trying to implement Single sign on using spring security. The application is hosted in IBM Websphere Application Server 8.5 (IBM JDK 7).
I've gone through http://docs.spring.io/autorepo/docs/spring-security-kerberos/1.0.2.BUILD-SNAPSHOT/reference/htmlsingle/
My security configuration file looks like this :
<sec:http entry-point-ref="spnegoEntryPoint" use-expressions="true">
<sec:intercept-url pattern="/contents/**" access="permitAll"/>
<sec:intercept-url pattern="/favicon.ico" access="permitAll"/>
<sec:intercept-url pattern="/WEB-INF/tags/**" access="permitAll"/>
<sec:intercept-url pattern="/systemuser/login**" access="permitAll"/>
<sec:intercept-url pattern="/systemuser/authentication/failure" access="permitAll"/>
<sec:intercept-url pattern="/systemuser/logout**" access="permitAll"/>
<sec:intercept-url pattern="/systemuser/405" access="permitAll"/>
<sec:intercept-url pattern="/systemuser/noscript" access="permitAll"/>
<sec:intercept-url pattern="/systemuser/**" access="isAuthenticated()"/>
<sec:form-login login-page="/systemuser/login" authentication-failure-url="/systemuser/login?error=true"
authentication-success-handler-ref="systemUserAuthenticationSuccessHandler"/>
<sec:logout logout-url="/systemuser/logout" success-handler-ref="systemUserLogoutSuccessHandler"/>
<sec:access-denied-handler error-page="/403"/>
<!-- enable csrf protection -->
<sec:csrf/>
<sec:headers/>
<sec:custom-filter ref="spnegoAuthenticationProcessingFilter"
before="BASIC_AUTH_FILTER"/>
</sec:http>
<sec:authentication-manager alias="authenticationManager">
<sec:authentication-provider ref="kerberosAuthenticationProvider"/>
<sec:authentication-provider ref="kerberosServiceAuthenticationProvider"/>
</sec:authentication-manager>
<bean id="kerberosAuthenticationProvider"
class="org.springframework.security.kerberos.authentication.KerberosAuthenticationProvider">
<property name="userDetailsService" ref="systemUserDetailsService"/>
<property name="kerberosClient">
<bean class="com.fdiapp.authentication.systemuser.service.IbmJaasKerberosClient">
<property name="debug" value="true"/>
</bean>
</property>
</bean>
<bean id="spnegoEntryPoint"
class="org.springframework.security.kerberos.web.authentication.SpnegoEntryPoint">
<constructor-arg value="/systemuser/login"/>
</bean>
<bean id="spnegoAuthenticationProcessingFilter"
class="org.springframework.security.kerberos.web.authentication.SpnegoAuthenticationProcessingFilter">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="successHandler" ref="systemUserAuthenticationSuccessHandler"/>
<property name="failureHandler" ref="systemUserAuthenticationFailureHandler"/>
</bean>
<bean id="kerberosServiceAuthenticationProvider"
class="org.springframework.security.kerberos.authentication.KerberosServiceAuthenticationProvider">
<property name="ticketValidator">
<bean
class="com.fdiapp.authentication.systemuser.service.IbmJaasKerberosTicketValidator">
<property name="servicePrincipal" value="HTTP/MYDOMAIN"/>
<property name="keyTabLocation" value="/WEB-INF/keyfile/hbdap-hkkdd001.keytab"/>
<property name="debug" value="true"/>
</bean>
</property>
<property name="userDetailsService" ref="systemUserDetailsService"/>
</bean>
<bean id="systemUserDetailsService"
class="com.fdiapp.authentication.systemuser.service.SystemUserDetailsService"/>
<bean id="systemUserAuthenticationSuccessHandler"
class="com.fdiapp.authentication.handler.SystemUserAuthenticationSuccessHandler"/>
<bean id="systemUserLogoutSuccessHandler"
class="com.fdiapp.authentication.handler.SystemUserLogoutSuccessHandler"/>
<bean id="systemUserAuthenticationFailureHandler"
class="com.fdiapp.authentication.handler.SystemUserAuthenticationFailureHandler"/>
Kerberos client :
public class IbmJaasKerberosClient implements KerberosClient {
private boolean debug = true;
private static final Log LOG = LogFactory.getLog(IbmJaasKerberosClient.class);
#Override
public String login(String username, String password) {
LOG.debug("Trying to authenticate " + username + " with Kerberos");
String validatedUsername;
try {
LoginContext loginContext = new LoginContext("", null, new KerberosClientCallbackHandler(username, password),
new LoginConfig(this.debug));
loginContext.login();
if (LOG.isDebugEnabled()) {
LOG.debug("Kerberos authenticated user: "+loginContext.getSubject());
}
validatedUsername = loginContext.getSubject().getPrincipals().iterator().next().toString();
loginContext.logout();
} catch (LoginException e) {
if(LOG.isDebugEnabled()){
LOG.error(e.getMessage());
}
throw new BadCredentialsException("Kerberos authentication failed", e);
}
return validatedUsername;
}
public void setDebug(boolean debug) {
this.debug = debug;
}
private static class LoginConfig extends Configuration {
private boolean debug;
public LoginConfig(boolean debug) {
super();
this.debug = debug;
}
#Override
public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
HashMap<String, String> options = new HashMap<String, String>();
options.put("credsType", "acceptor");
if (debug) {
options.put("debug", "true");
}
return new AppConfigurationEntry[] { new AppConfigurationEntry("com.ibm.security.auth.module.Krb5LoginModule",
AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, options), };
}
}
private static class KerberosClientCallbackHandler implements CallbackHandler {
private String username;
private String password;
public KerberosClientCallbackHandler(String username, String password) {
this.username = username;
this.password = password;
}
#Override
public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
for (Callback callback : callbacks) {
if (callback instanceof NameCallback) {
NameCallback ncb = (NameCallback) callback;
ncb.setName(username);
} else if (callback instanceof PasswordCallback) {
PasswordCallback pwcb = (PasswordCallback) callback;
pwcb.setPassword(password.toCharArray());
} else {
throw new UnsupportedCallbackException(callback, "We got a " + callback.getClass().getCanonicalName()
+ ", but only NameCallback and PasswordCallback is supported");
}
}
}
}
}
Kereros Ticket Validator :
public class IbmJaasKerberosTicketValidator implements KerberosTicketValidator, InitializingBean {
private String servicePrincipal;
private Resource keyTabLocation;
private Subject serviceSubject;
private boolean holdOnToGSSContext;
private boolean debug = true;
private static final Log LOG = LogFactory.getLog(IbmJaasKerberosTicketValidator.class);
#Override
public KerberosTicketValidation validateTicket(byte[] token) {
try {
return Subject.doAs(this.serviceSubject, new KerberosValidateAction(token));
}
catch (PrivilegedActionException e) {
throw new BadCredentialsException("Kerberos validation not successful", e);
}
}
#Override
public void afterPropertiesSet() throws Exception {
Assert.notNull(this.servicePrincipal, "servicePrincipal must be specified");
Assert.notNull(this.keyTabLocation, "keyTab must be specified");
if (keyTabLocation instanceof ClassPathResource) {
LOG.warn("Your keytab is in the classpath. This file needs special protection and shouldn't be in the classpath. JAAS may also not be able to load this file from classpath.");
}
String keyTabLocationAsString = this.keyTabLocation.getURL().toExternalForm();
// We need to remove the file prefix (if there is one), as it is not supported in Java 7 anymore.
// As Java 6 accepts it with and without the prefix, we don't need to check for Java 7
if (keyTabLocationAsString.startsWith("file:"))
{
keyTabLocationAsString = keyTabLocationAsString.substring(5);
}
LoginConfig loginConfig = new LoginConfig(keyTabLocationAsString, this.servicePrincipal,
this.debug);
Set<Principal> princ = new HashSet<Principal>(1);
princ.add(new KerberosPrincipal(this.servicePrincipal));
Subject sub = new Subject(false, princ, new HashSet<Object>(), new HashSet<Object>());
LoginContext lc = new LoginContext("", sub, null, loginConfig);
lc.login();
this.serviceSubject = lc.getSubject();
}
/**
* The service principal of the application.
* For web apps this is <code>HTTP/full-qualified-domain-name#DOMAIN</code>.
* The keytab must contain the key for this principal.
*
* #param servicePrincipal service principal to use
* #see #setKeyTabLocation(Resource)
*/
public void setServicePrincipal(String servicePrincipal) {
this.servicePrincipal = servicePrincipal;
}
/**
* <p>The location of the keytab. You can use the normale Spring Resource
* prefixes like <code>file:</code> or <code>classpath:</code>, but as the
* file is later on read by JAAS, we cannot guarantee that <code>classpath</code>
* works in every environment, esp. not in Java EE application servers. You
* should use <code>file:</code> there.
*
* This file also needs special protection, which is another reason to
* not include it in the classpath but rather use <code>file:/etc/http.keytab</code>
* for example.
*
* #param keyTabLocation The location where the keytab resides
*/
public void setKeyTabLocation(Resource keyTabLocation) {
this.keyTabLocation = keyTabLocation;
}
/**
* Enables the debug mode of the JAAS Kerberos login module.
*
* #param debug default is false
*/
public void setDebug(boolean debug) {
this.debug = debug;
}
/**
* Determines whether to hold on to the {#link GSSContext GSS security context} or
* otherwise {#link GSSContext#dispose() dispose} of it immediately (the default behaviour).
* <p>Holding on to the GSS context allows decrypt and encrypt operations for subsequent
* interactions with the principal.
*
* #param holdOnToGSSContext true if should hold on to context
*/
public void setHoldOnToGSSContext(boolean holdOnToGSSContext) {
this.holdOnToGSSContext = holdOnToGSSContext;
}
/**
* This class is needed, because the validation must run with previously generated JAAS subject
* which belongs to the service principal and was loaded out of the keytab during startup.
*/
private class KerberosValidateAction implements PrivilegedExceptionAction<KerberosTicketValidation> {
byte[] kerberosTicket;
public KerberosValidateAction(byte[] kerberosTicket) {
this.kerberosTicket = kerberosTicket;
}
#Override
public KerberosTicketValidation run() throws Exception {
byte[] responseToken = new byte[0];
GSSName gssName = null;
GSSContext context = GSSManager.getInstance().createContext((GSSCredential) null);
boolean first = true;
while (!context.isEstablished()) {
if (first) {
kerberosTicket = tweakJdkRegression(kerberosTicket);
}
responseToken = context.acceptSecContext(kerberosTicket, 0, kerberosTicket.length);
gssName = context.getSrcName();
if (gssName == null) {
throw new BadCredentialsException("GSSContext name of the context initiator is null");
}
first = false;
}
if (!holdOnToGSSContext) {
context.dispose();
}
return new KerberosTicketValidation(gssName.toString(), servicePrincipal, responseToken, context);
}
}
/**
* Normally you need a JAAS config file in order to use the JAAS Kerberos Login Module,
* with this class it is not needed and you can have different configurations in one JVM.
*/
private static class LoginConfig extends Configuration {
private String keyTabLocation;
private String servicePrincipalName;
private boolean debug;
public LoginConfig(String keyTabLocation, String servicePrincipalName, boolean debug) {
this.keyTabLocation = keyTabLocation;
this.servicePrincipalName = servicePrincipalName;
this.debug = debug;
}
#Override
public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
HashMap<String, String> options = new HashMap<String, String>();
options.put("useKeytab", this.keyTabLocation);
options.put("principal", this.servicePrincipalName);
options.put("credsType", "acceptor");
if (this.debug) {
options.put("debug", "true");
}
return new AppConfigurationEntry[] { new AppConfigurationEntry("com.ibm.security.auth.module.Krb5LoginModule",
AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, options), };
}
}
private static byte[] tweakJdkRegression(byte[] token) throws GSSException {
if (token == null || token.length < 48) {
return token;
}
int[] toCheck = new int[] { 0x06, 0x09, 0x2A, 0x86, 0x48, 0x82, 0xF7, 0x12, 0x01, 0x02, 0x02, 0x06, 0x09, 0x2A,
0x86, 0x48, 0x86, 0xF7, 0x12, 0x01, 0x02, 0x02 };
for (int i = 0; i < 22; i++) {
if ((byte) toCheck[i] != token[i + 24]) {
return token;
}
}
byte[] nt = new byte[token.length];
System.arraycopy(token, 0, nt, 0, 24);
System.arraycopy(token, 35, nt, 24, 11);
System.arraycopy(token, 24, nt, 35, 11);
System.arraycopy(token, 46, nt, 46, token.length - 24 - 11 - 11);
return nt;
}
}
I am getting the following error message :
[8/11/16 11:33:59:201 HKT] 000000c4 SpnegoAuthent W org.springframework.security.kerberos.web.authentication.SpnegoAuthenticationProcessingFilter doFilter Negotiate Header was invalid: Negotiate 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
org.springframework.security.authentication.BadCredentialsException: Kerberos validation not successful
at com.fdiapp.authentication.systemuser.service.IbmJaasKerberosTicketValidator.validateTicket(IbmJaasKerberosTicketValidator.java:50)
at org.springframework.security.kerberos.authentication.KerberosServiceAuthenticationProvider.authenticate(KerberosServiceAuthenticationProvider.java:64)
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:156)
at org.springframework.security.kerberos.web.authentication.SpnegoAuthenticationProcessingFilter.doFilter(SpnegoAuthenticationProcessingFilter.java:145)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:199)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:110)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.csrf.CsrfFilter.doFilterInternal(CsrfFilter.java:85)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:57)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:50)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192)
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160)
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:344)
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:261)
at com.ibm.ws.webcontainer.filter.FilterInstanceWrapper.doFilter(FilterInstanceWrapper.java:195)
at com.ibm.ws.webcontainer.filter.WebAppFilterChain.doFilter(WebAppFilterChain.java:91)
at org.springframework.web.multipart.support.MultipartFilter.doFilterInternal(MultipartFilter.java:118)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at com.ibm.ws.webcontainer.filter.FilterInstanceWrapper.doFilter(FilterInstanceWrapper.java:195)
at com.ibm.ws.webcontainer.filter.WebAppFilterChain.doFilter(WebAppFilterChain.java:91)
at com.ibm.ws.webcontainer.filter.WebAppFilterManager.doFilter(WebAppFilterManager.java:967)
at com.ibm.ws.webcontainer.filter.WebAppFilterManager.invokeFilters(WebAppFilterManager.java:1107)
at com.ibm.ws.webcontainer.servlet.CacheServletWrapper.handleRequest(CacheServletWrapper.java:87)
at com.ibm.ws.webcontainer.WebContainer.handleRequest(WebContainer.java:940)
at com.ibm.ws.webcontainer.WSWebContainer.handleRequest(WSWebContainer.java:1817)
at com.ibm.ws.webcontainer.channel.WCChannelLink.ready(WCChannelLink.java:200)
at com.ibm.ws.http.channel.inbound.impl.HttpInboundLink.handleDiscrimination(HttpInboundLink.java:463)
at com.ibm.ws.http.channel.inbound.impl.HttpInboundLink.handleNewRequest(HttpInboundLink.java:530)
at com.ibm.ws.http.channel.inbound.impl.HttpInboundLink.processRequest(HttpInboundLink.java:316)
at com.ibm.ws.http.channel.inbound.impl.HttpInboundLink.ready(HttpInboundLink.java:287)
at com.ibm.io.async.AsyncChannelFuture$1.run(AsyncChannelFuture.java:205)
at com.ibm.ws.util.ThreadPool$Worker.run(ThreadPool.java:1881)
Caused by: java.security.PrivilegedActionException: org.ietf.jgss.GSSException, major code: 13, minor code: 0
major string: Invalid credentials
minor string: Cannot obtain mechanism credential for mechanism 1.3.6.1.5.5.2
at java.security.AccessController.doPrivileged(AccessController.java:458)
at javax.security.auth.Subject.doAs(Subject.java:572)
atcom.fdiapp.authentication.systemuser.service.IbmJaasKerberosTicketValidator.validateTicket(IbmJaasKerberosTicketValidator.java:47)
Could anyone suggest me how can I make the above configuration workable?
Or
How can I retrieve user from the negotiate header using spn and ketab file so that I can authenticate.
Maybe this might help:
Oid krb5MechOid = new Oid("1.2.840.113554.1.2.2");
Oid spnegoMechOid = new Oid("1.3.6.1.5.5.2");
System.setProperty("javax.security.auth.useSubjectCredsOnly", "true");
GSSManager manager = GSSManager.getInstance();
GSSName gssUserName = manager.createName(userName, GSSName.NT_USER_NAME, krb5MechOid);
clientGssCreds = manager.createCredential(gssUserName.canonicalize(krb5MechOid),
GSSCredential.INDEFINITE_LIFETIME,
krb5MechOid,
GSSCredential.INITIATE_ONLY);
clientGssCreds.add (gssUserName,
GSSCredential.INDEFINITE_LIFETIME,
GSSCredential.INDEFINITE_LIFETIME,
spnegoMechOid,
GSSCredential.INITIATE_ONLY);
http://www.ibm.com/support/knowledgecenter/en/SS7K4U_8.5.5/com.ibm.websphere.zseries.doc/ae/tsec_SPNEGO_token.html

Why does filter chain continue if no token is passed in header with OAuth2 spring security?

I am trying to use OAuth2 with spring security, with the following config xml:
<http pattern="/oauth/token" create-session="stateless" authentication-manager-ref="oauthUserAuthenticationManager"
xmlns="http://www.springframework.org/schema/security">
<intercept-url pattern="/oauth/token" access="IS_AUTHENTICATED_FULLY"/>
<anonymous enabled="false"/>
<http-basic entry-point-ref="clientAuthenticationEntryPoint"/>
<custom-filter ref="clientCredentialsTokenEndpointFilter" before="BASIC_AUTH_FILTER"/>
</http>
<http auto-config="true" pattern="/services/rest/**" create-session="never" entry-point-ref="oauthAuthenticationEntryPoint"
xmlns="http://www.springframework.org/schema/security">
<anonymous enabled="false"/>
<intercept-url pattern="/services/rest/**"/>
<custom-filter ref="resourceServerFilter" before="PRE_AUTH_FILTER"/>
<access-denied-handler ref="oauthAccessDeniedHandler"/>
</http>
I successfully generated a token. Now while trying to access the secured resources, if I pass the token in header, everything works fine.
But if I don't pass the token, the security is bypassed.
I checked the code of OAuth2AuthenticationProcessingFilter:
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException,
ServletException {
final boolean debug = logger.isDebugEnabled();
final HttpServletRequest request = (HttpServletRequest) req;
final HttpServletResponse response = (HttpServletResponse) res;
try {
Authentication authentication = tokenExtractor.extract(request);
if (authentication == null) { // If token is null, do nothing
if (debug) {
logger.debug("No token in request, will continue chain.");
}
}
else {
request.setAttribute(OAuth2AuthenticationDetails.ACCESS_TOKEN_VALUE, authentication.getPrincipal());
if (authentication instanceof AbstractAuthenticationToken) {
AbstractAuthenticationToken needsDetails = (AbstractAuthenticationToken) authentication;
needsDetails.setDetails(authenticationDetailsSource.buildDetails(request));
}
Authentication authResult = authenticationManager.authenticate(authentication);
if (debug) {
logger.debug("Authentication success: " + authResult);
}
SecurityContextHolder.getContext().setAuthentication(authResult);
}
}
catch (OAuth2Exception failed) {
SecurityContextHolder.clearContext();
if (debug) {
logger.debug("Authentication request failed: " + failed);
}
authenticationEntryPoint.commence(request, response,
new InsufficientAuthenticationException(failed.getMessage(), failed));
return;
}
chain.doFilter(request, response);
}
Any idea, why does this filter skips security if token is not present? Should I add some other filter to handle this case?
Try
<intercept-url pattern="/**" access="IS_AUTHENTICATED_FULLY"/>

Spring Security 3.2.3 RELEASE with JavaConfig

I have a Spring Security configured in XML that works just fine. Now, I'm trying to have it expressed in JavaConfig only so as to get rid of the XML configuration altogether.
I've looked at the reference documentation, and at many blogs and support requests, but I still cannot find the solution.
It gives me the following exception:
Could not autowire field: private org.springframework.security.web.FilterChainProxy
com.thalasoft.learnintouch.rest.config.WebTestConfiguration.springSecurityFilterChain;
Pitifully I resorted to post my own request here...
The code:
#Configuration
#ComponentScan(basePackages = { "com.thalasoft.learnintouch.rest" })
public class WebTestConfiguration {
#Autowired
private WebApplicationContext webApplicationContext;
#Autowired
private FilterChainProxy springSecurityFilterChain;
}
public class SecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer {
}
public class WebInit implements WebApplicationInitializer {
private static Logger logger = LoggerFactory.getLogger(WebInit.class);
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
registerListener(servletContext);
registerDispatcherServlet(servletContext);
registerJspServlet(servletContext);
}
private void registerListener(ServletContext servletContext) {
// Create the root application context
AnnotationConfigWebApplicationContext appContext = createContext(ApplicationConfiguration.class, WebSecurityConfiguration.class);
// Set the application display name
appContext.setDisplayName("LearnInTouch");
// Create the Spring Container shared by all servlets and filters
servletContext.addListener(new ContextLoaderListener(appContext));
}
private void registerDispatcherServlet(ServletContext servletContext) {
AnnotationConfigWebApplicationContext webApplicationContext = createContext(WebConfiguration.class);
ServletRegistration.Dynamic dispatcher = servletContext.addServlet("dispatcher", new DispatcherServlet(webApplicationContext));
dispatcher.setLoadOnStartup(1);
Set<String> mappingConflicts = dispatcher.addMapping("/");
if (!mappingConflicts.isEmpty()) {
for (String mappingConflict : mappingConflicts) {
logger.error("Mapping conflict: " + mappingConflict);
}
throw new IllegalStateException(
"The servlet cannot be mapped to '/'");
}
}
private void registerJspServlet(ServletContext servletContext) {
}
private AnnotationConfigWebApplicationContext createContext(final Class... modules) {
AnnotationConfigWebApplicationContext appContext = new AnnotationConfigWebApplicationContext();
appContext.register(modules);
return appContext;
}
}
#Configuration
#EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
CustomAuthenticationProvider customAuthenticationProvider;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(customAuthenticationProvider);
}
#Bean
public DelegatingFilterProxy springSecurityFilterChain() {
DelegatingFilterProxy filterProxy = new DelegatingFilterProxy();
return filterProxy;
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/resources/**");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/**").hasRole("ROLE_ADMIN").and().httpBasic();
http.authorizeRequests().antMatchers("/admin/login", "/admin/logout", "/admin/denied").permitAll()
.antMatchers("/admin/**").hasRole("ROLE_ADMIN")
.and()
.formLogin()
.loginPage("/admin/login")
.defaultSuccessUrl("/admin/list")
.failureUrl("/admin/denied?failed=true")
.and()
.rememberMe();
http.logout().logoutUrl("/admin/logout").logoutSuccessUrl("/admin/login").deleteCookies("JSESSIONID");
}
}
The XML configuration that I hope to get rid of:
<!-- A REST authentication -->
<http use-expressions="true" pattern="/admin/**">
<intercept-url pattern="/**" access="hasRole('ROLE_ADMIN')" />
<http-basic entry-point-ref="restAuthenticationEntryPoint" />
<logout />
</http>
<!-- A form based browser authentication -->
<http auto-config="true" use-expressions="true">
<intercept-url pattern="/admin/login" access="permitAll" />
<intercept-url pattern="/admin/logout" access="permitAll" />
<intercept-url pattern="/admin/denied" access="permitAll" />
<intercept-url pattern="/admin/**" access="hasRole('ROLE_ADMIN')" />
<form-login
login-page="/admin/login"
default-target-url="/admin/list"
authentication-failure-url="/admin/denied?failed=true"
always-use-default-target="true" />
<logout logout-success-url="/admin/login" />
<logout delete-cookies="JSESSIONID" />
</http>
<!-- A custom authentication provider on legacy data -->
<authentication-manager>
<authentication-provider ref="customAuthenticationProvider" />
</authentication-manager>
UPDATE:
I added a Configuration directive:
#Configuration
public class SecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer {
}
and an explicit import directive:
#Import({ SecurityWebApplicationInitializer.class })
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
}
But the exception remained the exact same.
I'm running Spring Security 3.2.4.RELEASE and Spring 3.2.9.RELEASE
If you have any suggestion, it is welcomed.
I removed this bean definition from the security configuration and it seems to have solved the issue
#Bean
public DelegatingFilterProxy springSecurityFilterChain() {
DelegatingFilterProxy filterProxy = new DelegatingFilterProxy();
return filterProxy;
}

PreAuthentication with Spring Security -> Based on URL parameters

The customer want to have the following scenario:
Customer hands out link (webapp address) with 2 parameters to the webapp user. Based on these variables the user will take on specific roles in the webapp.
I don't want any authorization in it. There should only be the authentication check which looks at these url parameters and checks if they are valid and will connect the user to the appropriate role.
How can I realize this?! Is there already a solution available?
Thanks!
regards Matthias
I already solved the problem.
For those who are interested ....
web.xml
<!-- ===== SPRING CONFIG ===== -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/applicationContext.xml
/WEB-INF/applicationContext-security.xml
</param-value>
</context-param>
applicationContext.xml
<context:component-scan base-package="at.beko.rainstar2" />
<tx:annotation-driven transaction-manager="transactionManager" />
applicationContext-security.xml
<!-- Configuring security not finished!! -->
<http create-session="never" use-expressions="true" auto-config="false"
entry-point-ref="preAuthenticatedProcessingFilterEntryPoint">
<intercept-url pattern="/authError.xhtml" access="permitAll" />
<intercept-url pattern="/**" access="hasRole('ROLE_USER')" />
<custom-filter position="PRE_AUTH_FILTER" ref="preAuthFilter" />
<session-management session-fixation-protection="none" />
</http>
<beans:bean id="userDetailsServiceImpl"
class="at.beko.rainstar2.service.impl.UserDetailsServiceImpl" />
<beans:bean id="preAuthenticatedProcessingFilterEntryPoint"
class="at.beko.rainstar2.model.LinkForbiddenEntryPoint" />
<beans:bean id="preAuthenticationProvider"
class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider">
<beans:property name="preAuthenticatedUserDetailsService"
ref="userDetailsServiceImpl" />
</beans:bean>
<beans:bean id="preAuthFilter"
class="at.beko.rainstar2.service.filter.UrlParametersAuthenticationFilter">
<beans:property name="authenticationManager" ref="appControlAuthenticationManager" />
</beans:bean>
<authentication-manager alias="appControlAuthenticationManager">
<authentication-provider ref="preAuthenticationProvider" />
</authentication-manager>
LinkForbiddenEntryPoint.java
public class LinkForbiddenEntryPoint implements AuthenticationEntryPoint {
#Override
public void commence(HttpServletRequest request,
HttpServletResponse response, AuthenticationException authException)
throws IOException, ServletException {
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.sendRedirect("/rainstar2-webapp/authError.xhtml");
}
}
UrlParametersAuthenticationFilter.java
public class UrlParametersAuthenticationFilter extends
AbstractPreAuthenticatedProcessingFilter {
#Override
protected Object getPreAuthenticatedPrincipal(HttpServletRequest request) {
if (request.getParameterMap().size() == 2) {
return true;
}
return false;
}
#Override
protected Object getPreAuthenticatedCredentials(HttpServletRequest request) {
String[] credentials = new String[2];
credentials[0] = request.getParameter("param1");
credentials[1] = request.getParameter("param2");
return credentials;
}
}
UserDetailsServiceImpl.java
#SuppressWarnings("deprecation")
public class UserDetailsServiceImpl implements
AuthenticationUserDetailsService<Authentication> {
#Override
public UserDetails loadUserDetails(Authentication token)
throws UsernameNotFoundException {
UserDetails userDetails = null;
String[] credentials = (String[]) token.getPrincipal();
boolean principal = Boolean.valueOf(token.getCredentials().toString());
if (credentials != null && principal == true) {
String name = credentials[0];
if ("admin".equalsIgnoreCase(name)) {
userDetails = getAdminUser(name);
} else if ("händler".equalsIgnoreCase(name)) {
userDetails = getRetailerUser(name);
} else if ("user".equalsIgnoreCase(name)) {
userDetails = getUserUser(name);
}
}
if (userDetails == null) {
throw new UsernameNotFoundException("Could not load user : "
+ token.getName());
}
return userDetails;
}
private UserDetails getAdminUser(String username) {
Collection<GrantedAuthority> grantedAuthorities = new ArrayList<GrantedAuthority>();
grantedAuthorities.add(new GrantedAuthorityImpl("ROLE_USER"));
grantedAuthorities.add(new GrantedAuthorityImpl("ROLE_RETAILER"));
grantedAuthorities.add(new GrantedAuthorityImpl("ROLE_ADMIN"));
return new User(username, "notused", true, true, true, true,
grantedAuthorities);
}
private UserDetails getRetailerUser(String username) {
Collection<GrantedAuthority> grantedAuthorities = new ArrayList<GrantedAuthority>();
grantedAuthorities.add(new GrantedAuthorityImpl("ROLE_USER"));
grantedAuthorities.add(new GrantedAuthorityImpl("ROLE_RETAILER"));
return new User(username, "notused", true, true, true, true,
grantedAuthorities);
}
private UserDetails getUserUser(String username) {
Collection<GrantedAuthority> grantedAuthorities = new ArrayList<GrantedAuthority>();
grantedAuthorities.add(new GrantedAuthorityImpl("ROLE_USER"));
return new User(username, "notused", true, true, true, true,
grantedAuthorities);
}
}
The way I have resolved this with similar situations is to to use a servlet filter to grab the parameters. I would recommend extending the org.springframework.web.filter.GenericFilterBean.
From these parameters, create an auth object of some sort (such as a token), that can be passed into the AuthenticationManager which you can autowire in (or get in some other method).
You will then need to have an AuthenticationProvider that can handle your auth object and generate a UserDetails object with the GrantedAuthority collection you need to satisfy the specific roles you want the user to have.

Custom PasswordEncoder

I need to create a custom password encoder. I have completed to following tasks :
applicationContext-Security.xml
<authentication-manager alias="authenticationManager">
<authentication-provider>
<password-encoder ref="AppPasswordEncoder" />
<jdbc-user-service data-source-ref="dataSource" authorities-by-username-query="select username,password from username where username=?"/>
</authentication-provider>
<beans:bean class="com.app.security.MyPasswordEncoder" id="AppPasswordEncoder"/>
</authentication-manager>
class
public class SnatiPasswordEncoder implements PasswordEncoder {
#Override
public String encodePassword(String arg0, Object arg1)
throws DataAccessException {
return null;
}
#Override
public boolean isPasswordValid(String arg0, String arg1, Object arg2)
throws DataAccessException {
return false;
}
}
Several steps to encode the password :
ISO-8859-1
md5
base64
What should be my next step ?

Resources