AndreaNobili
AndreaNobili

Reputation: 43057

How exactly works this Spring Security example?

I am pretty new in Spring Security and I have some doubt related the configuration found into a tutorial.

This is the spring-security.xml file used to the Spring Security configuration into the project:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:security="http://www.springframework.org/schema/security"
    xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-4.0.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <security:http>
        <security:intercept-url pattern="/springLogin" access="permitAll"/>
        <security:intercept-url pattern="/doSpringLogin" access="permitAll"/>
        <security:intercept-url pattern="/myprofile" access="hasRole('ROLE_USER')"/>
        <security:intercept-url pattern="/springHome" access="hasRole('ROLE_USER')"/> 
        <security:intercept-url pattern="/products" access="hasRole('ROLE_USER')"/> 
        <security:intercept-url pattern="/springLogout" access="permitAll"/>
        <security:intercept-url pattern="/springLogin?error=true" access="permitAll"/>
        <security:form-login login-page="/springLogin" login-processing-url="/doSpringLogin"
        default-target-url="/springHome" authentication-failure-url="/springLogin?error=true"
        username-parameter="username" password-parameter="password"
        />
        <security:csrf disabled="true"/>
        <security:logout logout-url="/springLogout" logout-success-url="/springLogin"/>
    </security:http>

    <bean id="userDetailsServiceImpl" class="com.demo.security.UserDetailsServiceImpl"></bean>

    <bean id="authenticationProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
        <property name="userDetailsService" ref="userDetailsServiceImpl"></property>
    </bean>

    <bean id="authenticationManager" class="org.springframework.security.authentication.ProviderManager">
        <constructor-arg name="providers">
            <list>
                <ref bean="authenticationProvider"/>
            </list>
        </constructor-arg>
    </bean>

    <security:authentication-manager>
        <security:authentication-provider user-service-ref="userDetailsServiceImpl">
            <security:password-encoder hash="plaintext"></security:password-encoder>
        </security:authentication-provider>
    </security:authentication-manager>

</beans>

I it divided into some section. The first one is the tag content.

It contains something as:

<security:intercept-url pattern="/springLogin" access="permitAll"/>

that I think means that the page related to the /springLogin resource is accessible to everyone while

<security:intercept-url pattern="/myprofile" access="hasRole('ROLE_USER')"/>

means that the resource related to the /myprofile resource is accessible only to the logged user (the principal) having a ROLE_USER role setted.

Is it this reasoning correct?

Then in the previous configuration file there is:

1) The declaration of the authenticationManager bean:

<bean id="authenticationManager" class="org.springframework.security.authentication.ProviderManager">
    <constructor-arg name="providers">
        <list>
            <ref bean="authenticationProvider"/>
        </list>
    </constructor-arg>
</bean>

that I think it is used by Spring to populate the SecurityContext with the Principal objects (for example all the user of a web application) and with the Authorities (what a specific Principal can do).

Is this reasoning correct?

This object take as constructor arg a list of autentication provider bean that have to provide the Principal informations (so for example the role associated to a specific Principal)

In this case is provided an implementation of the DaoAuthenticationProvider class that take a bean having name="userDetailsService" as property, this one:

<bean id="userDetailsServiceImpl" class="com.demo.security.UserDetailsServiceImpl"></bean>

that is an instance of the UserDetailsServiceImpl class, this one:

public class UserDetailsServiceImpl implements UserDetailsService {

    @Override
    public UserDetails loadUserByUsername(String username)
            throws UsernameNotFoundException {
        System.out.println(username);
        User user = RegisteryDAO.getUserDAO().getUserByUsername(username);

        if(user == null){
            return null;
        }

        List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
        authorities.add(new SimpleGrantedAuthority(user.getRole()));

        UserDetails userDetails = new org.springframework.security.core.userdetails.
                User(user.getUsername(), user.getPassword(), true, true, true, true, authorities);


        return userDetails;
    }

}

So what exactly happen?

Using the debugger it seems to me that when te user try to access to a specific page this loadUserByUsername() return the UserDetails object related to the logged user that contain the List representing the roles associated to the specific logged user (for example the previous ROLE_USER)

Then I think that Spring automatically use the

<security:intercept-url pattern="/myprofile" access="hasRole('ROLE_USER')"/>

to check if the user have setted the propper role into the previous List list.

If it have so forward the request to the controller method that handle this Http Request otherwise avoid that this HttpRequest come to this controller method and show a page that say that the user can't access to this resource.

Upvotes: 4

Views: 1437

Answers (1)

Bohuslav Burghardt
Bohuslav Burghardt

Reputation: 34826

