Spring Boot 统一功能处理(拦截器实现用户登录权限的统一校验、统一异常返回、统一数据格式返回)

这篇具有很好参考价值的文章主要介绍了Spring Boot 统一功能处理(拦截器实现用户登录权限的统一校验、统一异常返回、统一数据格式返回)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

1. 用户登录权限校验

1.1 最初用户登录权限效验

1.2 Spring AOP 用户统⼀登录验证

1.3 Spring 拦截器

(1)创建自定义拦截器

(2)将自定义拦截器添加到系统配置中,并设置拦截的规则

1.4 练习:登录拦截器

(1)实现 UserController 实体类

(2)返回的登录页面:login.html

(3)实现效果

 1.5 拦截器实现原理

(1)实现原理源码分析

1.6 统一访问前缀添加

(1)在系统的配置文件中设置

 (2)在 application.properies 中配置

2. 统一的异常处理

2.1 异常的统一封装

(1)创建一个类,并在类上标识:@ControllerAdvice

 (2)添加方法 @ExceptionHandler 来订阅异常

3. 统一数据返回格式

3.1 为什么要统一数据返回格式

3.2 统一数据返回格式的实现

(1)创建一个类,并添加 @ControllerAdvice

(2)实现 ResponseBodyAdvice 接口,并重写 supports 和 beforeBodyAdvice 方法

4. @ControllerAdvice 源码分析

(1) @ControllerAdvice 源码

(2)查看 initializingBean 有哪些实现类

 (3)查询 initControllerAdviceCache 方法


本节主要讲解Spring Boot 统一功能处理,同样也是 AOP 的实战环节,我们希望能够实现以下目标:

  1. 统一用户登陆权限验证
  2. 统一异常处理
  3. 统一数据格式返回

1. 用户登录权限校验

回顾一下最初用户登录验证的实现方法:

  1. 最初的用户登录校验版本:在每个方法中获取 Session 以及 Session 中的信息,对用户账号以及密码进行校验,正确则登录成功,反之则失败
  2. 第二版本:实现统一方法去校验是否登陆成功,在每个需要验证的方法中调用统一的用户登录身份效验方法来判断
  3. 第三版本:使用 Spring AOP 来进行用户统一登录校验
  4. 第四版本:使用 Spring 拦截器来实现用户的统一登录验证

1.1 最初用户登录权限效验

@RestController
@RequestMapping("/user")
public class UserController {

    @RequestMapping("/a1")
    public Boolean login (HttpServletRequest request) {
        // 有 Session 就获取,没有就不创建
        HttpSession session = request.getSession(false);
        if (session != null && session.getAttribute("userinfo") != null) {
            // 说明已经登录,进行业务处理
            return true;
        } else {
            // 未登录
            return false;
        }
    }

    @RequestMapping("/a2")
    public Boolean login2 (HttpServletRequest request) {
        // 有 Session 就获取,没有就不创建
        HttpSession session = request.getSession(false);
        if (session != null && session.getAttribute("userinfo") != null) {
            // 说明已经登录,进行业务处理
            return true;
        } else {
            // 未登录
            return false;
        }
    }
}

这种方式写的代码,每个方法中都有相同的用户登录验证权限,缺点是:

  •     每个方法中都要单独写用户登录验证的方法,即使封装成公共方法,也一样要传参调用和在方法中进行判断
  •     添加控制器越多,调用用户登录验证的方法也越多,这样就增加了后期的修改成功和维护成功
  •     这些用户登录验证的方法和现在要实现的业务几乎没有任何关联,但还是要在每个方法中都要写一遍,所以提供一个公共的 AOP 方法来进行统一的用户登录权限验证是非常好的解决办法。

1.2 Spring AOP 用户统⼀登录验证

统一用户登录验证,首先想到的实现方法是使用 Spring AOP 前置通知或环绕通知来实现:

@Aspect // 当前类是一个切面
@Component
public class UserAspect {
    // 定义切点方法 Controller 包下、子孙包下所有类的所有方法
    @Pointcut("execution(* com.example.springaop.controller..*.*(..))")
    public void  pointcut(){}
    
    // 前置通知
    @Before("pointcut()")
    public void doBefore() {}
    
