自定义异常配置

Spring Security 中默认提供的异常处理器不一定满足我们的需求,如果开发者需要自定义, 也是可以的,定义方式如下:

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.authorizeRequests()
                .antMatchers("/admin").hasRole("admin")
                .anyRequest().authenticated()
                .and()
                .exceptionHandling()
                .authenticationEntryPoint((req,resp,e)-> {
                    resp.setStatus(HttpStatus.UNAUTHORIZED.value());
                    resp.getWriter().write("please login");
                })
                .accessDeniedHandler((req,resp,e)->{
                    resp.setStatus(HttpStatus.FORBIDDEN.value());
                    resp.getWriter().write("forbidden");
                })
                .and()
                .formLogin()
                .and()
                .csrf().disable();

    }
}

首先我们设置了访问 /admin 接口必须具备 admin 角色,其他接口只需要认证就可以访问。然后我们对 exceptionHandling 分别配置了 authenticationEntryPoint 和 accessDeniedHandler。回顾上一小节的源码分析,这里配置完成后,defaultEntryPointMappings 和 defaultDeniedHandlerMappings 中的处理器就会失效。

接下来我们启动项目,如果用户未经登录就访问 /hello 接口,则结果如图12-1所示。

image 2024 04 14 23 52 02 342
Figure 1. 图12-1 认证失败响应

当用户登录成功后,但是不具备 admin 角色,此时如果访问 /admin 接口,则结果如图12-2 所示。

image 2024 04 14 23 52 46 642
Figure 2. 图12-2 鉴权失败响应

当然,开发者也可以为不同的接口配置不同的异常处理器,配置方式如下:

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        AntPathRequestMatcher matcher1 = new AntPathRequestMatcher("/qq/**");
        AntPathRequestMatcher matcher2 = new AntPathRequestMatcher("/wx/**");
        http.authorizeRequests()
                .antMatchers("/wx/**").hasRole("wx")
                .antMatchers("/qq/**").hasRole("qq")
                .anyRequest().authenticated()
                .and()
                .exceptionHandling()
                .defaultAuthenticationEntryPointFor((req, resp, e) -> {
                    resp.setContentType("text/html;charset=utf-8");
                    resp.setStatus(HttpStatus.UNAUTHORIZED.value());
                    resp.getWriter().write("请登录,QQ 用户");
                }, matcher1)
                .defaultAuthenticationEntryPointFor((req, resp, e) -> {
                    resp.setContentType("text/html;charset=utf-8");
                    resp.setStatus(HttpStatus.UNAUTHORIZED.value());
                    resp.getWriter().write("请登录,WX 用户");
                }, matcher2)
                .defaultAccessDeniedHandlerFor((req, resp, e) -> {
                    resp.setContentType("text/html;charset=utf-8");
                    resp.setStatus(HttpStatus.FORBIDDEN.value());
                    resp.getWriter().write("权限不足,QQ 用户");
                }, matcher1)
                .defaultAccessDeniedHandlerFor((req, resp, e) -> {
                    resp.setContentType("text/html;charset=utf-8");
                    resp.setStatus(HttpStatus.FORBIDDEN.value());
                    resp.getWriter().write("权限不足,WX 用户");
                }, matcher2)
                .and()
                .formLogin()
                .and()
                .csrf().disable();

    }
}

一开始我们定义了两个路径匹配器 matcher1 和 matcher2,然后配置 /wx/** 格式的路径需要有 wx 角色才能访问,/qq/** 格式的路径则需要有 qq 角色才可以访问。接下来分别调用 defaultAuthenticationEntryPointFor 方法和 defaultAccessDeniedHandlerFor 方法向 defaultEntryPointMappings 和 defaultDeniedHandlerMappings 两个变量中添加异常处理器即可。

配置完成后,启动项目进行测试,不同接口将会给出不同的异常响应,这里不再赞述。