Here is an explanation of some of the concepts and questions you are asking about.


AuthenticationManager

AuthenticationManager is the component responsible for processing the Authentication request. The Authentication request might be instance of UsernamePasswordAuthenticationToken for username/password logins.

For other implementations look at Authentication JavaDoc.

AuthenticationManager also has collection of AuthenticationProvider implementations. These components are capable of processing specific Authentication types and AuthenticationManager iterates through them attempting to find one capable of handling the Authentication passed to it. If it finds one, it calls it with Authentication object presented and returns fully populated Authentication object if successful (otherwise AuthenticationException is thrown).


AuthenticationProvider

As mentioned above, AuthenticationProvider processes certain type of Authentication request. For instance the DaoAuthenticationProvider will perform following steps when called by AuthenticationManager:

  • Take the UsernamePasswordAuthenticationToken passed to it
  • Use the UserDetailsService service implementation provided to it (in your case it is UserDetailServiceImpl) to look up user by username
  • Check the password provided in the authentication token against the user using PasswordEncoder and SaltSource, if specified.
  • If the authentication succeeds, returns the populated Authentication object (UsernamePasswordAuthenticationToken in this case), which contains principal, credentials and is marked as authenticated
  • In case the authentication fails, AuthenticationException will be thrown

DaoAuthenticationProvider which you are using which is capable of processing UsernamePasswordAuthenticationToken requests. So typically form logins, and so on. You can see which types of authentications provider support by looking at its supports() method implementation, which in case of DaoAuthenticationProvider looks like this:

public boolean supports(Class<?> authentication) {
    return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication));
}

Spring Security Filter Chain

Now let's look at the Security filter chain, as defined by Spring Security documentation:

The order that filters are defined in the chain is very important. Irrespective of which filters you are actually using, the order should be as follows:

  1. ChannelProcessingFilter, because it might need to redirect to a different protocol

  2. SecurityContextPersistenceFilter, so a SecurityContext can be set up in the SecurityContextHolder at the beginning of a web request, and any changes to the SecurityContext can be copied to the HttpSession when the web request ends (ready for use with the next web request)

  3. ConcurrentSessionFilter, because it uses the SecurityContextHolder functionality but needs to update the SessionRegistry to reflect ongoing requests from the principal

  4. Authentication processing mechanisms - UsernamePasswordAuthenticationFilter, CasAuthenticationFilter, BasicAuthenticationFilter etc - so that the SecurityContextHolder can be modified to contain a valid Authentication request token

  5. The SecurityContextHolderAwareRequestFilter, if you are using it to install a Spring Security aware HttpServletRequestWrapper into your servlet container

  6. RememberMeAuthenticationFilter, so that if no earlier authentication processing mechanism updated the SecurityContextHolder, and the request presents a cookie that enables remember-me services to take place, a suitable remembered Authentication object will be put there

  7. AnonymousAuthenticationFilter, so that if no earlier authentication processing mechanism updated the SecurityContextHolder, an anonymous Authentication object will be put there

  8. ExceptionTranslationFilter, to catch any Spring Security exceptions so that either an HTTP error response can be returned or an appropriate AuthenticationEntryPoint can be launched

  9. FilterSecurityInterceptor, to protect web URIs and raise exceptions when access is denied

When the user submits login form, AuthenticationManager is called at step 4 in the filter chain. In the case of form login it would be handled by UsernamePasswordAuthenticationFilter which calls the AuthenticationManager to process the authentication:

public Authentication attemptAuthentication(HttpServletRequest request,
        HttpServletResponse response) throws AuthenticationException {
    // ...
    return this.getAuthenticationManager().authenticate(authRequest);
}

Using the debugger it seems to me that when te user try to access to a specific page this loadUserByUsername() return the UserDetails

Actually the loadUserByUsername() is called when user authenticates, for instance after submitting login form. If the user is already authenticated this is not called.

I think that means that the page related to the /springLogin resource is accessible to everyone:

<security:intercept-url pattern="/springLogin" access="permitAll" />

Then I think that Spring will automatically use following to check if the user has proper role:

<security:intercept-url pattern="/myprofile" access="hasRole('ROLE_USER')" />

Correct. This process is handled by FilterSecurityInterceptor, which extends AbstractSecurityInterceptor - core Spring Security component dealing with authorization. If the user is not authenticated or doesn't have the required role an Exception is thrown and handled by the ExceptionTranslationFilter. This filter handles Security exceptions. For instance in case of authentication failure it will redirect user to authentication entry point, e.g. the login page.


Internal architecture of Spring Security is pretty nicely described in the reference documentation. I recommend to take a look at it.

Upvotes: 3

Related Questions