Chapter 5. Securing Your Applications

Spring Security provides a wide range of features for securing Java/Spring-based enterprise applications. At first glance, the security features of Servlets or EJB look an alternative of Spring Security; however, these solutions lack certain requirements for developing enterprise applications. The server's environment dependency could be another drawback of these solutions.

Authentication and authorization are the main areas of application security. Authentication is the verification of a user's identity, whereas authorization is the verification of the privileges of a user.

Spring Security integrates with a variety of authentication models, most of which are provided by third-party providers. In addition, Spring Security has developed its own authentication models, based upon major security protocols. Here are some of these protocols:

  • Form-based authentication
  • HTTP Basic authentication
  • LDAP
  • JAAS
  • Java Open Single Sign On
  • Open ID authentication

Since there is a big list of Spring Security models, we can only detail the most popular of them in this chapter.

Spring Security is quite strong on authorization features. We can categorize these features into three groups: web, method, and domain object authorization. Later, in the Authorization section, we will explain these categories.

In order to use Spring Security features in a web application, you need to include the following dependencies in your project:

<dependency>
   <groupId>org.springframework.security</groupId>
   <artifactId>spring-security-web</artifactId>
   <version>4.0.2.RELEASE</version>
</dependency>
<dependency>
   <groupId>org.springframework.security</groupId>
   <artifactId>spring-security-config</artifactId>
   <version>4.0.2.RELEASE</version>
</dependency>

The open standard for authorization (OAuth) concept, introduced in late 2006, aimed to allow third-party limited access to users' resources on Microsoft, Google, Facebook, Twitter, or similar accounts, without sharing their usernames and passwords.

In 2010, OAuth was standardized as the OAuth 1.0a protocol in RFC 5849. Later in 2012, it evolved to the OAuth 2.0 framework in RFC 6749. In this chapter, we explain Spring's OAuth 2.0 framework implementation.

The OAuth 2.0 Authorization Framework enables a third-party application to obtain limited access to an HTTP service, either on behalf of a resource owner by orchestrating an approval interaction between the resource owner and the HTTP service, or by allowing the third-party application to obtain access on its own behalf (http://tools.ietf.org/html/rfc6749).

Spring provides a separate module (spring-security-oauth2) for its OAuth 2.0 implementation, which relies on Spring Security features. In this chapter, we explain authentication and how Spring facilitates the process by providing its own easy-to-use features as well as giving you options to plug in your customized implementation. Authorization is the second topic included in this chapter, in which we explain how to configure separate security models within the same application. In the last section, we explain Spring's OAuth 2.0 feature.

Authentication

In an application's security domain, the first thing that comes to mind is authentication. During the authentication process, an application compares a user's credentials (for example, a username and password or a token) with the information available to it. If these two match, it allows the process to enter the next step. We will follow the next step in the Authorization section.

Spring Security provides features to support a variety of security authentication protocols. In this section, we will focus on basis and form-based authentication.

Spring provides a built-in form for the purpose of form-based authentication. In addition, it lets you define your own customized login form.

Spring gives you the option to use in-memory authentication, in which the username and password will be hardcoded in the application.

An alternative option is to use a customized authentication provider that lets you decide how to authenticate users by program, for example, calling a data layer service to validate users. It also lets you integrate Spring Security with your existing security framework.

The first thing you need in order to configure Spring Security to authenticate users is to define a Servlet filter known as springSecurityFilterChain. This filter is responsible for applying security measures (for example, validating users, navigating to different pages after login bases on the user's role, and protecting application URLs) in a web application.

WebSecurityConfigurerAdapter is a convenient Spring template for configuring springSecurityFilterChain:

@Configuration
@EnableWebSecurity
@ComponentScan(basePackages = "com.springessentialsbook.chapter5")
public class WebSecurityConfigurator extends WebSecurityConfigurerAdapter {
    @Autowired
    private AuthenticationSuccessHandler authenticationSuccessHandler;

    @Autowired
    public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication().withUser("operator").password("password").roles("USER");
        auth.inMemoryAuthentication().withUser("admin").password("password").roles("ADMIN");
        auth.inMemoryAuthentication().withUser("accountant").password("password").roles("ACCOUNTANT");

    }

@Configuration registers this class as a configuration class. The method's name, configureGlobalSecurity, is not important, as it only configures an AuthenticationManagerBuilder instance through autowire. The only important thing is annotating the class with @EnableWebSecurity, which registers Spring web security in the application. As you can see, we used in-memory authentication for simplicity, which hardcoded the user's username, password, and role used for user authentication. In real enterprise applications, LDAP, databases or the cloud provide services for validating user credentials.

We don't code all that much in the config class, but it really does a lot behind the scenes. Here are some of the features implemented by the class. Apart from user authentication and role assignment, we will explain other features next in this chapter.

  • Protecting all application URLs by asking for authentication first
  • Creating a Spring default login form to authenticate the user
  • Authenticating users (operator/password, admin/password, accountant/password) and assigning separate roles for each user (user, admin, and accountant) using form-based authentication
  • Allowing the user to log out
  • CSRF attack prevention

As we explained, in real-world enterprise applications, one never hardcodes user credentials within the application's code. You may have an existing security framework that calls a service in order to validate users. In this case, you can configure Spring Security in a customized service to authenticate the user.

The authentication interface implementation is what carries user credentials within the Spring Security context. You can obtain the authentication object anywhere within the application using SecurityContextHolder.getContext().getAuthentication().

When a user is authenticated, Authentication will be populated. If you don't specify AuthenticationProvider (for example, if you use in-memory authentication), Authentication will be populated automatically. Here, we look at how to customize AuthenticationProvider and populate the Authentication object.

The following code shows how Spring's AuthenticationProvider implementation class integrates with a customized user detail service (which returns user credentials from a data source):

@Component
public class MyCustomizedAuthenticationProvider implements AuthenticationProvider {
  @Autowired
  UserDetailsService userService;
  public Authentication authenticate(Authentication authentication) throws AuthenticationException {
    User user=null;
    Authentication auth=null;
    String username=authentication.getName();
    String password=authentication.getCredentials().toString();
    user= (User) userService.loadUserByUsername(username);
    if(password ==null ||   ! password.equals(user.getPassword())) throw new UsernameNotFoundException("wrong user/password");
    if(user !=null){
      auth = new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword(), user.getAuthorities());
    } else throw new UsernameNotFoundException("wrong user/password");
    return auth;

  }
  public boolean supports(Class<?> aClass) {
    return true;
  }
}

Your customized authentication provider should implement AuthenticationProvider and its authenticate method.

Note that the userService instance here should implement the Spring UserDetailsService interface and its loadUserByUserName method. The method returns the data model of a user. Note that you can extend Spring's User object and create your own customized user. We mocked the UserService integration part with a data service. In a real application, there could be a service call to fetch and return user data, and your UserServiceImpl class will only wrap the user in the UserDetails data model, as follows:

@Service
public class UserServiceImpl implements UserDetailsService {
    public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
        // suppose we fetch user data from DB and populate it into // User object
        // here we just mock the service
        String role=null;
        if(userName.equalsIgnoreCase("admin")){
            role ="ROLE_ADMIN";
        }else if(userName.equalsIgnoreCase("accountant") ){
            role="ROLE_ACCOUNTANT";
        }else if(userName.equalsIgnoreCase("operator")){
            role="ROLE_USER";
        }else{
            throw new UsernameNotFoundException("user not found in DB");
        }
        List<GrantedAuthority> authorities=new ArrayList<GrantedAuthority>();
        authorities.add(new GrantedAuthorityImpl(role));
        return new User(userName, "password", true, true, true, true, authorities);
    }
}

After this, you can set your customized provider in the configuration class, as shown in the following code. When a user is authenticated, the authentication object should be populated programmatically. Later in this chapter, in the Authorization section, we will explain this object.

@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled=true)
@ComponentScan(basePackages = "com.springessentialsbook.chapter5")
public class MultiWebSecurityConfigurator   {

