spring security : how to apply #PreAuthorize to SwitchUserFilter - spring

I have to check whether the user has the permission to switch user, on the basis of his userId. In my security xml, inside the <http> element, I am specifying a custom filter :
<custom-filter after="FILTER_SECURITY_INTERCEPTOR" ref="switchUserProcessingFilter"/>
<beans:bean id="switchUserProcessingFilter" class="com.something.MySwitchUserFilter">
<beans:property name="userDetailsService" ref="userServices" />
<beans:property name="switchUserUrl" value="/admin/switchUser" />
<beans:property name="exitUserUrl" value="/exitUser" />
<beans:property name="successHandler" ref="userSwitchSuccessHandler"></beans:property>
</beans:bean>
In MySwitchUserFilter, I override only a single method :
#Override
#PreAuthorize("canISwitchToThisUser(#{request.getAttribute('userId')}")
protected Authentication attemptSwitchUser(HttpServletRequest request) throws AuthenticationException {
return super.attemptSwitchUser(request);
}
#PreAuthorize is working at other locations, but i wonder why its not working here. Note that I am also passing an request parameter 'userId' along with the customary 'j_username'. Note that I am able to switch user, just that the annotation is not being processed.

Related

How to Over ride BindAuthenticator handleBindException for Spring LDAP Authentication setup in Spring Boot

For Spring security setup in Spring Boot. The LDAP Authentication provider is configured by default to use BindAuthenticator class.
This Class contains method
/**
* Allows subclasses to inspect the exception thrown by an attempt to bind with a
* particular DN. The default implementation just reports the failure to the debug
* logger.
*/
protected void handleBindException(String userDn, String username, Throwable cause) {
if (logger.isDebugEnabled()) {
logger.debug("Failed to bind as " + userDn + ": " + cause);
}
}
This Method is to handle the authentication related Exceptions like invalid credentials.
I want to over-ride this method so i can handle this issue and return proper error message on the basis of error codes returned by LDAP. like invalid password or the account is locked.
Current LDAP implementation always returns "Bad Credentials" that does not give the right picture that why my credentials are invalid. i want to cover the cases
where the account is Locked
password is expired so i can redirect to change password
account locked due to number of invalid password retries
Please help
The issue i fixed by defining the LDAP context instead of using the Spring Boot LDAPAuthenticationProviderConfigurer.
Then created the FilterBasedLdapUserSearch and Over-written the BindAuthentication with my ConnectBindAuthenticator.
i created a separate LDAPConfiguration class for spring boot configuration and registered all these custom objects as Beans.
From the above Objects i created LDAPAuthenticationProvider by passing my Custom Objects to constructor
The Config is as below
#Bean
public DefaultSpringSecurityContextSource contextSource() {
DefaultSpringSecurityContextSource contextSource = new DefaultSpringSecurityContextSource(env.getProperty("ldap.url"));
contextSource.setBase(env.getProperty("ldap.base"));
contextSource.setUserDn(env.getProperty("ldap.managerDn"));
contextSource.setPassword(env.getProperty("ldap.managerPassword"));
return contextSource;
}
#Bean
public ConnectBindAuthenticator bindAuthenticator() {
ConnectBindAuthenticator connectBindAuthenticator = new ConnectBindAuthenticator(contextSource());
connectBindAuthenticator.setUserSearch(ldapUserSearch());
connectBindAuthenticator.setUserDnPatterns(new String[]{env.getProperty("ldap.managerDn")});
return connectBindAuthenticator;
}
#Bean
public LdapUserSearch ldapUserSearch() {
return new FilterBasedLdapUserSearch("", env.getProperty("ldap.userSearchFilter"), contextSource());
}
You have to change your spring security configuration to add your extension of BindAuthenticator:
CustomBindAuthenticator.java
public class CustomBindAuthenticator extends BindAuthenticator {
public CustomBindAuthenticator(BaseLdapPathContextSource contextSource) {
super(contextSource);
}
#Override
protected void handleBindException(String userDn, String username, Throwable cause) {
// TODO: Include here the logic of your custom BindAuthenticator
if (somethingHappens()) {
throw new MyCustomException("Custom error message");
}
super.handleBindException(userDn, username, cause);
}
}
spring-security.xml
<beans:bean id="contextSource"
class="org.springframework.security.ldap.DefaultSpringSecurityContextSource">
<beans:constructor-arg value="LDAP_URL" />
<beans:property name="userDn" value="USER_DN" />
<beans:property name="password" value="PASSWORD" />
</beans:bean>
<beans:bean id="userSearch"
class="org.springframework.security.ldap.search.FilterBasedLdapUserSearch">
<beans:constructor-arg index="0" value="USER_SEARCH_BASE" />
<beans:constructor-arg index="1" value="USER_SEARCH_FILTER" />
<beans:constructor-arg index="2" ref="contextSource" />
</beans:bean>
<beans:bean id="ldapAuthProvider"
class="org.springframework.security.ldap.authentication.LdapAuthenticationProvider">
<beans:constructor-arg>
<beans:bean class="com.your.project.CustomBindAuthenticator">
<beans:constructor-arg ref="contextSource" />
<beans:property name="userSearch" ref="userSearch" />
</beans:bean>
</beans:constructor-arg>
</beans:bean>
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider ref="ldapAuthProvider" />
</security:authentication-manager>
Hope it's helpful.

How to validate user using info in headers in spring security

Currently I'm creating the web without login page.
I have another website that will send a header with info:
user:John
userCode:1234567
So my current website will check the content of the headers and validate the user in authentication manager like this:
First I create the AuthenticationEntryPoint so the unauthentication user will go there.In the AuthenticationEntryPoint I create a token and redirect the user to main page,so before its goes to the main page,spring will authenticate the user and give a token for a valid user to use the page. The code is like this:
#Component
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
if(authException.getClass().getSimpleName().equals("InsufficientAuthenticationException")) {
if (request.getHeader("user") != null) {
UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(request.getHeader("user"), request.getHeader("userCode"));
SecurityContextHolder.getContext().setAuthentication(auth);
response.sendRedirect(request.getContextPath());
}
}
}
In the AuthenticationManager the process will go as usual and give token if the user is valid. Is there anything I need to change or another approach that can be used in spring?
Thanks!
Your case make me think of the Siteminder implementation example, in the reference documentation.
With Siteminder, a header (SM_USER) is passed with the HTTP request.
This is an example for pre-authentication in Spring Security.
Did you try this configuration ?
They begin by defining a "custom-filter" which is an instance of RequestHeaderAuthenticationFilter.
Extract of the documentation :
<security:http>
<!-- Additional http configuration omitted -->
<security:custom-filter position="PRE_AUTH_FILTER" ref="siteminderFilter" />
</security:http>
<bean id="siteminderFilter" class="org.springframework.security.web.authentication.preauth.RequestHeaderAuthenticationFilter">
<property name="principalRequestHeader" value="SM_USER"/>
<property name="authenticationManager" ref="authenticationManager" />
</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="userDetailsService"/>
</bean>
</property>
</bean>
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider ref="preauthAuthProvider" />
</security:authentication-manager>

Spring MVC + Spring Security login with a rest web service

