自定义失败处理器

HeJin大约 2 分钟Spring全家桶SpringSecurity认证授权

我们还希望在认证失败或者是授权失败的情况下也能和我们的接口一样返回相同结构的json,这样可以让前端能对响应进行统一的处理。要实现这个功能我们需要知道SpringSecurityl的异常处理机制

在SpringSecurity中,如果我们在认证或者授权的过程中出现了异常会被ExceptionTranslationFilter捕获到。在ExceptionTranslationFilter中会去判断是认证失败还是授权失败出现的异常。

  • 如果是认证过程中出现的异常会被封装成AuthenticationException,然后调用AuthenticationEntryPoint对象的方法去进行异常处理。
  • 如果是授权过程中出现的异常会被封装成AccessDeniedException然后调用AccessDeniedHandler对象的方法去进行异常处理。

所以如果我们需要自定义异常处理,我们只需要自定义AuthenticationEntryPoint和AccessDeniedHandler,然后配置给SpringSecurity即可。

自定义实现类

认证异常处理器

@Component
public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint {
    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
        ResponseResult<String> responseResult = new ResponseResult<>(HttpStatus.UNAUTHORIZED.value(), "用户认证失败,请重新登录!");
        String jsonString = JSON.toJSONString(responseResult);

        WebUtils.renderString(response, jsonString);
    }
}

授权异常处理器

@Component
public class AccessDeniedHandlerImpl implements AccessDeniedHandler {
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
        ResponseResult<String> responseResult = new ResponseResult<>(HttpStatus.FORBIDDEN.value(), "您的权限不足!");
        String jsonString = JSON.toJSONString(responseResult);

        WebUtils.renderString(response, jsonString);
    }
}

配置给SpringSecurity

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;

    @Autowired
    private AuthenticationEntryPoint authenticationEntryPoint;

    @Autowired
    private AccessDeniedHandler accessDeniedHandler;

    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 关闭csrf
        http.csrf().disable()
                // 不通过session获取SecurityContext
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
                // 对于登录接口,允许匿名访问
                .antMatchers("/user/login").anonymous()
                // 除上面外的所有请求全部需要鉴权认证
                .anyRequest().authenticated();

        // 配置认证过滤器在UsernamePasswordAuthenticationFilter之前
        http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);

        http.exceptionHandling()
                // 认证失败处理器
                .authenticationEntryPoint(authenticationEntryPoint)
                // 授权异常处理器
                .accessDeniedHandler(accessDeniedHandler);
    }

}

测试

访问登录接口,输错密码:

修改测试接口/test,随便设置一个权限:

@RestController
public class HelloController {

    @GetMapping("/test")
    @PreAuthorize("hasAuthority('system:test:list333')") // 用户没有该权限
    public String test(){
        return "hello SpringSecurity";
    }

}

登录成功后,带上返回的token请求头访问/test:

限制认证失败和授权异常的返回都是统一格式的。