    // 环绕通知
    @Around("pointcut()")
    public Object doAround(ProceedingJoinPoint joinPoint) {
        Object obj = null;
        System.out.println("Around 方法开始执行");
        try {
            obj = joinPoint.proceed();
        } catch (Throwable e) {
            e.printStackTrace();
        }
        System.out.println("Around 方法结束执行");
        return obj;
    }
}

但如果只在以上代码 Spring AOP 的切面中实现用户登录权限效验的功能,有这样两个问题:

  1.     没有办法得到 HttpSession 和 Request 对象
  2.     我们要对一部分方法进行拦截,而另一部分方法不拦截,比如注册方法和登录方法是不拦截的,也就是实际的拦截规则很复杂,使用简单的 aspectJ 表达式无法满足拦截的需求
     

1.3 Spring 拦截器

针对上面代码 Spring AOP 的问题,Spring 中提供了具体的实现拦截器:HandlerInterceptor,拦截器的实现有两步:

  1.     创建自定义拦截器,实现 Spring 中的 HandlerInterceptor 接口中的 preHandle方法
  2.     将自定义拦截器加入到框架的配置中,并且设置拦截规则

(1)创建自定义拦截器

//实现 HandlerInterceptor 接口
public class loginInterceptor implements HandlerInterceptor {
    /**
     * 返回 true 继续下序流程
     * false 表示验证失败
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 用户登录业务判断
        // false 表示当不存在 session 不存在时不需要创造一个会话信息
        HttpSession session = request.getSession(false);
        if (session != null && session.getAttribute("userinfo") != null){
            // 说明用户已经登录
            return true;
        }
        // 可以直接跳转到登录页面 或 返回一个 401、403 没有权限码
        response.sendRedirect("/login.html");
//        response.setStatus(401);
        return false;
    }
}

(2)将自定义拦截器添加到系统配置中,并设置拦截的规则

  • addPathPatterns:表示需要拦截的 URL,**表示拦截所有⽅法
  • excludePathPatterns:表示需要排除的 URL
@Configuration // 让随着spring启动而启动
public class AppConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new loginInterceptor())
                .addPathPatterns("/**")// 拦截所有请求
                .excludePathPatterns("/user/login")// 不拦截的 url 地址
                .excludePathPatterns("/user/reg")
                .excludePathPatterns("/**/*.html");
    }
}

1.4 练习:登录拦截器

实现愿望:

  1. 登录、注册页面不拦截,其余页面都拦截
  2. 等登陆成功写入 session 后,拦截页面可访问

(1)实现 UserController 实体类


@RestController
@RequestMapping("/user")
public class UserController {
    @RequestMapping("/getUser")
    public String getuser(){
        System.out.println("执行了 getUser !");
        return "get user";
    }

    @RequestMapping("/login")
    public String login(){
        System.out.println("执行了 login !");
        return "get login";
    }

    @RequestMapping("/reg")
    public String reg(){
        System.out.println("执行了 reg !");
        return "get reg";
    }
}

(2)返回的登录页面:login.html

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>登录页面</title>
</head>
<body>
<h1>登录页面</h1>
</body>
</html>

(3)实现效果

Spring Boot 统一功能处理(拦截器实现用户登录权限的统一校验、统一异常返回、统一数据格式返回),spring boot,后端,java

 

 1.5 拦截器实现原理

Spring Boot 统一功能处理(拦截器实现用户登录权限的统一校验、统一异常返回、统一数据格式返回),spring boot,后端,java

Spring Boot 统一功能处理(拦截器实现用户登录权限的统一校验、统一异常返回、统一数据格式返回),spring boot,后端,java 

(1)实现原理源码分析

  1. 所有的 Controller 执行都会通过一个调度器 DispatcherServlet 来实现 Spring Boot 统一功能处理(拦截器实现用户登录权限的统一校验、统一异常返回、统一数据格式返回),spring boot,后端,java
  2. 而所有方法都会执行 DispatcherServlet 中的 doDispatch 调度⽅法,doDispatch 源码分析如下:Spring Boot 统一功能处理(拦截器实现用户登录权限的统一校验、统一异常返回、统一数据格式返回),spring boot,后端,java

 通过源码分析,可以看出,Sping 中的拦截器也是通过动态代理和环绕通知的思想实现的

