I'm having a problem pre-authenticating users in a spring based app.
Here is my scenario. I have two CustomUserDetailsServices, one for Employees and one for Customers. In front of my server it is implemented a proxy, that adds to the Http Header of the client request two header informations. Let's call theam Header_A
and Header_B
.
I'd like that spring security to do the following steps:
Header_A
and use it in the EmployeeUserDetailsService
, to call the loadUserByUsername(String name)
method. If the user is found, authenticate it and give him access to the app.Header_B
and use it in the CustomerUserDetailsService
.I've tried the following, and it works, but with a work-around(I don't want to do that work-around):
<sec:http use-expressions="true" access-denied-page="/denied.jsp" entry-point-ref="http403EntryPoint">
<sec:intercept-url pattern="/**" access="hasRole('ROLE_USER')" />
<sec:custom-filter after="PRE_AUTH_FILTER" ref="customerFilter" />
<sec:custom-filter position="PRE_AUTH_FILTER" ref="employeeFilter" />
<sec:logout delete-cookies="true" invalidate-session="true" logout-success-url="/" />
</sec:http>
<bean id="employeeFilter" class="org.springframework.security.web.authentication.preauth.RequestHeaderAuthenticationFilter">
<property name="principalRequestHeader" value="Header_A"/>
<property name="authenticationManager" ref="authenticationManager" />
<property name="exceptionIfHeaderMissing" value="false"/>
</bean>
<bean id="customerFilter" class="org.springframework.security.web.authentication.preauth.RequestHeaderAuthenticationFilter">
<property name="principalRequestHeader" value="Header_B"/>
<property name="authenticationManager" ref="authenticationManager" />
<property name="exceptionIfHeaderMissing" value="false"/>
</bean>
<bean id="employeePreAuthProvider" class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider">
<property name="throwExceptionWhenTokenRejected" value="false" />
<property name="preAuthenticatedUserDetailsService">
<bean id="userDetailsServiceWrapper" class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
<property name="userDetailsService" ref="employeeUserDetailsService"/>
</bean>
</property>
</bean>
<bean id="customerPreAuthProvider" class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider">
<property name="throwExceptionWhenTokenRejected" value="false" />
<property name="preAuthenticatedUserDetailsService">
<bean id="userDetailsServiceWrapper" class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
<property name="userDetailsService" ref="customerUserDetailsService"/>
</bean>
</property>
</bean>
<sec:authentication-manager alias="authenticationManager">
<sec:authentication-provider ref="employeePreAuthProvider" />
<sec:authentication-provider ref="customerPreAuthProvider" />
</sec:authentication-manager>
<bean id="customerUserDetailsService" class="xxx.CustomerUserDetailsService"/>
<bean id="employeeUserDetailsService" class="xxx.EmployeeUserDetailsService"/>
This thing does the following:
The workaround that I do is to verify the length of the headers(that is fix), and to return; if one header get's in the wrong UserDetailsService
If you want to only use the EmployeeUserDetailsService for Header_A and the CustomerUserDetailsService for Header_B, then you can create multiple AuthenticationManager instances and wire them in the corresponding filters. For example:
<sec:http use-expressions="true" access-denied-page="/denied.jsp" entry-point-ref="http403EntryPoint" authentication-manager-ref="authenticationManager">
<sec:intercept-url pattern="/**" access="hasRole('ROLE_USER')" />
<sec:custom-filter after="PRE_AUTH_FILTER" ref="customerFilter" />
<sec:custom-filter position="PRE_AUTH_FILTER" ref="employeeFilter" />
<sec:logout delete-cookies="true" invalidate-session="true" logout-success-url="/" />
</sec:http>
<bean id="employeeFilter" class="org.springframework.security.web.authentication.preauth.RequestHeaderAuthenticationFilter">
<property name="principalRequestHeader" value="Header_A"/>
<property name="authenticationManager" ref="employeeAuthenticationManager" />
<property name="exceptionIfHeaderMissing" value="false"/>
</bean>
<bean id="customerFilter" class="org.springframework.security.web.authentication.preauth.RequestHeaderAuthenticationFilter">
<property name="principalRequestHeader" value="Header_B"/>
<property name="authenticationManager" ref="customerAuthenticationManager" />
<property name="exceptionIfHeaderMissing" value="false"/>
</bean>
<bean id="employeePreAuthProvider" class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider">
<property name="throwExceptionWhenTokenRejected" value="false" />
<property name="preAuthenticatedUserDetailsService">
<bean id="userDetailsServiceWrapper" class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
<property name="userDetailsService" ref="employeeUserDetailsService"/>
</bean>
</property>
</bean>
<bean id="customerPreAuthProvider" class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider">
<property name="throwExceptionWhenTokenRejected" value="false" />
<property name="preAuthenticatedUserDetailsService">
<bean id="userDetailsServiceWrapper" class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
<property name="userDetailsService" ref="customerUserDetailsService"/>
</bean>
</property>
</bean>
<sec:authentication-manager id="authenticationManager">
<sec:authentication-provider ref="customerPreAuthProvider" />
<sec:authentication-provider ref="employeePreAuthProvider" />
</sec:authentication-manager>
<sec:authentication-manager id="customerAuthenticationManager">
<sec:authentication-provider ref="customerPreAuthProvider" />
</sec:authentication-manager>
<sec:authentication-manager id="employeeAuthenticationManager">
<sec:authentication-provider ref="employeePreAuthProvider" />
</sec:authentication-manager>
<bean id="customerUserDetailsService" class="xxx.CustomerUserDetailsService"/>
<bean id="employeeUserDetailsService" class="xxx.EmployeeUserDetailsService"/>
A few notes: