Spring Security 实战内容:自定义异常处理

在这里插入图片描述

1. 前言

今天正好项目中 Spring Security 需要对认证授权异常的处理,就分享出来吧 。

2. Spring Security 中的异常

Spring Security 中的异常主要分为两大类:一类是认证异常,另一类是授权相关的异常。

2.1 AuthenticationException

AuthenticationException 是在用户认证的时候出现错误时抛出的异常。主要的子类如图:

[图片上传失败...(image-cc6182-1649933938789)]

根据该图的信息,系统用户不存在,被锁定,凭证失效,密码错误等认证过程中出现的异常都由 AuthenticationException 处理。

2.2 AccessDeniedException

AccessDeniedException 主要是在用户在访问受保护资源时被拒绝而抛出的异常。同 AuthenticationException 一样它也提供了一些具体的子类。如下图:

[图片上传失败...(image-f8a717-1649933938789)]

AccessDeniedException 的子类比较少,主要是 CSRF 相关的异常和授权服务异常。

3. Http 状态对认证授权的规定

Http 协议对认证授权的响应结果也有规定。

3.1 401 未授权状态

HTTP 401 错误 - 未授权(Unauthorized) 一般来说该错误消息表明您首先需要登录(输入有效的用户名和密码)。 如果你刚刚输入这些信息,立刻就看到一个 401 错误,就意味着,无论出于何种原因您的用户名和密码其中之一或两者都无效(输入有误,用户名暂时停用,账户被锁定,凭证失效等) 。总之就是认证失败了。其实正好对应我们上面的 AuthenticationException

3.2 403 被拒绝状态

HTTP 403 错误 - 被禁止(Forbidden) 出现该错误表明您在访问受限资源时没有得到许可。服务器理解了本次请求但是拒绝执行该任务,该请求不该重发给服务器。并且服务器想让客户端知道为什么没有权限访问特定的资源,服务器应该在返回的信息中描述拒绝的理由。一般实践中我们会比较模糊的表明原因。 该错误对应了我们上面的 AccessDeniedException

4. Spring Security 中的异常处理

我们在 Spring Security 提到 HttpSecurity 提供的 exceptionHandling() 方法用来提供异常处理。该方法构造出 ExceptionHandlingConfigurer 异常处理配置类。该配置类提供了两个实用接口:

  • AuthenticationEntryPoint 该类用来统一处理 AuthenticationException 异常
  • AccessDeniedHandler 该类用来统一处理 AccessDeniedException 异常

我们只要实现并配置这两个异常处理类即可实现对 Spring Security 认证授权相关的异常进行统一的自定义处理。

4.1 实现 AuthenticationEntryPoint

json 信息响应。

 import com.fasterxml.jackson.databind.ObjectMapper;
 import org.springframework.http.MediaType;
 import org.springframework.security.core.AuthenticationException;
 import org.springframework.security.web.AuthenticationEntryPoint;

 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.HashMap;

 /**
  */
 public class SimpleAuthenticationEntryPoint implements AuthenticationEntryPoint {
     @Override
     public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {

         //todo your business
         HashMap<String, String> map = new HashMap<>(2);
         map.put("uri", request.getRequestURI());
         map.put("msg", "认证失败");
         response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
         response.setCharacterEncoding("utf-8");
         response.setContentType(MediaType.APPLICATION_JSON_VALUE);
         ObjectMapper objectMapper = new ObjectMapper();
         String resBody = objectMapper.writeValueAsString(map);
         PrintWriter printWriter = response.getWriter();
         printWriter.print(resBody);
         printWriter.flush();
         printWriter.close();
     }
 }

4.2 实现 AccessDeniedHandler

同样以 json 信息响应。

 import com.fasterxml.jackson.databind.ObjectMapper;
 import org.springframework.http.MediaType;
 import org.springframework.security.access.AccessDeniedException;
 import org.springframework.security.web.access.AccessDeniedHandler;

 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.HashMap;

 /**
  */
 public class SimpleAccessDeniedHandler implements AccessDeniedHandler {
     @Override
     public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
         //todo your business
         HashMap<String, String> map = new HashMap<>(2);
         map.put("uri", request.getRequestURI());
         map.put("msg", "认证失败");
         response.setStatus(HttpServletResponse.SC_FORBIDDEN);
         response.setCharacterEncoding("utf-8");
         response.setContentType(MediaType.APPLICATION_JSON_VALUE);
         ObjectMapper objectMapper = new ObjectMapper();
         String resBody = objectMapper.writeValueAsString(map);
         PrintWriter printWriter = response.getWriter();
         printWriter.print(resBody);
         printWriter.flush();
         printWriter.close();
     }
 }

4.3 个人实践建议

其实我个人建议 Http 状态码 都返回 200 而将 401 状态在 元信息 Map 中返回。因为异常状态码在浏览器端会以 error 显示。我们只要能捕捉到 401403 就能认定是认证问题还是授权问题。

4.4 配置

实现了上述两个接口后,我们只需要在 WebSecurityConfigurerAdapterconfigure(HttpSecurity http) 方法中配置即可。相关的配置片段如下:

 http.exceptionHandling().accessDeniedHandler(new SimpleAccessDeniedHandler()).authenticationEntryPoint(new SimpleAuthenticationEntryPoint())

5. 总结

今天我们对 Spring Security 中的异常处理进行了讲解。分别实现了自定义的认证异常处理和自定义的授权异常处理。

愿与诸君共进步,大量的面试题及答案还有资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化、分布式架构等这些成为架构师必备的知识体系
可以进交流群539413949获取,最后祝大家都能拿到自己心仪的offer

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Security中,可以通过自定义异常处理登录过程中的异常情况。以下是实现自定义异常的步骤: 1. 创建一个自定义异常类,继承自`AuthenticationException`或其子类。例如,可以创建一个名为`CustomAuthenticationException`的异常类。 ```java public class CustomAuthenticationException extends AuthenticationException { public CustomAuthenticationException(String msg) { super(msg); } } ``` 2. 创建一个自定义的认证提供者(AuthenticationProvider)或认证过滤器(AuthenticationFilter)。 - 如果你选择创建认证提供者,需要实现`AuthenticationProvider`接口,并在`authenticate()`方法中抛出自定义异常。 ```java public class CustomAuthenticationProvider implements AuthenticationProvider { @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { // 自定义认证逻辑 if (认证失败) { throw new CustomAuthenticationException("自定义异常信息"); } // 认证成功则返回一个认证对象 return new UsernamePasswordAuthenticationToken(authentication.getPrincipal(), authentication.getCredentials(), authentication.getAuthorities()); } @Override public boolean supports(Class<?> authentication) { return authentication.equals(UsernamePasswordAuthenticationToken.class); } } ``` - 如果你选择创建认证过滤器,需要继承`AbstractAuthenticationProcessingFilter`类,并在`attemptAuthentication()`方法中抛出自定义异常。 ```java public class CustomAuthenticationFilter extends AbstractAuthenticationProcessingFilter { public CustomAuthenticationFilter() { super(new AntPathRequestMatcher("/login", "POST")); } @Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException { // 自定义认证逻辑 if (认证失败) { throw new CustomAuthenticationException("自定义异常信息"); } // 认证成功则返回一个认证对象 return getAuthenticationManager().authenticate(new UsernamePasswordAuthenticationToken(username, password)); } } ``` 3. 在Spring Security配置类中,将自定义的认证提供者或认证过滤器添加到认证流程中。 ```java @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private CustomAuthenticationProvider customAuthenticationProvider; @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.authenticationProvider(customAuthenticationProvider); } } ``` 这样,当登录过程中发生自定义异常时,Spring Security会捕获并处理异常,你可以在登录失败的处理逻辑中进行相应的操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值