How to do it...

Let us now add important supporting components to the authentication process established by the previous recipe:

  1. Create and apply two filters needed to establish a security filter chain in this new security model. Implement our first filter interface, org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter, which will intercept the authentication and authorization process every time /login is called. Save this AppAuthenticationFilter class in org.packt.secured.mvc.handler package:
public class AppAuthenticationFilter extends  
    UsernamePasswordAuthenticationFilter { 
 
  @Override 
  protected void   
      successfulAuthentication(HttpServletRequest  
    request, HttpServletResponse response, FilterChain  
    chain, Authentication authResult) throws IOException,  
        ServletException { 
         
    Collection<? extends GrantedAuthority> authorities =  
        authResult.getAuthorities(); 
    List<String> roles = new ArrayList<String>(); 
    for (GrantedAuthority a : authorities) { 
      roles.add(a.getAuthority()); 
    } 
     
    String name = obtainPassword(request); 
       String password = obtainUsername(request); 
            
    UsernamePasswordAuthenticationToken userDetails =  
    new UsernamePasswordAuthenticationToken(name, password,  
          authorities); 
    setDetails(request, userDetails);   
    chain.doFilter(request, response); 
  } 
 
  @Override 
  protected void  
    unsuccessfulAuthentication(HttpServletRequest  
    request, HttpServletResponse response, 
      AuthenticationException failed) throws IOException,  
        ServletException { 
 
    response.sendRedirect("/ch04/login.html?error=true"); 
  } 
   
  @Override 
  public Authentication  
     attemptAuthentication(HttpServletRequest  
      request, HttpServletResponse response) 
        throws AuthenticationException { 
     
    String name = obtainPassword(request); 
       String password = obtainUsername(request); 
         
    SecurityContext context =  
        SecurityContextHolder.getContext(); 
    Authentication auth = null; 
    if(context.getAuthentication() == null){ 
       auth = new UsernamePasswordAuthenticationToken( 
            name, password); 
       setDetails(request,  
           (UsernamePasswordAuthenticationToken) auth);   
    }else{ 
       auth = (AnonymousAuthenticationToken)  
           context.getAuthentication(); 
       return auth; 
    }          
    return auth; 
  } 
}

This class has three overridden methods responsible for filtering incoming authentication requests, namely attemptAuthentication(), which is executed once an anonymous user attempts to access the /login, successfulAuthentication(), which runs after an authentication has been created either from an authenticated or valid anonymous user, and lastly, unsuccessfulAuthentication(), which is responsible for global error page redirection, equivalent to executing http.formLogin.failureUrl(). Authentication is classified into two types, org.springframework.security.authentication.UsernamePasswordAuthenticationToken and org.springframework.security.authentication.AnonymousAuthenticationToken.

Do not apply @Component on any filter implementation because it will give you java.lang.IllegalArgumentException: authenticationManager must be specified.
  1. The next custom filter implementation in our filter stack is the org.springframework.security.web.authentication.AnonymousAuthenticationFilter, which is responsible for managing anonymous user access. Save this class AppAnonAuthFilter together with the previous filter:
public class AppAnonAuthFilter  
       extends AnonymousAuthenticationFilter { 
  private String principal; 
  private String key; 
  private List<GrantedAuthority> authorities; 
   
    public AppAnonAuthFilter(String key) { 
       super(key); 
       this.key = key; 
  } 
   
  public AppAnonAuthFilter(String key, Object principal,  
      List<GrantedAuthority> authorities) { 
     super(key, principal, authorities); 
     this.key = key; 
     this.principal = principal.toString(); 
     this.authorities = authorities; 
  } 
   
  @Override 
  protected Authentication  
      createAuthentication(HttpServletRequest request) { 
     
    if(principal.equalsIgnoreCase( 
        request.getParameter("username")) ){ 
          AnonymousAuthenticationToken authTok =  
          new AnonymousAuthenticationToken(key, principal,  
           authorities); 
          SecurityContext context =  
            SecurityContextHolder.getContext(); 
          context.setAuthentication(authTok); 
          return authTok; 
    } 
    return null; 
  } 
} 

This class creates an Authentication object once an anonymous account guest has been detected; otherwise, it just throws null to the Spring Security container. This filter must be programmed not to create conflict with the processes of the UsernamePasswordAuthenticationFilter class.

Do not apply @Component on any filter implementation because it will give you a HTTP status 500.
  1. As helper objects, handlers are triggered by security models every time an Authentication object is thrown. A custom success authentication handler assists filter chains in defining the different default success URLs after a successful user authentication process. This class also overrides the formLogin.defaultSuccessUrl() and gives the application several options of default view pages depending on the roles of the users:
@Component 
public class CustomSuccessHandler extends  
    SimpleUrlAuthenticationSuccessHandler { 
  
    private RedirectStrategy redirectStrategy =  
new DefaultRedirectStrategy(); 
  
    @Override 
    protected void handle(HttpServletRequest request,  
      HttpServletResponse response, Authentication  
        authentication) throws IOException { 
        String targetUrl = targetUrl(authentication); 
        if (response.isCommitted()) { 
            System.out.println("Can't redirect"); 
            return; 
        } 
        redirectStrategy.sendRedirect(request, response,  
          targetUrl); 
    } 
  
    protected String targetUrl(Authentication  
           authentication) { 
      String url = ""; 
      Collection<? extends GrantedAuthority> authorities =  
      authentication.getAuthorities(); 
      List<String> roles = new ArrayList<String>(); 
      for (GrantedAuthority a : authorities) { 
        roles.add(a.getAuthority()); 
      } 
      
      if (isUserRole(roles)) { 
          // add user-related transactions here 
url = "/deptform.html"; 
      } else if (isAdminRole(roles)){ 
          // add admin-related transactions here 
         url = "/deptform.html"; 
      } else if (isHrAdminRole(roles)){ 
          // add admin-related transactions here 
url = "/deptform.html"; 
      } else{ 
        url = "/deptform.html"; 
        } 
       return url; 
    } 
      // refer to sources 
} 
  1. Save this file in org.secured.mvc.core.handler.
  2. Another handler called the logout handler must be custom implemented to provide routes once /logout is triggered, depending on the roles of the users. This class overrides formLogin.logoutSuccessUrl():
@Component 
public class CustomLogoutHandler extends  
    SimpleUrlLogoutSuccessHandler { 
   
  private RedirectStrategy redirectStrategy =  
new DefaultRedirectStrategy(); 
   
  @Override 
  public void onLogoutSuccess(HttpServletRequest request, 
    HttpServletResponse response, Authentication  
     authentication) throws IOException, ServletException { 
     
     String targetUrl = targetUrl(authentication); 
     if (response.isCommitted()) { 
        System.out.println("Can't redirect"); 
        return; 
     } 
     redirectStrategy.sendRedirect(request, response,  
        targetUrl); 
  } 
   
  protected String targetUrl(Authentication authentication) { 
     String url = ""; 
     Collection<? extends GrantedAuthority> authorities =  
      authentication.getAuthorities(); 
     List<String> roles = new ArrayList<String>(); 
     for (GrantedAuthority a : authorities) { 
         roles.add(a.getAuthority()); 
     } 
 
     if (isUser(roles)) { 
         url = "/after_logout.html?message=" 
+ "Thank your, User!"; 
      } else if (isAdmin(roles)){ 
         url = "/after_logout.html?message=" 
+ "Thank you, Admin!"; 
      } else if (isHrAdmin(roles)){ 
         url = "/after_logout.html?message=" 
+ "Thank you, HR!"; 
      }  
     return url; 
    } 
    // refer to sources 
} 
  1. The last handler essential to this recipe is the handler that will be executed when the user /login fails. Though this class has a limited scope of work, this can be useful in scrutinizing error messages depending on the nature of the validation error or the type of AuthenticationException:
@Component 
public class CustomFailureHandler extends  
   SimpleUrlAuthenticationFailureHandler { 
   
  private RedirectStrategy redirectStrategy = new  
    DefaultRedirectStrategy(); 
   
  @Override 
  public void onAuthenticationFailure(HttpServletRequest  
     request, HttpServletResponse response, 
     AuthenticationException exception) throws IOException,  
          ServletException { 
 
    String targetUrl = ""; 
    if(exception instanceof BadCredentialsException){ 
      targetUrl = "/login.html?error="  
           + exception.getMessage(); 
    } 
    else { 
      targetUrl = "/login.html?error=true"; 
    } 
 
    // refer to sources 
     
    redirectStrategy.sendRedirect(request, response,  
        targetUrl); 
  } 
} 
  1. Together with the previous custom authentication manager and providers, construct the proper model that will highlight the whole custom security architecture:
@Configuration 
@EnableWebSecurity 
public class AppSecurityModelC extends  
    WebSecurityConfigurerAdapter { 
   
  // refer to sources 
   
  @Override 
  protected void configure(AuthenticationManagerBuilder auth) throws Exception {  } 
   
  @Override 
  protected void configure(HttpSecurity http) throws Exception { 
      
      http 
         .anonymous().authorities("ROLE_ANONYMOUS") 
         .and() 
         .authorizeRequests() 
         .antMatchers("/login**", "/after**").permitAll() 
         .antMatchers("/deptanon.html").anonymous() 
         .anyRequest().authenticated()        
         .and() 
         .formLogin() 
         .loginPage("/login.html") 
         .defaultSuccessUrl("/deptform.html") 
         .failureHandler(customFailureHandler) 
         .successHandler(customSuccessHandler) 
         .and() 
         .addFilterBefore(appAnonAuthFilter(), 
             UsernamePasswordAuthenticationFilter.class) 
         .addFilter(appAuthenticationFilter( 
authenticationManager())) 
         .logout().logoutUrl("/logout.html") 
         .logoutSuccessHandler(customLogoutHandler) 
         .and() 
   .exceptionHandling().authenticationEntryPoint( 
             setAuthPoint()); 
           
     http.csrf().disable(); 
    } 
}     
  1. Implement the authentication filter that will assess all incoming valid users. Include this inside the AppSecurityModelC context definition:
@Bean 
public UsernamePasswordAuthenticationFilter  
   appAuthenticationFilter(AuthenticationManager authMgr) { 
       AppAuthenticationFilter filter =  
          new AppAuthenticationFilter(); 
       filter.setRequiresAuthenticationRequestMatcher( 
        new AntPathRequestMatcher("/login.html", "POST") ); 
       filter.setAuthenticationManager(authMgr); 
       return filter; 
} 
  1. Create another filter that will assess users that are not considered valid users and will allow guest or anonymous access to the application:
@Bean 
public AnonymousAuthenticationFilter   
         appAnonAuthFilter(){ 
    List<GrantedAuthority> anonAuth = new ArrayList<>();   
    anonAuth.add(new  
    SimpleGrantedAuthority("ROLE_ANONYMOUS")); 
    AppAnonAuthFilter anonFilter =  
       new AppAnonAuthFilter("ANONYMOUS","guest",anonAuth); 
    return  anonFilter;
}
  1. To register into Spring Security container the preceding filters, create an authentication manager by overriding the authenticationManager() of the WebSecurityConfigurerAdapter:
@Override 
protected AuthenticationManager authenticationManager()  
      throws Exception { 
     // refer to sources 
}
  1. Since we have bypassed the default filter configuration of the Spring Security framework, it is mandatory to tell the security platform when to trigger either of the filters implemented by injecting a new AuthenticationTrustResolver:
@Bean 
public AuthenticationTrustResolver trustResolver() { 
  return new AuthenticationTrustResolver() { 
 
    @Override 
    public boolean isRememberMe(final Authentication authentication) { 
      return true; 
    } 
 
    @Override 
    public boolean isAnonymous(final Authentication authentication) { 
      Collection<? extends GrantedAuthority> auths =  
      authentication.getAuthorities(); 
      List<String> roles = new ArrayList<String>(); 
      for (GrantedAuthority a : auths) { 
        roles.add(a.getAuthority()); 
      } 
      if(roles.contains("ROLE_ANONYMOUS") || roles.size() == 0){ 
        return true; 
      }
else{ return false; } } }; }
  1. Together with AuthenticationTrustResolver, implementation is a new AuthenticationEntryPoint to tell the platforms what URL to trigger with the custom filters created in preceding steps.
@Bean 
public AuthenticationEntryPoint setAuthPoint(){ 
  return new AppAuthPoint("/login.html"); 
}
  1. Create an additional @Controller for the request transactions of our anonymous account guest:
@Controller 
public class AnonymousController { 
   
  @RequestMapping(value="/deptanon.html") 
  public String anonPage(){ 
    return "dept_anon"; 
  }   
} 
  1. Create the additional view page /deptanon.html for the default view page of our anonymous account inside the src/main/webapp/anonymous_sites directory:
<html><head> 
<meta http-equiv="Content-Type" content="text/html;  
charset=ISO-8859-1"> 
<title>Anonymous</title> 
</head> 
<body> 
  <h1>Anonymous Account</h1> 
  <p>This content is for our beloved guest wants to check      our DEPARTMENT database. Enjoy! 
  <em><a href="<c:url value='/login.html'/>">You want some more about us? Login!.</a></em> 
</body> 
</html> 
  1. Update the views.properties for the added view details.
  2. Be sure to update the SpringContextConfig by importing the new AppSecurityModelC and including org.packt.secured.mvc.core.handler in its @ComponentScan.
  3. Save all files. Just like in the recent recipes, always clear the browser sessions and remove the previously deployed ch04 project in the Tomcat 9 server. clean, build, and deploy the Maven project.
..................Content has been hidden....................

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