1.6 统一访问前缀添加

方法:

  1. 在系统的配置文件中设置
  2. 在 application.properies 中配置

(1)在系统的配置文件中设置

/**
 * 所有的接口添加 api 前缀
 * c 代表所有的请求(Controller)
 * 表示所有的地址都会加上这个前缀
 * @param configurer
 */
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
    configurer.addPathPrefix("api",c -> true);
}

现在我们去查看之前不被拦截的地址

Spring Boot 统一功能处理(拦截器实现用户登录权限的统一校验、统一异常返回、统一数据格式返回),spring boot,后端,java

 (2)在 application.properies 中配置

Spring Boot 统一功能处理(拦截器实现用户登录权限的统一校验、统一异常返回、统一数据格式返回),spring boot,后端,java

Spring Boot 统一功能处理(拦截器实现用户登录权限的统一校验、统一异常返回、统一数据格式返回),spring boot,后端,java 

2. 统一的异常处理

  1. 给当前的类上加 @ControllerAdvice 表示控制器通知类
  2. 给方法上添加 @ExceptionHandler(xxx.class),表示异常处理器,添加异常返回的业务代码

我们先去制造些异常:

Spring Boot 统一功能处理(拦截器实现用户登录权限的统一校验、统一异常返回、统一数据格式返回),spring boot,后端,java

Spring Boot 统一功能处理(拦截器实现用户登录权限的统一校验、统一异常返回、统一数据格式返回),spring boot,后端,java Spring Boot 统一功能处理(拦截器实现用户登录权限的统一校验、统一异常返回、统一数据格式返回),spring boot,后端,java

2.1 异常的统一封装

(1)创建一个类,并在类上标识:@ControllerAdvice

@ControllerAdvice
public class ExceptionHandler {
}

 (2)添加方法 @ExceptionHandler 来订阅异常

@ControllerAdvice
@ResponseBody// 表示当前的所有方法返回的都是数据不是页面
public class ExHandler {

    /**
     * 拦截所有的空指针异常,继续统一的数据返回
     */

    @ExceptionHandler(NullPointerException.class)// 空指针异常
    public HashMap<String,Object> nullException(NullPointerException e){
        HashMap<String,Object> result = new HashMap<>();
        result.put("code","-1");
        result.put("msg","空指针异常:" + e.getMessage());//错误码的描述信息
        result.put("date",null);
        return result;
    }

}

Spring Boot 统一功能处理(拦截器实现用户登录权限的统一校验、统一异常返回、统一数据格式返回),spring boot,后端,java

但是需要考虑的一点是,如果每个异常都这样写,那么工作量是非常大的,并且还有自定义异常,所以上面这样写肯定是不好的,既然是异常直接写 Exception 就好了,它是所有异常的父类,如果遇到不是前面写的两种异常,那么就会直接匹配到 Exception

当有多个异常通知时,匹配顺序为当前类及其⼦类向上依次匹配

@ControllerAdvice
@ResponseBody// 表示当前的所有方法返回的都是数据不是页面
public class ExHandler {

    /**
     * 拦截所有的空指针异常,继续统一的数据返回
     */

    @ExceptionHandler(NullPointerException.class)// 空指针异常
    public HashMap<String,Object> nullException(NullPointerException e){
        HashMap<String,Object> result = new HashMap<>();
        result.put("code","-1");
        result.put("msg","空指针异常:" + e.getMessage());//错误码的描述信息
        result.put("date",null);
        return result;
    }

    @ExceptionHandler(Exception.class)// 所有异常
    public HashMap<String,Object> AllException(NullPointerException e){
        HashMap<String,Object> result = new HashMap<>();
        result.put("code","-1");
        result.put("msg","异常:" + e.getMessage());//错误码的描述信息
        result.put("date",null);
        return result;
    }
}

 Spring Boot 统一功能处理(拦截器实现用户登录权限的统一校验、统一异常返回、统一数据格式返回),spring boot,后端,java

 

3. 统一数据返回格式