    @Autowired
    private AuthenticationProvider authenticationProvider;

    @Autowired
    public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(authenticationProvider);
    }

We defined the springSecurityFilterChain filter in the first step. To make it work, we need to register it in the web application, like so:

import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
public class SecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer { }

The class doesn't need any code, as the superclass (AbstractSecurityWebApplicationInitializer) registers the Spring Security filter. This happens while the Spring context starts up.

If we don't use Spring MVC, we should pass the following to the constructor:

super(WebSecurityConfigurator);

The class AnnotatedConfigDispatcherServletInitializer extends Spring's Servlet initializer AbstractAnnotationConfigDispatcherServletInitializer. This class allows Servlet 3 containers (for example, Tomcat) to detect the web application automatically, without needing web.xml. This is another step of simplifying the setting up of a web application, and it registers DispatcherServlet and Servlet mapping programmatically. By setting the WebSecurityConfigurator class in getRootConfigClasses, you tell the parent class method that creates the context of the application to use your annotated and customized Spring Security configuration class. The following is the code for the AnnotatedConfigDispatcherServletInitializer class:

public class AnnotatedConfigDispatcherServletInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
   @Override
   protected Class<?>[] getRootConfigClasses() {
      return new Class[] { MultiWebSecurityConfigurator.class };
   }

   @Override
   protected Class<?>[] getServletConfigClasses() {
      return null;
   }

   @Override
   protected String[] getServletMappings() {
      return new String[] { "/" };
   }
}

What we have configured so far in Spring Security is for checking whether the username and password are correct. If we want to configure other security features, such as defining a login page and the web application URL request to be authenticated, we need to override the configure(HttpSecurity http) method of WebSecurityConfigurerAdapter.

In our customized security configurator, we define a login page (login.jsp) and an authorization failure page (nonAuthorized.jsp), as follows:

@Configuration
@EnableWebSecurity
public class WebSecurityConfigurator extends WebSecurityConfigurerAdapter {
@Autowired
private AuthenticationSuccessHandler authenticationSuccessHandler;

