Spring Security Authentication

Spring Security Authentication

How is a request processed?

The client makes a HTTP request to a web server(TomCat or JBoss), the request is then forwarded to the appropriate servlet for processing(For spring : dispatcher servlet). The answer is updated and returned to the client. Filters sit in front of the servlets and intercepts requests, they can perform actions on the response request objects and forward them on the next filter in the chain. To enable Spring security you must enable spring security filter chain(delegating filter proxy will forward all requests to other Spring security filters to perform authentication or authorization checks), you only need to configure 2 components with the webserver: dispatcher servlet and delegating filter proxy. This makes the framework portable.

If we dive a little deeper, we can see that the delegationFilterProxy is registered in the Spring container's web.xml file and it just delegates the requests to another Spring filter known as filterChainProxy which also delegates to another object named SecurityFilterChain. SecurityFilterChain is a wrapper around the collection of Spring filters that perform the actual security task on the request and response objects. In the web.xml file we have:

<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>
     org.springframework.web.filter.DelegatingFilterProxy
</filter-class>
</filter>
<filter-mapping>
     <filter-name>springSecurityFilterChain</filter-name>
     <url-pattern>/*<url-pattern>
</filter-mapping>

They key is filter-mapping, in our case all the requests (/*) that the container receives would be intercepted by the delegating filterProxy which will perform security checks on all requests. SecurityFilterChainProxy manages all security filter chains, when it receives a request, it iterates through all security filters chains to find one that matches for request. It then request that filter Chain to perform security checks.

public interface SecurityFilterChain{
boolean matches(HttpServletRequest request);
List<Filter> getFilters();
}

Filter chain(how authentication requests are handled)

SecurityContextPersistenceFilter it is responsible for managing the security context(it holds the details of the authenticated principle (user ) like username, roles, identification details). First, it tries to retrieve the security context from a security context repository(in tipical web application, that would be the session ->httpSessionRepository). In case we didn't authenticate before, he'll add an empty context to SecurityContextHolder(SecurityContextHolder is making the security context available to all subsequence security filters and your application that are running in the same thread of execution.) The request will be intercepted then by an AuthenticationFilter, once the authentication is successful, the filter places the authentication principle into the security context, so no future request will need to authenticate(unless the user log out or the session expires) AnnonymousAtheticationFilter will add an anonymous authentication object into the security context. FilterSecurityInterceptor performs authorization, in a web application it will check if the authenticated principle has permission to access a particular HTTP resource. It can throw an authentication or authorization exception. This exception will be called and handled by ExceptionTranslationFilter (it can redirect to a login page or update the header/content-type).

SecurityContent Interface is simple, it has just 2 methods: to set and retrieve the authentication object, the context can be extended and updated to add features that you required to be stored in your application for authentication/authorization. The authentication filter will extract the credentials from the request and create an authenticationRequestToken(depending on your strategy), the filtering will send the authentication to an AuthenticationManager which will return an authenticated priciple token to the filter. AuthenticationManager actually delegates to one or more authentication providers to do the authentication. If there are more configured, the manager will try each one until he'll get an authenticated token.

public interface AuthenticationManager{
Anthentication authenticate(Authentication authentication) throw AuthenticationException;
}
public interface Authentication extends Principal, Serializable {
boolean isAuthenticated();
Object getPrincipal();
Object getCredentials();
Collection<? extends GrantedAuthority> getAuthorities();
}

Authentication principle will be stored in memory in the session context(which will be persisted in the session, accessible by any object from the current thread) Authentication providers: to perform authentication, they need to retrieve the principle details from an identity store(like a database) , for this they use UserDetailsService. Once the provider received the userDetails from the service, it will match the credentials against the request, if successful, it will return a new authenticated token back to the manager with the authentication flag set to true, credentials null and granted authorities populated.

public interface AuthenticationProvider{
Authentication authenticate(Authentication authentication) throws AuthenticationException;
boolean supports(Class<?> authentication);
}
public interface UserDetailsService{
UserDetails loadByUsername(String username) throws UsernameNotFoundException;
}

Recap:

authUntitled.png

So, how do we use Spring Security?

Add the dependency in your pom.xml file:

 <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
</dependency>

After you run the project, you'll see a security password in the application logs. If a user details service is not configured, then Spring provides a default one with an in-memory authentication manager which creates a default user with a randomly generated password.We'll use the password from the logs to log into the browser to get access to our API's.

Configuring Basic Authentication

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
       http.authorizeRequests().anyRequest().authenticated().and().httpBasic().and().logout();
    }
}

Basic Authentication: you don't need a login page, session ID, or cookies, you can even embed the credentials directly into the URL. Digest Authentication was developed to make use of new cryptographic hashing advancements and nonce values to prevent replay attacks: credentials are not transmitted in plaintext, md5 checksum hashes are used. Let's try to use Digest Authentication now:


import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.www.DigestAuthenticationEntryPoint;
import org.springframework.security.web.authentication.www.DigestAuthenticationFilter;

@Configuration
@Order(101)
public class AdminSecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception{
        httpSecurity.antMatcher("/employee")
                .addFilter(getDigestFilter()).exceptionHandling()
                .authenticationEntryPoint(getDigestEntryPoint())
                .and().authorizeRequests().antMatchers("/employee").hasRole("ADMIN");
    }

    public DigestAuthenticationFilter getDigestFilter() throws Exception{
        DigestAuthenticationFilter filter = new DigestAuthenticationFilter();
        filter.setUserDetailsService(userDetailsServiceBean());
        filter.setAuthenticationEntryPoint(getDigestEntryPoint());
        return filter;
    }

    private DigestAuthenticationEntryPoint getDigestEntryPoint(){
        DigestAuthenticationEntryPoint entry = new DigestAuthenticationEntryPoint();
        entry.setRealmName("admin-digest-realm");
        entry.setKey("sdgdfghbfd+!ds334SDS");
        return entry;
    }
    @Override
    protected void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception{
        authenticationManagerBuilder.inMemoryAuthentication()
                .withUser("user")
                .password("pass-user")
                .roles("USER")
                .and()
                .withUser("admin")
                .password("pass-admin")
                .roles("ADMIN");
    }

    @Override
    @Bean
    public UserDetailsService userDetailsServiceBean() throws Exception{
        return super.userDetailsServiceBean();
    }

    @Bean
    public PasswordEncoder passwordEncoder(){
        return NoOpPasswordEncoder.getInstance();
    }
}

The realm is the security policy domain and the key is an encoded string used to avoid replay attacks.

We can use NoOpPasswordEncoder as a Bean, it is okay for testing purposes,but do not use this in production!

So is Digest Authentication better than Basic Authentication?

Well, using Basic Authentication over HTTPS protects the user credentials and encrypts the payload being transmitted (usually this contain sensitive information).

Digest Authentication protocol (Digest-MD5) - on which most of the HTTP Digest Authentication is based - is considered in present deprecated due to a lot of security deficiencies that make it vulnerable to attacks:

  • Digest-MD5 outer hash does not protect the whole authentication exchange, this fact makes the mechanism vulnerable to MITM aatacks.

    -The RC4 algorithm is prone to attack when used as the security layer without discarding the initial key stream output

    -The DES cipher for the security layer is considered insecure due to its small key space

  • You cannot store the password as a one way hash

You can read more about this here .