Spring MVC + Spring Security login with a rest web service

11,314
  1. you can define a custom pre-auth filter by extending AbstractPreAuthenticatedProcessingFilter.
  2. 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.
  3. 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
  4. 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

Share:
11,314
hectorg87
Author by

hectorg87

Updated on June 11, 2022

Comments

  • hectorg87
    hectorg87 almost 2 years

    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:

    1. Use a custom authenticationFilter with a authenticationSuccessHandler. Obviously doesn't work because the user is already logged in this point.
    2. Make an implementation of entry-point-ref filter.
    3. Do a custom-filter in the position BASIC_AUTH_FILTER
    4. Make a custom Authentication Provider (Struggled a lot with no luck!). I'm retrying this while I get some answers.
    5. 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 { ... }
    
  • hectorg87
    hectorg87 almost 11 years
    Hello @coder, all of it makes sense. I'll try it and post the results. Thanks!
  • Jorge Infante Osorio
    Jorge Infante Osorio about 10 years
    @hector87 can you share your implementation and configuration? thanks.
  • Jonathan Lebrun
    Jonathan Lebrun almost 10 years
    Yes, I'm also interested by the implementation. Could you post it somewhere ? Thanks a lot
  • alphabytes
    alphabytes over 9 years
    @hectorg87 can you share the blog links if you have published them. i am trying to authenticate FB SignIn in a REST based Spring Application. currently i have configured Security with custom AuthenticationEntryPoint and custom SimpleUrlAuthenticationSuccessHandler and Authentication manager with custom userLoginService - which gets the user credentials from the db. i am not sure how to add/manage PreAuth filters with my existing configuration since PreAuthenticatedAuthenticationProvider requires preAuthenticatedUserDetailsService i am not sure if my userDetailService needs to be replaced.