自定义异常配置
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所示。

Figure 1. 图12-1 认证失败响应
当用户登录成功后,但是不具备 admin 角色,此时如果访问 /admin 接口,则结果如图12-2 所示。

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 两个变量中添加异常处理器即可。
配置完成后,启动项目进行测试,不同接口将会给出不同的异常响应,这里不再赞述。