3.1 为什么要统一数据返回格式

  1. 方便前端程序员更好的接收和解析后端数据接口返回的数据。
  2. 降低前端程序员和后端程序员的沟通成本,按照某个格式实现就行了,因为所有接口都是这样返回的
  3. 有利于项目统一数据的维护和修改。
  4. 有利于后端技术部门的统一规范的标准制定,不会出现稀奇古怪的返回内容。

3.2 统一数据返回格式的实现

(1)创建一个类,并添加 @ControllerAdvice

@ControllerAdvice
public class ResponseAdvice {

}

(2)实现 ResponseBodyAdvice 接口,并重写 supports 和 beforeBodyAdvice 方法

@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {
    /**
     * 表示是否需要重写
     * 返回true则执行beforeBodyWrite方法,反之则不执行
     */
    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        return true;
    }

    @Override
    public Object beforeBodyWrite(Object body,
                                  MethodParameter returnType,
                                  MediaType selectedContentType,
                                  Class selectedConverterType,
                                  ServerHttpRequest request,
                                  ServerHttpResponse response) {
        HashMap<String,Object> hashMap = new HashMap<>();
        hashMap.put("code",200);// 状态码
        hashMap.put("msg","");// 错误的描述信息
        hashMap.put("date",body);
        return hashMap;
    }
}

supports方法相当于是一个开关,只有当 true 时才能执行重写 beforeBodyWrite 方法,false就不重写

Spring Boot 统一功能处理(拦截器实现用户登录权限的统一校验、统一异常返回、统一数据格式返回),spring boot,后端,java

当访问 getUser 时发生异常了,类型访问异常 

Spring Boot 统一功能处理(拦截器实现用户登录权限的统一校验、统一异常返回、统一数据格式返回),spring boot,后端,java

注意:

我们知道String既不属于基本数据类型,又不属于对象且在重写方法的时候其余类型都是用的统一的格式化工具,而String用的是它自身的格式化工具,String自身的格式化工具在执行的时候还没有加载好,就会导致 原始类型 是String的时候,在转化成HashMap的时候就会报错

所以在统一返回的时候需要对String进行单独的处理

 Spring Boot 统一功能处理(拦截器实现用户登录权限的统一校验、统一异常返回、统一数据格式返回),spring boot,后端,java

jackson就是用于 json 数据转换的,json的转换工具 

@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
    HashMap<String,Object> hashMap = new HashMap<>();
    hashMap.put("code",200);// 状态码
    hashMap.put("msg","");// 错误的描述信息
    hashMap.put("date",body);
    if (body instanceof String){
        // 判断数据类型是不是 String,是String需要特殊处理,因为 String 在转换的时候会报错
        try {
            return objectMapper.writeValueAsString(hashMap);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
    }
    return hashMap;
}

Spring Boot 统一功能处理(拦截器实现用户登录权限的统一校验、统一异常返回、统一数据格式返回),spring boot,后端,java

4. @ControllerAdvice 源码分析

通过对 @ControllerAdvice 源码的分析我们可以知道上面统一异常和统一数据返回的执行流程

(1) @ControllerAdvice 源码

Spring Boot 统一功能处理(拦截器实现用户登录权限的统一校验、统一异常返回、统一数据格式返回),spring boot,后端,java

可以看到 @ControllerAdvice 派生于 @Component 组件而所有组件初始化都会调用 InitializingBean 接口

(2)查看 initializingBean 有哪些实现类

在查询过程中发现,其中 Spring MVC 中的实现子类是 RequestMappingHandlerAdapter,它里面有一个方法 afterPropertiesSet()方法,表示所有的参数设置完成之后执行的方法

Spring Boot 统一功能处理(拦截器实现用户登录权限的统一校验、统一异常返回、统一数据格式返回),spring boot,后端,java

 (3)查询 initControllerAdviceCache 方法

Spring Boot 统一功能处理(拦截器实现用户登录权限的统一校验、统一异常返回、统一数据格式返回),spring boot,后端,java

发现这个方法在执行时会查找使用所有的 @ControllerAdvice 类,发送某个事件时,调用相应的 Advice 方法,比如返回数据前调用统一数据封装,比如发生异常是调用异常的 Advice 方法实现的文章来源地址https://www.toymoban.com/news/detail-653971.html

到了这里,关于Spring Boot 统一功能处理(拦截器实现用户登录权限的统一校验、统一异常返回、统一数据格式返回)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • SpringBoot统一功能处理(拦截器)

    1.1自定义拦截器 写一个类去实现 HandlerInterceptor接口 表示当前类是一个拦截器,再 重写HandlerInterceptor接口中的方法 , preHandle 为在方法 执行前拦截 ,postHandle为方法执行中拦截,afterCompletion为方法执行中拦截.需要在什么时候拦截就重写什么方法 2.1.配置拦截规则 实现 WebMvcConfigur

    2024年02月14日
    浏览(33)
  • 使用spring boot拦截器实现青少年模式

    便用Spring Boot拦截器采累计在线时间采实现青少年模式的步骤,可以参考以卜步骤: 1.创建一个拦截器类,实现Handlerlnterceptor 接口。 2.在拦截器类中,定义一个变量来记录用户在线时间。 3.在preHandle方法中,记录用户的登录时间。 4.在afterCompletion方法中,计算用户在线时间,

    2023年04月08日
    浏览(36)
  • springmvc统一异常处理拦截器

    使用@RestControllerAdvice+@ExceptionHandler实现 也可以使用@ControllerAdvice+@ResponseBody+@ExceptionHandler实现 创建一个异常处理的类,放在config包下  组件类:  也可以让不同的异常返回不同的结果,捕获什么异常由@ExceptionHandler的value属性决定,传入一个类对象(可以通过反射获得)  

    2024年02月15日
    浏览(36)
  • Spring Boot 3自定义注解+拦截器+Redis实现高并发接口限流

    在当今互联网应用开发中,高并发访问是一个常见的挑战。为了保障系统的稳定性和可靠性,我们需要对接口进行限流,防止因过多的请求导致系统崩溃。 本文将介绍如何利用Spring Boot 3中的自定义注解、拦截器和Redis实现高并发接口限流,帮助程序员解决这一挑战。 1. 自定

    2024年04月28日
    浏览(41)
  • spring boot3token拦截器链的设计与实现

      ⛰️个人主页:     蒾酒 🔥系列专栏:《spring boot实战》 🌊山高路远,行路漫漫,终有归途。 目录 写在前面 流程分析 需要清楚的 实现步骤 1.定义拦截器 2.创建拦截器链配置类 3.配置拦截器链顺序 4.配置拦截排除项 最后 本文介绍了spring boot后端服务开发中有关如何设计

    2024年03月11日
    浏览(42)
  • 【SpringMVC】统一异常处理 前后台协议联调 拦截器

    1. 问题描述 在讲解这一部分知识点之前,我们先来演示个效果,修改BookController类的 getById 方法 重新启动运行项目,使用PostMan发送请求,当传入的id为1,则会出现如下效果: 前端接收到这个信息后和之前我们约定的格式不一致,这个问题该如何解决? 在解决问题之前,我们

    2024年02月11日
    浏览(43)
  • 【Spring Boot系列】- Spring Boot拦截器

    拦截器(Interceptor)是在面向切面编程中应用的,就是在service或者一个方法前调用一个方法,或者在方法后调用一个方法。是基于JAVA的反射机制。可以根据 URL 对请求进行拦截,主要应用于登陆校验、权限验证、乱码解决、性能监控和异常处理等功能。 在 Spring Boot 项目中,

    2024年02月13日
    浏览(33)
  • spring boot 拦截器例子

    在Spring Boot中,拦截器是通过实现`HandlerInterceptor`接口来实现的。它允许你在请求到达控制器方法之前和之后执行自定义的逻辑。下面我将为你提供一个简单的Spring Boot拦截器的例子。 假设我们有一个简单的控制器类`UserController`,其中有两个请求处理方法:`getUser`和`saveUser`,

    2024年02月15日
    浏览(32)
  • Spring Boot 配置拦截器

    通过拦截器,我们可以针对特定 URI 做拦截,做相关业务处理,比如检查用户是否登录,打印每个请求的处理耗时等。 新建登录验证类  LoginValidationInterceptor.java : 定义一个拦截器类后,您需要实现  HandlerInterceptor  接口,其有三个方法可以重写: preHandle : 在调用 Controller 方

    2024年02月08日
    浏览(51)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包