如果身份验证/授权失败,如何为Spring Security定制响应?

时间:2019-06-05 03:06:54

标签: spring-boot spring-security

我正在开发一个Spring Boot应用程序,使用Spring Security来检查用户登录是否成功或用户是否有权访问其中的资源。

现在,无论输入的用户名/密码错误还是用户被锁定,它始终返回401未经授权。我想自定义它,说如果凭据错误,它将返回400,如果用户被锁定,它将返回401。

我已经为应用程序定义了自定义过滤器和身份验证入口点。

public class JWTUsernameAndPasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

   ....

   @Override
   public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
        throws AuthenticationException {
        Optional<UserEntity> userEntity = userService
            .findByUserId(request.getParameter("username"));
        if (userEntity.isPresent()) {
           if (userEntity.get().getStatus().equals("LOCKED")) {
              throw new BadCredentialsException("User is locked!"); //I want it to return status 403 here
           }
        }

        String privateKey = userService.findByUserId(request.getParameter("username")).get().getLoginKey();

        String username = request.getParameter("username");
        String password = CryptoUtil.decrypt(privateKey, request.getParameter("password"));

        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username,
            password, Collections.emptyList());
        return authenticationManager.authenticate(authenticationToken);
    }

    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
        AuthenticationException failed) throws IOException, ServletException {
        String username = request.getParameter("username");
        try {
            // Do some process stuff....
        } catch (Exception ex) {
           log.error(ex);
        }
        super.unsuccessfulAuthentication(request, response, failed); // I want to return 400 here
    }
}


public class JwtAuthenticationExceptionEntryPoint implements AuthenticationEntryPoint {

    @Getter
    @Value("${application.version}")
    private String version;

    @Getter
    @Value("${application.name}")
    private String applicationName;

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response,
        AuthenticationException authException) throws IOException, ServletException {

         RESTFulStatus status = new RESTFulStatus(RESTFulStatus.STATUS_ERROR, HttpStatus.UNAUTHORIZED.value(),
            HttpStatus.UNAUTHORIZED.getReasonPhrase(),
            authException.getMessage(), version, applicationName, Instant.now(), request.getRequestURI());
         log.error(authException.getMessage());
         ObjectMapper mapper = new ObjectMapper();
         response.setStatus(HttpStatus.UNAUTHORIZED.value());
         response.setContentType("application/json");
         response.setCharacterEncoding("UTF-8");
         response.getWriter().write(mapper.writeValueAsString(status));
    }
}

配置类

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
   @Autowired
   private JwtAuthenticationExceptionEntryPoint authenticationEntryPoint;

   @Override
   protected void configure(HttpSecurity http) throws Exception {
      http.headers().cacheControl();
      http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
      http.csrf().disable();
      http.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint).and()
            .addFilter(new JWTUsernameAndPasswordAuthenticationFilter(authenticationManager(), jwtConfig, userService, accessHistoryService))
            .addFilter(new JwtAuthorizationFilter(authenticationManager(), jwtConfig)).authorizeRequests();

      http.authorizeRequests().antMatchers(HttpMethod.POST, jwtConfig.getUri()).permitAll()
            .antMatchers("/auth/**").permitAll()
            .antMatchers("/1/**").hasRole(RoleConstant.ROLE_ADMIN)
            .antMatchers("/test/**").hasAnyRole(RoleConstant.ROLE_ADMIN)
            .antMatchers("/report/**").hasAnyRole(RoleConstant.ROLE_ADMIN, RoleConstant.ROLE_REPORT)
            .anyRequest().authenticated();
      // If a user try to access a resource without having enough permissions
      http.exceptionHandling().accessDeniedPage("/login");

      //test in browser
      http.httpBasic();
   }
}

1 个答案:

答案 0 :(得分:1)

扩展UsernamePasswordAuthenticationFilter时,您可以自定义AuthenticationFailureHandlerattemptAuthentication()抛出AuthenticationException时将调用它。

AuthenticationFailureHandler看起来像:

public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler{

    @Override
    public void onAuthenticationFailure(HttpServletRequest request,
            HttpServletResponse response, AuthenticationException exception)
            throws IOException, ServletException {


         RESTFulStatus status = new RESTFulStatus(RESTFulStatus.STATUS_ERROR, HttpStatus.UNAUTHORIZED.value(),
            HttpStatus.UNAUTHORIZED.getReasonPhrase(),
            authException.getMessage(), version, applicationName, Instant.now(), request.getRequestURI());
         log.error(exception.getMessage());
         ObjectMapper mapper = new ObjectMapper();
         response.setStatus(HttpStatus.UNAUTHORIZED.value());
         response.setContentType("application/json");
         response.setCharacterEncoding("UTF-8");
         response.getWriter().write(mapper.writeValueAsString(status));
    }

}

注意:

  • 确保attemptAuthentication()会抛出AuthenticationException 如果身份验证失败。
  • 确保将此自定义AuthenticationFailureHandler设置为 JWTUsernameAndPasswordAuthenticationFilter在配置过程中