Pre-authentication in Spring Security + LDAP - java

Here is what I want to achieve:
I am using Websphere and I want to rely on the container to do the authentication (using Kerberos+SPNEGO). When it come to Spring Security, I want to rely on the pre-authentication, and use LDAP to retrieve user details (roles etc) for authorization checking.
Here is the part of Spring app context config I have (tried to only include related parts)
<s:global-method-security secured-annotations="enabled" pre-post-annotations="enabled" proxy-target-class="true" />
<bean id="springSecurityFilterChain" class="org.springframework.security.web.FilterChainProxy">
<s:filter-chain-map path-type="ant">
<s:filter-chain pattern="/**"
filters="securityContextPersistenceFilter,preAuthenticatedFilter" />
</s:filter-chain-map>
</bean>
<s:http use-expressions="true" create-session="stateless" auto-config="true">
<!--
<s:http-basic />
-->
</s:http>
<bean id="securityContextPersistenceFilter"
class="org.springframework.security.web.context.SecurityContextPersistenceFilter">
<property name='securityContextRepository'>
<bean class='org.springframework.security.web.context.HttpSessionSecurityContextRepository'>
<property name='allowSessionCreation' value='true' />
</bean>
</property>
</bean>
<bean id="preAuthenticatedFilter"
class="org.springframework.security.web.authentication.preauth.j2ee.J2eePreAuthenticatedProcessingFilter">
<property name="authenticationManager" ref="authenticationManager" />
</bean>
<s:authentication-manager alias="authenticationManager">
<s:authentication-provider ref="preAuthenticatedAuthenticationProvider" />
</s:authentication-manager>
<bean id="preAuthenticatedAuthenticationProvider"
class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider">
<property name="preAuthenticatedUserDetailsService" >
<bean class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper" >
<property name="userDetailsService" ref="userDetailsService" />
</bean>
</property>
</bean>
<bean id="userDetailsService" class="org.springframework.security.ldap.userdetails.LdapUserDetailsService" >
<constructor-arg index="0" ref="ldapUserSearch"/>
<constructor-arg index="1" ref="ldapAuthoritiesPopulator"/>
<property name="userDetailsMapper" >
<bean class="com.foo.MyUserDetailsMapper" />
</property>
</bean>
<bean id="ldapContextSource"
class="org.springframework.security.ldap.DefaultSpringSecurityContextSource">
<!-- some setting skipped here -->
</bean>
<bean id="ldapUserSearch" class="org.springframework.security.ldap.search.FilterBasedLdapUserSearch">
<!-- some setting skipped here -->
</bean>
<bean id="ldapAuthoritiesPopulator"
class="org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator">
<!-- some setting skipped here -->
</bean>
<bean id="ldapTemplate" class="org.springframework.ldap.core.simple.SimpleLdapTemplate">
<constructor-arg ref="ldapContextSource" />
</bean>
It mostly worked, I can see correct user name and role coming in for my custom UserDetailsMapper (com.foo.MyUserDetailsMapper) which comes from LDAP, and inside that I am returning a new UserDetails with updated roles.
The problem is, in my controller, when I tried to do
SecurityContextHolder.getContext().getAuthentication()
It is returning null. (For which works before I change to pre-authentication)
Is there anything I missed?

Found the problem. Sorry that is mostly because of my own implementation fault which is not visible in the question itself.
My custom UserDetails impl is incorrectly having getEnabled() returning false. In LdapAuthenticationProvider, it is working fine as there is no checking on the user status.
However, in PreAuthenticatedAuthenticationProvider, by default there is a UserDetailsChecker which checks the status of user, for which getEnabled() returning false will cause the user details checker to fail silently, and causing authentication not populated to SecurityContext (i.e. treating that account as not authenticated)
Although it is mostly my implementation issue, I think still worth leaving here as a reference for difference of LdapAuthenticationProvider and PreAuthenticatedAuthenticationProvider

Related

Spring OAuth ClassCast from principal to user obj

We've recently ugpraded Spring Security from 3.1.3 to 4.01, and we've had to make some changes to our OAuth configs that left some requests breaking with a class cast when they try to cast the principal to a user -- specifically, it's the string form of the client id.
Here are the relevant sections of our configs.
For context, our standard auth management (which returns a user for the principal):
<!-- define an authentication-manager who uses a custom userDetailService -->
<sec:authentication-manager alias="authenticationManager">
<!-- Utility username / password form based authentication-provider -->
<sec:authentication-provider user-service-ref="adminUserDetailsService">
<!-- etc. -->
</sec:authentication-provider>
</sec:authentication-manager>
Secondly, our filter that looks stuff up in a oauth client details table and adds auth:
<bean id="oauth2ProviderFilter" class="blah.blah.blah.BlahOAuth2AuthenticationProcessingFilter">
<property name="authenticationManager">
<bean class="org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationManager">
<property name="tokenServices" ref="oauth2TokenServices" />
</bean>
</property>
</bean>
<bean id="oauth2TokenServices" class="org.springframework.security.oauth2.provider.token.DefaultTokenServices">
<property name="tokenStore" ref="tokenStore" />
<property name="supportRefreshToken" value="true" />
</bean>
<bean id="tokenStore" class="org.springframework.security.oauth2.provider.token.JdbcTokenStore">
<constructor-arg ref="dataSource" />
</bean>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<!-- etc... -->
</bean>
Checking the token in the headers:
<bean id="oauth2ClientCredentialsTokenEndpointFilter"
class="org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter">
<constructor-arg index="0" value="/oauth/authorize" />
<property name="authenticationManager" ref="oauthAuthenticationManager" />
</bean>
<bean id="oauthAuthenticationManager" class="org.springframework.security.authentication.ProviderManager">
<constructor-arg>
<list>
<ref bean="oauthDaoAuthenticationProvider"/>
</list>
</constructor-arg>
</bean>
<bean id="oauthDaoAuthenticationProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<property name="userDetailsService" ref="oauthUserDetailService" />
<property name="passwordEncoder" ref="oauthPasswordEncoder" />
</bean>
Our best theory at the moment is that something within the endpoint filter has stopped doing a look up of the user object and instead just returns the user id -- is this known to be the case? Where have we gone wrong in our set up?

Spring session management SessionDestroyedEvent not thrown

I have a customized RegisterSessionAuthenticationStrategy and Im using CompositeSessionAuthenticationStrategy with exceptionIfMaximumExceeded = false.
I have the following scenario:
login on browser #1
login on browser #2
I want that the first session will be destroyed but they both stay active.
whan I reach the SessionRegistry.registerNewSession session #1 is set has expired but I never get a SessionDestroyedEvent for it.
security.xml
<security:session-management
session-authentication-strategy-ref="sas" />
</security:http>
<bean id="sas"
class="org.springframework.security.web.authentication.session.CompositeSessionAuthenticationStrategy">
<constructor-arg>
<list>
<bean
class="org.springframework.security.web.authentication.session.ConcurrentSessionControlAuthenticationStrategy">
<constructor-arg ref="clusteredSessionRegistryImpl" />
<property name="maximumSessions" value="1" />
<property name="exceptionIfMaximumExceeded" value="false" />
</bean>
<bean
class="org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy">
<constructor-arg ref="clusteredSessionRegistryImpl" />
</bean>
</list>
</constructor-arg>
</bean>

Support Basic Authentication + LDAP and Pre-Authenticated at the same time

I have a security app context which works fine with Pre-authentication.
I would want to know if it is possible to have both Basic Authentication (With LDAP Bind as authentication manager) and Pre-authentication effective at the same time:
If container provides principal name, we will rely on it (and go to LDAP to get user details), and if pre-authentication of container does not happen (e.g. we have deployed in Jetty for testing without pre-authetication), we would want Basic Authentication to be used which in turns authenticated by LDAP Bind.
Is it something possible? How can I do it?
Here is my existing (simplified) app context:
<s:global-method-security
secured-annotations="enabled"
pre-post-annotations="enabled"
proxy-target-class="true" />
<bean id="springSecurityFilterChain" class="org.springframework.security.web.FilterChainProxy">
<s:filter-chain-map path-type="ant">
<s:filter-chain pattern="/**"
filters="securityContextPersistenceFilter,preAuthenticatedFilter" />
</s:filter-chain-map>
</bean>
<bean id="securityContextPersistenceFilter"
class="org.springframework.security.web.context.SecurityContextPersistenceFilter">
<property name='securityContextRepository'>
<bean
class='org.springframework.security.web.context.HttpSessionSecurityContextRepository'>
<property name='allowSessionCreation' value='true' />
</bean>
</property>
</bean>
<bean id="preAuthenticatedFilter"
class="org.springframework.security.web.authentication.preauth.j2ee.J2eePreAuthenticatedProcessingFilter">
<property name="authenticationManager" ref="authenticationManager" />
</bean>
<s:authentication-manager alias="authenticationManager">
<s:authentication-provider ref="preAuthenticatedAuthProvider" />
</s:authentication-manager>
<bean id="preAuthenticatedAuthProvider" class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider">
<property name="preAuthenticatedUserDetailsService" >
<bean class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper" >
<property name="userDetailsService" ref="userDetailsService" />
</bean>
</property>
</bean>
<bean id="contextSource"
class="org.springframework.security.ldap.DefaultSpringSecurityContextSource">
<!-- some config skipped -->
</bean>
<bean id="userDetailsService" class="org.springframework.security.ldap.userdetails.LdapUserDetailsService" >
<constructor-arg index="0" ref="ldapUserSearch"/>
<constructor-arg index="1" ref="ldapAuthoritiesPopulator"/>
<property name="userDetailsMapper" ref="fooUserDetailsMapper" />
</bean>
<bean id="fooUserDetailsMapper" class="com.foo.FooUserDetailsMapper" />
<bean id="ldapUserSearch" class="org.springframework.security.ldap.search.FilterBasedLdapUserSearch">
<!-- some config skipped -->
</bean>
<bean id="ldapAuthoritiesPopulator"
class="org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator">
<!-- some config skipped -->
</bean>
<bean id="ldapTemplate" class="org.springframework.ldap.core.simple.SimpleLdapTemplate">
<constructor-arg ref="contextSource" />
</bean>
<bean id="ldapAuthorities" class="com.fil.ims.LdapAuthoritiesServices" />
I have tried the followings but none of them works
add a new ldap authentication provider (org.springframework.security.ldap.authentication.LdapAuthenticationProvider), and add one more <s:authentication-provider> entry under <s:authentication-manager>, or
add a separate org.springframework.security.web.authentication.www.BasicAuthenticationFilter, which points to a new <s:authentication-provider> which points to the new ldap authentication provider (It is complaining for "An AuthenticationEntryPoint is required".)
What should be the right way to do so?
From my bare understanding, seems 2 should be the right way, if someone can give me some direction, it will be good enough.
Thanks

Spring security duplicated RemeberMeProvider

I build an application with GWT on client side (in future I rewrite interface to JS) and duplicate functionality by REST requests. Based on Spring framework.
Now I need to implement authorization.
Here is my security-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
..........>
<security:http auto-config="false">
<security:form-login login-page="/login.html"/>
<security:intercept-url pattern="/login" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
<security:intercept-url pattern="/mainGWT.html**" access="ROLE_USER"/>
<security:remember-me services-ref="rememberMeServices" />
</security:http>
<bean id="userDetailsService" class="com.damintsev.servlet.UserDetailsServiceImpl"/>
<bean id="daoAuthenticationProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<property name="userDetailsService" ref="userDetailsService"/>
</bean>
<bean id="rememberMeAuthenticationProvider" class="org.springframework.security.authentication.RememberMeAuthenticationProvider">
<constructor-arg name="key" value="rock"/>
</bean>
<context:component-scan base-package="com.login"/>
<bean id="rememberMeServices" class=
"org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices">
<property name="userDetailsService" ref="userDetailsService"/>
<property name="key" value="rock"/>
<property name="tokenValiditySeconds" value="5000"/>
<property name="alwaysRemember" value="true"/>
</bean>
<bean id="rememberMeFilter" class=
"org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter">
<property name="rememberMeServices" ref="rememberMeServices"/>
<property name="authenticationManager" ref="authenticationManager"/>
</bean>
<bean id="providerManager" class="org.springframework.security.authentication.ProviderManager">
<property name="providers">
<list>
<ref bean="daoAuthenticationProvider"/>
<ref bean="org.springframework.security.authentication.RememberMeAuthenticationProvider#0"/>
</list>
</property>
</bean>
<security:authentication-manager alias="authenticationManager">
</security:authentication-manager>
But rememberMe services didnt work. So I start to debug this and I found that rememberMeAuthenticationProvider initializes twise!!
At first time initializes with name org.springframework.security.authentication.RememberMeAuthenticationProvider#0 and strange (autogenerated maybe) key.
The second provider initializes with propertly name rememberMeAuthenticationProvider with propertly key.
Later there is a problem because key in TokenBasedRememberMeServices didnt match with rememberMeAuthenticationProvider.
But when I change name of bean to org.springframework.security.authentication.RememberMeAuthenticationProvider#0 it works fine.
What am I doing wrong ? Who initialize provider twise ?
<spring.version>3.2.3.RELEASE</spring.version>
Second part of question: I dont understand how set RememberMe cookie to client so I write class. And if from client I receive remember = true I call that methood. And It works. If You provide some exmples with remeber me + REST it will helps.
#Component
public class Security {
#Autowired
private RememberMeServices rememberMeServices;
public void remeberMe(HttpServletRequest request, HttpServletResponse response, Authentication authentication1) {
rememberMeServices.setAlwaysRemember(true);
rememberMeServices.loginSuccess(request, response, authentication1);
}
}
You have a bean called rememberMeAuthenticationProvider and you are also using the namespace remember-me element which will also create one, hence the duplicate. You've also declared a filter which won't be used unless you actually insert it into the filter chain.
Either remove the namespace element and declare all the beans fully yourself, or stick with the namespace and let it do the work. If you want to customize the RememberMeServices you can you can retain that and the services-ref as you have it, but it's not clear from your question why you need to customize things.
If a remember-me cookie is sent by the client then the server will process it regardless of the type of client. How it is set in the first place depends on the login mechanism. Form login processing will automatically invoke the RememberMeServices and set the cookie if appropriate.

CAS Spring Security Java - Not Authorizing with User Details Service Not Loading Roles

I am using latest version of spring 3.2.5 and spring security 3.1.4 with java 6. I have setup CAS server using the instructions from this page
https://wiki.jasig.org/display/CASUM/Best+Practice+-+Setting+Up+CAS+Locally+using+the+Maven+WAR+Overlay+Method
The CAS server part is working fine and authenticating.
I have setup client side using the instructions from this page and various other pages
https://wiki.jasig.org/display/CASC/Configuring+the+JA-SIG+CAS+Client+for+Java+using+Spring
When tried to enter secure page in the application, CAS is redirecting to the correct login page and then correctly authenticating and then correctly redirecting to the calling application page, but not invoking the user details service supplied and not authorizing the user and not loading roles using the user details service.
After authentication user lands on this page. The page was correct but I don't want to see the ticket parameter in the URL and also load the user and roles using user details service bean supplied.
http://localhost:8080/my/sports-life/schedule?ticket=ST-3-xklhdGJW6gZxieELGxo5-cas01.example.org
Any pointers to get my authorization going is highly appreciated. Thanks in advance.
Here are the relevant beans from application context
<!-- Single sign on with CAS -->
<bean id="casEntryPoint" class="org.springframework.security.cas.web.CasAuthenticationEntryPoint">
<property name="loginUrl" value="https://localhost:8443/cas/login"/>
<property name="serviceProperties" ref="serviceProperties"/>
</bean>
<bean id="serviceProperties" class="org.springframework.security.cas.ServiceProperties">
<property name="service" value="http://localhost:8080/my/sports-life/schedule/j_spring_cas_security_check"/>
<property name="sendRenew" value="false"/>
</bean>
<bean id="casFilter" class="org.springframework.security.cas.web.CasAuthenticationFilter">
<property name="authenticationManager" ref="preAuthenticationManager"/>
<property name="authenticationSuccessHandler">
<bean class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
<property name="defaultTargetUrl" value="/my"/>
<property name="targetUrlParameter" value="spring-security-redirect" />
</bean>
</property>
</bean>
<bean id="casAuthenticationProvider" class="org.springframework.security.cas.authentication.CasAuthenticationProvider">
<property name="userDetailsService" ref="myAccountDetailsService" />
<property name="serviceProperties" ref="serviceProperties" />
<property name="ticketValidator">
<bean class="org.jasig.cas.client.validation.Cas20ServiceTicketValidator">
<constructor-arg index="0" value="https://localhost:8443/cas" />
</bean>
</property>
<property name="key" value="Vi9Pra88Si777"/>
<property name="authenticationUserDetailsService" ref="authenticationUserDetailsService"/>
</bean>
<bean id="authenticationUserDetailsService" class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
<property name="userDetailsService" ref="myAccountDetailsService"/>
</bean>
<bean name="authenticationFilter" class="org.jasig.cas.client.authentication.AuthenticationFilter">
<property name="casServerLoginUrl" value="https://localhost:8443/cas/login" />
<property name="renew" value="false" />
<property name="gateway" value="false" />
<property name="service" value="http://localhost:8080/my/sports-life/schedule" />
</bean>
<!--
<bean
name="ticketValidationFilter"
class="org.jasig.cas.client.validation.Cas10TicketValidationFilter">
<property name="service" value="http://localhost:8080/my/sports-life/schedule" />
<property name="ticketValidator">
<bean class="org.jasig.cas.client.validation.Cas20ServiceTicketValidator">
<constructor-arg index="0" value="https://localhost:8443/cas" />
</bean>
</property>
</bean>
-->
<bean id="preauthAuthProvider"
class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider">
<property name="preAuthenticatedUserDetailsService">
<bean id="userDetailsServiceWrapper"
class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
<property name="userDetailsService" ref="myAccountDetailsService"/>
</bean>
</property>
</bean>
<!--
<bean id="preAuthEntryPoint" class="org.springframework.security.web.authentication.Http403ForbiddenEntryPoint" />
<bean id="j2eePreAuthFilter" class="org.springframework.security.web.authentication.preauth.j2ee.J2eePreAuthenticatedProcessingFilter">
<property name="authenticationManager" ref="preAuthenticationManager"/>
<property name="authenticationDetailsSource">
<bean class="org.springframework.security.web.authentication.WebAuthenticationDetailsSource" />
</property>
</bean>
-->
<bean id="myAccountDetailsService" class="com.viprasi.security.AccountDetailsServiceImpl">
</bean>
Then here are relevant config from my spring security configuration file.
<http use-expressions="true" entry-point-ref="casEntryPoint">
<intercept-url pattern="/app/j_spring_cas*" access="permitAll"
requires-channel="https" />
<!-- Member -->
<intercept-url pattern="/app/**" access="isAuthenticated()" />
<access-denied-handler error-page="/app/login/accessdenied" />
<anonymous />
<http-basic />
<custom-filter position="CAS_FILTER" ref="casFilter" />
</http>
<authentication-manager alias="preAuthenticationManager">
<authentication-provider ref="casAuthenticationProvider" />
<!--
<authentication-provider user-service-ref='accountDetailsService' />
-->
</authentication-manager>
i think the anonymous tag applies the role *IS_AUTHENTICATED_ANONYMOUSLY* to a user.
the result of this is that the user isAuthenticated() and no further filter is invoked (e.g. the one who calls the UserDetailsServive)
change isAuthenticated() to hasRole('ROLE')
I have an example cas client You can check in cas-web-client how provide your own authorization
We ran into lot of issues and at the end the main culprit was url rewriting filter. This filter was changing url and then spring security and cas failed to handled the changed URL. After moving the url rewrite filter below the security filters, it's all started working.

Categories

Resources