   ...
   @Override
   public void configure(HttpSecurity http) throws Exception {
   ...

   .and().formLogin()
   .loginPage("/login").successHandler(authenticationSuccessHandler)
   .failureUrl("/nonAuthorized")
   .usernameParameter("username").passwordParameter("password").loginProcessingUrl("/login")

...

This code tells Spring to process a submitted HTTP request form (with the POST method) with the expected username and password as parameters and "/login" as the action. Here is the login form:

<form role="form" action="/login" method="post">
  <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
  <div>
    <label for="username">Username</label>
    <input type="text" name="username" id="username" required autofocus>
  </div>
  <div>
    <label for="password">Password</label>
    <input type="password" name="password" id="password" required>
  </div>
  <button type="submit">Sign in</button>
</form>

Tip

If you don't specify a username, password, and loginProcessingUrl parameter in the configuration file, Spring Security expects j_username, j_password, and j_spring_security_check from the client browser. By overriding Spring's default values, you can hide the Spring Security implementation from the client browser.

A cross-site request forgery (CSRF) attack happens, for example, when a malicious link clicked by an authenticated web client performs an unwanted action, such as transferring funds, obtaining contact e-mails, or changing passwords. Spring Security provides a randomly generated CSRF to protect the client from CSRF attacks.

If you omit .loginPage in the configure method, Spring uses its default login page, which is a very plain HTML login page. In this case, Spring Security uses the expected j_username, j_password, and j_spring_security_check parameters for the username, password, and action, and you should not configure them in the method.

For example, here we ask Spring to provide its own default login form:

@Override
public void configure(HttpSecurity http) throws Exception {
   ...
         .and().formLogin()
         .successHandler(authenticationSuccessHandler)
         .failureUrl("/nonAuthorized")
         ...

}

Spring Security supports HTTP Basic authentication, in which the client browser opens a popup (for the initial time) when you want to access a resource that matches a pattern ("/adResources*/**" in this case):

protected void configure(HttpSecurity http) throws Exception {
    http.antMatcher("/adResources*/**").authorizeRequests().anyRequest().hasRole("ADMIN")
        .and()
        .httpBasic();
}

Server-side navigation could be the next step after authentication. Even though routing information is provided from the client side in modern client-side frameworks such as AngularJS, you may still want to keep routing logic on the server side. A success handler is a Spring Security feature that lets you define navigation logic after authentication in a web application.

Spring Security lets you configure customized server-side navigation after authentication. You can configure it inside the configure method (using successHandler):

@Override
public void configure(HttpSecurity http) throws Exception {
    ...
    .loginPage("/login").successHandler(authenticationSuccessHandler)
      ....
}

Your customized navigation handler should implement the interface AuthenticationSuccessHandler. OnAuthenticationSuccess is the method that will be called when a user is authenticated. Within this method, we should define the target URL. In the sample implementation class shown here, the user's role is just used to define the target URL:

@Component
public class MyCustomizedAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
    private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();

    public void onAuthenticationSuccess(final HttpServletRequest request, final HttpServletResponse 
    response, final Authentication authentication) throws IOException {
        handle(request, response, authentication);
        final HttpSession session = request.getSession(false);
        if (session != null) {
            session.setMaxInactiveInterval(3600);//1 hour
        }
        clearAttributes(request);
    }

    protected void handle(final HttpServletRequest request, final HttpServletResponse response, final 
    Authentication authentication) throws IOException {
        final String url = getUrl(authentication);
        if (response.isCommitted()) {
           return;
        }
        redirectStrategy.sendRedirect(request, response, url);
    }

    private String getUrl(final Authentication authentication) {
        String url=null;
        final Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
        for (final GrantedAuthority grantedAuthority : authorities) {
            if (grantedAuthority.getAuthority().equals("ROLE_USER")) {
                url= "/user" ;
                break;
            } else if (grantedAuthority.getAuthority().equals("ROLE_ADMIN")) {
                url= "/admin" ;
                break;
             } else if (grantedAuthority.getAuthority().equals("ROLE_ACCOUNTANT")) {
                url= "/accountant" ;
                break;
            }else {
                throw new IllegalStateException();
            }
        }
        return url;
    }

    protected void clearAttributes(final HttpServletRequest request) {
        final HttpSession session = request.getSession(false);
        if (session == null) {
            return;
        }
        session.removeAttribute(WebAttributes.AUTHENTICATION_EXCEPTION);
    }

    public void setRedirectStrategy(final RedirectStrategy redirectStrategy) {
        this.redirectStrategy = redirectStrategy;
    }

    protected RedirectStrategy getRedirectStrategy() {
        return redirectStrategy;
    }
}

Spring Security lets you configure your security configuration in multiple methods, and in each method, you can define a different category of resources. Here, we have separated the security configuration for form-based and basic authentication into these two classes:

@EnableWebSecurity
@ComponentScan(basePackages = "com.springessentialsbook.chapter5")
public class MultiWebSecurityConfigurator {
   @Autowired
   private AuthenticationProvider authenticationProvider;
   @Autowired
   public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception {

      auth.authenticationProvider(authenticationProvider);
   }
   @Configuration
   protected static class LoginFormBasedWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
      @Autowired
      private AuthenticationSuccessHandler authenticationSuccessHandler;
      @Override
      public void configure(HttpSecurity http) throws Exception {
         http.authorizeRequests()
               ...
               .permitAll();
      }
   }
   @Configuration
   @Order(1)
   public static class HttpBasicWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
      @Override
      protected void configure(HttpSecurity http) throws Exception {
         http.antMatcher("/adResources*/**").authorizeRequests().anyRequest().hasRole("ADMIN")
         .and()
         .httpBasic();
      }

}}

For example, in one method, we configure resources in the adResources path to be viewed by the admin role in an HTTP-based authentication (the browser opens a popup and asks for a username and password). In the second method, we apply form login authorization and limit access to resources based on user roles.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset