Spring Security自定义失败处理器问题
作者:Maiko Star
Spring Security自定义失败处理器
我们还希望在认证失败或者是授权失败的情况下也能和我们的接口一样返回相同结构的json,这样可以让前端能对响应进行统一的处理。要实现这个功能我们需要知道SpringSecurity的异常处理机制。
在SpringSecurity中,如果我们在认证或者授权的过程中出现了异常会被ExceptionTranslationFilter捕获到。在ExceptionTranslationFilter中会去判断是认证失败还是授权失败出现的异常。
- 如果是认证过程中出现的异常会被封装成AuthenticationException然后调用AuthenticationEntryPoint对象的方法去进行异常处理。
- 如果是授权过程中出现的异常会被封装成AccessDeniedException然后调用AccessDeniedHandler对象的方法去进行异常处理。
所以如果我们需要自定义异常处理,我们只需要自定义AuthenticationEntryPoint和AccessDeniedHandler然后配置SpringSecurity即可。
在Spring Security中,你可以通过实现特定的接口来自定义认证失败处理器(AuthenticationFailureHandler
)和授权失败处理器(AccessDeniedHandler
)。以下是如何分别自定义它们的步骤:
一、自定义认证失败处理器(AuthenticationFailureHandler)
- 1.1创建自定义的
AuthenticationFailureHandler
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.authentication.AuthenticationFailureHandler; public class CustomAuthenticationFailureHandler implements AuthenticationFailureHandler { @Override public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException { // 在这里处理认证失败的情况 response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); response.getWriter().write("登录失败: " + exception.getMessage()); } }
- 1.2在Spring Security配置中使用你的自定义认证失败处理器
@Override protected void configure(HttpSecurity http) throws Exception { http // ... 其他配置 ... .formLogin() .failureHandler(new CustomAuthenticationFailureHandler()) // 设置自定义认证失败处理器 // ... 其他表单登录配置 ... // ... 其他HTTP安全配置 ... ; }
二、自定义授权失败处理器(AccessDeniedHandler)
- 2.1创建自定义的
AccessDeniedHandler
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.security.access.AccessDeniedException; import org.springframework.security.web.access.AccessDeniedHandler; public class CustomAccessDeniedHandler implements AccessDeniedHandler { @Override public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException { // 在这里处理授权失败的情况 response.setStatus(HttpServletResponse.SC_FORBIDDEN); response.getWriter().write("权限不足: " + accessDeniedException.getMessage()); } }
- 2.2在Spring Security配置中使用你的自定义授权失败处理器
@Override protected void configure(HttpSecurity http) throws Exception { http // ... 其他配置 ... .exceptionHandling() .accessDeniedHandler(new CustomAccessDeniedHandler()) // 设置自定义授权失败处理器 // ... 其他异常处理配置 ... // ... 其他HTTP安全配置 ... ; }
三、实战
我们还希望在认证失败或者是授权失败的情况下也能和我们的接口一样返回相同结构的json,这样可以让前端能对响应进行统一的处理。要实现这个功能我们需要知道SpringSecurity的异常处理机制。
在SpringSecurity中,如果我们在认证或者授权的过程中出现了异常会被ExceptionTranslationFilter捕获到。在ExceptionTranslationFilter中会去判断是认证失败还是授权失败出现的异常。
- 如果是认证过程中出现的异常会被封装成AuthenticationException然后调用AuthenticationEntryPoint对象的方法去进行异常处理。
- 如果是授权过程中出现的异常会被封装成AccessDeniedException然后调用AccessDeniedHandler对象的方法去进行异常处理。
所以如果我们需要自定义异常处理,我们只需要自定义AuthenticationEntryPoint和AccessDeniedHandler然后配置SpringSecurity即可。
(com.sangeng.handler)
- 自定义认证失败处理器:
@Component public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint { @Override public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { ResponseResult result = new ResponseResult(HttpStatus.UNAUTHORIZED.value(), "认证失败请重新登录"); String json = JSON.toJSONString(result); WebUtils.renderString(response, json); } }
- 自定义授权失败处理器:
@Component public class AccessDeniedHandlerImpl implements AccessDeniedHandler { @Override public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException { ResponseResult result = new ResponseResult(HttpStatus.FORBIDDEN.value(), "权限不足"); String json = JSON.toJSONString(result); WebUtils.renderString(response, json); } }
- 修改配置类:
@Configuration //配置类 @EnableWebSecurity // 开启Spring Security的功能 代替了 implements WebSecurityConfigurerAdapter public class SecurityConfig { @Autowired AuthenticationConfiguration authenticationConfiguration;//获取AuthenticationManager @Autowired JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter; @Autowired AccessDeniedHandlerImpl accessDeniedHandler; @Autowired AuthenticationEntryPointImpl authenticationEntryPoint; @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Bean public AuthenticationManager authenticationManagerBean() throws Exception { return authenticationConfiguration.getAuthenticationManager(); } /** * 配置Spring Security的过滤链。 * * @param http 用于构建安全配置的HttpSecurity对象。 * @return 返回配置好的SecurityFilterChain对象。 * @throws Exception 如果配置过程中发生错误,则抛出异常。 */ @Bean SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http // 禁用CSRF保护 .csrf(csrf -> csrf.disable()) // 设置会话创建策略为无状态 .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) // 配置授权规则 指定user/login路径.允许匿名访问(未登录可访问已登陆不能访问). 其他路径需要身份认证 .authorizeHttpRequests(auth -> auth.requestMatchers("/user/login").anonymous().anyRequest().authenticated()) //开启跨域访问 .cors(AbstractHttpConfigurer::disable) // 添加JWT认证过滤器 .addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class) // 配置异常处理 .exceptionHandling(exception -> exception.accessDeniedHandler(accessDeniedHandler).authenticationEntryPoint(authenticationEntryPoint)); // 构建并返回安全过滤链 return http.build(); }
注意事项
- 确保你的认证和授权失败处理器都正确实现了相应的接口,并且正确设置了它们的状态码和响应内容。
- 你可以将你的自定义处理器作为Spring Bean注入到你的配置中,而不是直接在配置方法中创建新的实例。这样可以更好地管理你的Spring应用程序中的bean。
- 在处理认证和授权失败时,你可能希望返回JSON而不是纯文本响应,特别是在构建RESTful API时。为此,你可以使用
@ResponseBody
注解或ResponseEntity
类来构建JSON响应。
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。