I have a SpringMVC web application that needs to authenticate to a RESTful web service using Spring Security by sending the username and password. When an user is logged, a cookie needs to be set to the user's browser and in the subsequent calls the user session is validated with another RESTful web service by using the cookie.
I've been looking everywhere, but I have not been able to find a good example on how to accomplish this, and all my attempts have been in vain.
Here is what I have in mind:
I can have two authentication-providers declared, the first checks the cookie, and if it fails for any reason it goes to the second one which checks with the username and password (will fail too if there is no username and password in that request).
Both services return the authorities of the user each time, and spring security is "stateless".
On the other hand, I have questioned myself if this approach is correct, since it's been so difficult to find an example or somebody else with the same problem. Is this approach wrong?
The reason why I want to do this instead of just JDBC authentication is because my whole web application is stateless and the database is always accessed through RESTful web services that wrap a "petitions queue", I'd like to respect this for user authentication and validation too.
What have I tried so far? I could paste the long long springSecurity-context.xml, but I'll just list them instead for now:
Use a custom authenticationFilter with a authenticationSuccessHandler. Obviously doesn't work because the user is already logged in this point.
Make an implementation of entry-point-ref filter.
Do a custom-filter in the position BASIC_AUTH_FILTER
Make a custom Authentication Provider (Struggled a lot with no luck!). I'm retrying this while I get some answers.
I was starting to use CAS when I decided to write a question instead. Maybe in the future I can consider having a CAS server in my webapp, however for the moment, this feels like a huge overkill.
Thanks in advance!
BTW, I'm using Spring Security 3.1.4 and Spring MVC 3.2.3
EDIT: I WAS ABLE TO DO IT THANKS TO #coder ANSWER
Here is some light on what I did, I'll try to document all this and post it here or in a blog post sometime soon:
<http use-expressions="true" create-session="stateless" entry-point-ref="loginUrlAuthenticationEntryPoint"
authentication-manager-ref="customAuthenticationManager">
<custom-filter ref="restAuthenticationFilter" position="FORM_LOGIN_FILTER" />
<custom-filter ref="restPreAuthFilter" position="PRE_AUTH_FILTER" />
<intercept-url pattern="/signin/**" access="permitAll" />
<intercept-url pattern="/img/**" access="permitAll" />
<intercept-url pattern="/css/**" access="permitAll" />
<intercept-url pattern="/js/**" access="permitAll" />
<intercept-url pattern="/**" access="hasRole('ROLE_USER')" />
</http>
<authentication-manager id="authManager" alias="authManager">
<authentication-provider ref="preauthAuthProvider" />
</authentication-manager>
<beans:bean id="restPreAuthFilter" class="com.company.CustomPreAuthenticatedFilter">
<beans:property name="cookieName" value="SessionCookie" />
<beans:property name="checkForPrincipalChanges" value="true" />
<beans:property name="authenticationManager" ref="authManager" />
</beans:bean>
<beans:bean id="preauthAuthProvider"
class="com.company.CustomPreAuthProvider">
<beans:property name="preAuthenticatedUserDetailsService">
<beans:bean id="userDetailsServiceWrapper"
class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
<beans:property name="userDetailsService" ref="userDetailsService" />
</beans:bean>
</beans:property>
</beans:bean>
<beans:bean id="userDetailsService" class="com.company.CustomUserDetailsService" />
<beans:bean id="loginUrlAuthenticationEntryPoint"
class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
<beans:constructor-arg value="/signin" />
</beans:bean>
<beans:bean id="customAuthenticationManager"
class="com.company.CustomAuthenticationManager" />
<beans:bean id="restAuthenticationFilter"
class="com.company.CustomFormLoginFilter">
<beans:property name="filterProcessesUrl" value="/signin/authenticate" />
<beans:property name="authenticationManager" ref="customAuthenticationManager" />
<beans:property name="authenticationFailureHandler">
<beans:bean
class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
<beans:property name="defaultFailureUrl" value="/login?login_error=t" />
</beans:bean>
</beans:property>
</beans:bean>
And the Custom Implementations are something like this:
// Here, the idea is to write authenticate method and return a new UsernamePasswordAuthenticationToken
public class CustomAuthenticationManager implements AuthenticationManager { ... }
// Write attemptAuthentication method and return UsernamePasswordAuthenticationToken
public class CustomFormLoginFilter extends UsernamePasswordAuthenticationFilter { ... }
// Write getPreAuthenticatedPrincipal and getPreAuthenticatedCredentials methods and return cookieName and cookieValue respectively
public class CustomPreAuthenticatedFilter extends AbstractPreAuthenticatedProcessingFilter { ... }
// Write authenticate method and return Authentication auth = new UsernamePasswordAuthenticationToken(name, token, grantedAuths); (or null if can't be pre-authenticated)
public class CustomPreAuthProvider extends PreAuthenticatedAuthenticationProvider{ ... }
// Write loadUserByUsername method and return a new UserDetails user = new User("hectorg87", "123456", Collections.singletonList(new GrantedAuthorityImpl("ROLE_USER")));
public class CustomUserDetailsService implements UserDetailsService { ... }
you can define a custom pre-auth filter by extending
AbstractPreAuthenticatedProcessingFilter.
In your implementation of
getPreAuthenticatedPrincipal() method you can check if cookie exists
and if it exists return cookie name is principal and cookie value in
credentials.
Use PreAuthenticatedAuthenticationProvider and provide your custom preAuthenticatedUserDetailsService to check if cookie is vali, if its valid also fetch granted authorities else throw AuthenticationException like BadCredentialsException
For authenticating user using username/password, add a form-login filter, basic-filter or a custom filter with custom authentication provider (or custom userdetailsService) to validate user/password
In case cookie exists, pre auth filter will set authenticated user in springContext and your username./password filter will not be called, if cookie is misisng/invalid, authentication entry point will trigger the authentication using username/password
Hope it helps

Spring Security - check remember me when login failed

How I get remember me value when login failed and reopen the login page?
Can i get the value of _spring_security_remember_me on controller?
I just need to keep the value of the checkbox when login error occurs!
You can try the following solution:
1. insert custom filter into spring security filter chain
2. inside this filter obtain http session and store there the value of request parameter
As we change the login form (adding another parameter) we need to customize spring representation of login form and spring login processing filter.
Here is the configuration:
<authentication-manager alias="authenticationManager"/>
<beans:bean id="myFilter" class="test.MyAuthenticationProcessingFilter">
<custom-filter position="AUTHENTICATION_PROCESSING_FILTER" />
<beans:property name="defaultTargetUrl" value="/initialize.action"/>
<beans:property name="authenticationFailureUrl" value="/login_failed.action"/>
<beans:property name="authenticationManager" ref="authenticationManager"/>
<beans:property name="alwaysUseDefaultTargetUrl" value="true"/>
<beans:property name="filterProcessesUrl" value="/perform_login"/>
</beans:bean>
<beans:bean id="entryPoint" class="org.springframework.security.ui.webapp.AuthenticationProcessingFilterEntryPoint">
<beans:property name="loginFormUrl" value="/login.action"/>
</beans:bean>
MyAuthenticationProcessingFilter extends spring's org.springframework.security.ui.webapp.AuthenticationProcessingFilter, wraps attemptAuthentication method obtaining request parameter and storing it inside http session. This class is written just to show the idea, for better practice browse AuthenticationProcessingFilter code for username and password parameters.
public class MyAuthenticationProcessingFilter extends AuthenticationProcessingFilter {
#Override
public Authentication attemptAuthentication(HttpServletRequest request)
throws AuthenticationException {
String param = request.getParameter("_spring_security_remember_me");
HttpSession session = request.getSession();
if (session != null || getAllowSessionCreation()) {
session.setAttribute("_spring_security_remember_me", param);
}
return super.attemptAuthentication(request);
}
}
You may notice that "myFilter" and "entryPoint" beans together define parameters that are otherwise defined by element inside . You use when you want the default behavior. But in our case we use custom beans, so you should remove element completely.
Now we need to tell use our beans. "myFilter" bean is passed to spring chain by using element inside bean definition:
<beans:bean id="myFilter" class="test.MyAuthenticationProcessingFilter">
<custom-filter position="AUTHENTICATION_PROCESSING_FILTER" />
...
</beans:bean>
"entryPoint" is passed to using attribute:
<http entry-point-ref="entryPoint">
...
<!-- no form-login here -->
</http>
your question is a bit unclear, or you have a wrong image of how remember me with spring security works. Read the Spring Security Reference Chapter 11 "Remember-Me Authentication"
Briefly it works this way:
If a user log in successfully with his user name and password and have enabled the remember me checkbox, Spring Security will create a cookie that verify the user and "send" it to the user
Not logged in User request a secured page (Authentication required) spring will check if he as a valid cookie.
If he has such a cookie spring security will "login" him "automatically" and show him the page
If he has no valid cookie spring will forward him to the login page (see above)
I hope this helps you.

Spring Security sesson timeout not recognized on ajax call

I want to redirect users to the login page when a session timeout occurs. This works out-of-the-box with spring security, but only on non-ajax calls.
On an ajax-call you have to react on the session timeout by yourself. Therefore I have created
my own filter(filter implemented like in this question) who checks if a session is timed out. The filter is registered via custom-filter tag in spring security config.
<http use-expressions="true">
<custom-filter ref="customTimeoutHandler" after="LAST"/>
</http>
The problem is, that the session timeout is not recognized by the filter. If I check for request.isRequestedSessionIdValid() it returns true even if the session is timed out. When I enter a new secured URL manually, the standard spring security filter recognizes the timeout correctly and does a redirect to the login page.
What could be wrong here? How recognizes spring security the session timeout?
UPDATE
It seems, that the session management filter of spring security replaces the timed-out session with a new anonymous one. Therefore everytime I check for session timeout it returns true, because the new anonymous session is, of course, not timed-out.
You can check the SecurityContext.
Grab the Authentication object and check the authorities looking for an ANONYMOUS one. Something like:
SecurityContext sc = SecurityContextHolder.getContext();
Authentication a = sc.getAuthentication();
if(!a.isAuthenticated() || a.getAuthorities().contains(new GrantedAuthorityImpl("ROLE_ANONYMOUS"))) {
//user not authenticated or ANONYMOUS
} else {
//user authenticated
}
This solution works like a charm for me.
The basic concept is to point to a servlet instead of the login page. The servlet then determines if the request was a ajax request and if that is true, it returns the redirect to the login page as xml fragment. The browser can interpret that fragment and redirects to the login page.
I am developing enterprise application including gwt/gwtp and spring security .
I add some issue with session time out , casue the SimpleRedirectInvalidSessionStrategy which used by default is executing response.sendRedirect() , the html page response I wanted to redirect is swallow by gwt com.google.gwt.user.client.rpc.InvocationException as the exception message . and no actully redirect is taking place .
for solving this
1 . I define my cosutom session-manamgemt-filter
for doing this you need in your spring-security.xml configuration file set
<session-management session-fixation-protection="none"/> by this spring secuirty will not take it default session managment filter .
define your session managment filter
enter code here
{
<custom-filter position="SESSION_MANAGEMENT_FILTER" ref="mySessionManagmentFilter"/>
<beans:bean id="mySessionManagmentFilter"
class="org.springframework.security.web.session.SessionManagementFilter">
<beans:constructor-arg index="0" ref="mySessionSecurityContextRepository"/>
<beans:constructor-arg index="1" ref="mySessionAutenticationStrategy"/>
<beans:property name="invalidSessionStrategy">
<beans:ref local="myInvalidSessionStrategy"/>
</beans:property>
</beans:bean>
<beans:bean id="mySessionSecurityContextRepository"
class='org.springframework.security.web.context.HttpSessionSecurityContextRepository'>
<beans:property name='allowSessionCreation' value='false'/>
</beans:bean>
<beans:bean id="mySessionAutenticationStrategy"
class="org.springframework.security.web.authentication.session.ConcurrentSessionControlStrategy">
<beans:constructor-arg name="sessionRegistry" ref="sessionRegistry"/>
<beans:property name="maximumSessions" value="1"/>
<beans:property name="exceptionIfMaximumExceeded" value="false"/>
<beans:property name="alwaysCreateSession" value="true"/>
</beans:bean>
<beans:bean id="myInvalidSessionStrategy"
class="com.my.project.MyInvalidSessionStrategy">
<beans:constructor-arg value="/login.jsp?timeout=1"/>
</beans:bean>
}
here custom - MyInvalidSessionStrategy
{
public class MyInvalidSessionStrategy implements InvalidSessionStrategy {
private final Logger logger = LoggerFactory.getLogger(getClass());
private final String destinationUrl;
public OperationalInvalidSessionStrategy(String invalidSessionUrl) {
this.destinationUrl = invalidSessionUrl;
}
#Override
public void onInvalidSessionDetected(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
String exMsg =session timeout ! , need to redirect to login page
logger.warn(exMsg);
throw new TimeOutException(exMsg);
}
}
}
so when time out taking place the new implementation is throwing an exception ..
the exception can be truck on gwt callback onFailure method
check the type of the exception and on onFailure method redirect the user to login page .
with Window.Location.replace(GWT.getHostPageBaseURL() + "/login.jsp")

Resources