首页 > 其他分享 >【转载】【SSM】SpringBoot 统一功能处理,(*Spring 拦截器实现与原理)

【转载】【SSM】SpringBoot 统一功能处理,(*Spring 拦截器实现与原理)

时间:2023-01-31 14:57:35浏览次数:80  
标签:拦截器 SpringBoot 登录 Spring result null 方法 public

✨1.用户登录权限效验

1.1 最初用户登录验证

1.2 Spring AOP 用户统一登录验证的问题

1.3 Spring 拦截器

1. 自定义拦截器

2. 将自定义拦截器加入到系统配置

1.4 拦截器实现原理

1. 实现原理源码分析

2. 拦截器小结

✨2. 统一异常处理

✨3. 统一数据返回格式

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

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


✨1.用户登录权限效验

用户登录权限的发展从之前每个方法中自己验证用户登录权限,到现在统一的用户登录验证处理,它是一个逐渐完善和逐渐优化的过程。

1.1 最初用户登录验证

最初用户登录验证的实现方法:

@RestController
public class IndexController {
    @RequestMapping("/method1")
    public String method1(HttpServletRequest request){
        User user = null;
        HttpSession session = request.getSession();
        if(session != null){
            user = (User) session.getAttribute("currentUser");
        }
        if(user == null){
            return "redirect:/login.html";
        }
        return "redirect:/";
    }
    @RequestMapping("/method2")
    public String method2(HttpServletRequest request){
        User user = null;
        HttpSession session = request.getSession();
        if(session != null){
            user = (User) session.getAttribute("currentUser");
        }
        if(user == null){
            return "redirect:/login.html";
        }
        return "redirect:/";
    }
}

每个方法中都有相同的用户登录验证权限,它的缺点是:

  1. 每个方法中都要单独写用户登录验证的方法,即使封装成公共方法,也一样要传参调用和在方法中 进行判断。
  2. 添加控制器越多,调用用户登录验证的方法也越多,这样就增加了后期的修改成本和维护成本。
  3. 这些用户登录验证的方法和接下来要实现的业务几何没有任何关联,但每个方法中都要写一遍。 所以提供一个公共的 AOP 方法来进行统一的用户登录权限验证迫在眉睫。

1.2 Spring AOP 用户统一登录验证的问题

我们想到的另一个实现方案是 Spring AOP 前置通知或环绕通知来实现,具体实现代码如下:

@Aspect
@Component
public class UserAspect {

    @Pointcut("execution(* com.example.demo.controller..*.*(..))")
    public void pointCut(){}
    
     // 前置方法
    @Before("pointcut()")
        public void doBefore(){
    }
    
    // 环绕方法
    @Around("pointCut()")
    public Object doAround(ProceedingJoinPoint joinPoint){
        User user = null;
        System.out.println("Around 方法开始执行");
        try {
            // 执行拦截方法
            user = (User) joinPoint.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        System.out.println("Around 方法结束执行");
        return user;
    }
}

如果要在 Spring AOP 的切面中实现用户登录权限效验的功能,有以下两个问题:

  1. 没办法获取到 HttpSession 对象。
  2. 我们要对一部分方法进行拦截,而另一部分方法不拦截,如注册方法和登录方法是不拦截的,这样 的话排除方法的规则很难定义,甚至没办法定义。 

1.3 Spring 拦截器

对于以上问题 Spring 中提供了具体的实现拦截器:HandlerInterceptor,拦截器的实现分为以下两个步骤:

  1. 创建自定义拦截器,实现 HandlerInterceptor 接口的 preHandle(执行具体方法之前的预处理) 方法。
  2. 将自定义拦截器加入 WebMvcConfigurer 的 addInterceptors 方法中。

1. 自定义拦截器

接下来使用代码来实现一个用户登录的权限效验,自定义拦截器是一个普通类,具体实现代码如下: 

@Slf4j
@Component  // 拦截器,如果用户未登录,则直接重定向到登录页,不会走到后续操作
public class MyInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        log.info("MyInterceptor.preHandle()");
        User currentUser = null;
        HttpSession session = request.getSession();
        if(session != null){
            currentUser = (User)session.getAttribute("currentUser");
        }
        if(currentUser == null){
            // 说明用户未登录
            response.sendRedirect("/login.html");
            return false;
        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        log.info("MyInterceptor.postHandle()");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        log.info("MyInterceptor.afterCompletion()");
    }
}

2. 将自定义拦截器加入到系统配置

将上一步中的自定义拦截器加入到系统配置信息中,具体实现代码如下:

// 1. 必须是一个 Spring bean(否则没有机会调用)
// 2. 必须实现了 WebMvcConfigurer 接口
@Configuration
public class WebConfig implements WebMvcConfigurer {
    private final MyInterceptor myInterceptor;

    @Autowired
    public WebConfig(MyInterceptor myInterceptor) {
        this.myInterceptor = myInterceptor;
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 可以添加多个拦截器,先添加的拦截器,先被应用
        registry.addInterceptor(myInterceptor)
                .addPathPatterns("/**")               // 应用到所有 URL 上
                .excludePathPatterns("/error")        // 只要有错误,都会到这
                .excludePathPatterns("/login.html")   // 但不包括login.html
                .excludePathPatterns("/login.do");    // 但不包括login.do
    }
}
  • addPathPatterns:表示需要拦截的 URL,“**”表示拦截任意方法(也就是所有方法)。
  • excludePathPatterns:表示需要排除的 URL。

说明:以上拦截规则可以拦截此项目中的使用 URL,包括静态文件(图片文件、JS 和 CSS 等文件)。

1.4 拦截器实现原理

正常情况下的调用顺序:

 

 

然而有了拦截器之后,会在调用 Controller 之前进行相应的业务处理,执行的流程如下图所示:

 

 

 

1. 实现原理源码分析

  1. 所有的 Controller 执行都会通过一个调度器 DispatcherServlet 来实现 。
  2. 在开始执行 Controller 之前,会先调用 预处理方法 applyPreHandle。
  3. 在 applyPreHandle 中会获取所有的拦截器 HandlerInterceptor 并执行拦截器中 的 preHandle 方法。

2. 拦截器小结

通过上面的源码分析,我们可以看出,Spring 中的拦截器也是通过动态代理和环绕通知的思想实现的, 大体的调用流程如下:

 

 


✨2. 统一异常处理

统一异常处理使用的是 @ControllerAdvice + @ExceptionHandler 来实现的

  • @ControllerAdvice 表示 控制器通知类
  • @ExceptionHandler 是异常处理器

两个结合表示当出现异常的时候执行某个通知,也 就是执行某个方法事件,具体实现代码如下: 

@ControllerAdvice
@ResponseBody
public class ExceptionAdvice {
    @ExceptionHandler(Exception.class)
    public Object exceptionAdvice(Exception e){
        HashMap<String, Object> result = new HashMap<>();
        result.put("success", -1);
        result.put("message", "总的异常信息:" + e.getMessage());
        result.put("data", null);
        return result;
    }
    @ExceptionHandler(NullPointerException.class)
    public Object NullPointerExceptionAdvice(NullPointerException e) {
        HashMap<String, Object> result = new HashMap<>();
        result.put("success", -1);
        result.put("message", "空指针异常:" + e.getMessage());
        result.put("data", null);
        return result;
    }
}
  • 如果出现了异常就返回给前端一个 HashMap 的对象,其中包含的字段如代码中定义的那样。
  • 我们可以针对不同的异常,返回不同的结果。
  • 当有多个异常通知时,匹配顺序为当前类及其子类向上依次匹配。

✨3. 统一数据返回格式

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

统一数据返回格式的优点有很多,比如以下几个:

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

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

统一的数据返回格式可以使用 @ControllerAdvice + ResponseBodyAdvice 的方式实现,具体实现代码如下: 

@ControllerAdvice
public class ResBodyAdvice implements ResponseBodyAdvice {
    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        // 内容是否需要重写(通过此方法可以选择性部分控制器和方法进行重写)
        // 返回 true 表示重写
        return false;
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        // 构造统一返回对象
        HashMap<String, Object> result = new HashMap<>();
        result.put("success", 1);
        result.put("message", "");
        result.put("data", body);
        return result;
    }
}

转载:https://www.lmlphp.com/user/610796/article/item/10630125

标签:拦截器,SpringBoot,登录,Spring,result,null,方法,public
From: https://www.cnblogs.com/51python/p/17078941.html

相关文章

  • 【转载】springboot上传+下载文件实例
     前言一次“上传文件”的点击蕴含着一轮请求,我们要做的就是针对每一次的请求进行i/o处理,并返回给前端用户 一.文件上传文件上传,也称为upload,是指将本地图片、视频......
  • 【转载】 spring 利用注解类添加日志到mysql
    一、前言我们写完一个项目,运维时,如果出现了bug,我们需要查看控制台的日志,但是那个日志无关方法太多,查找不是很方便,还有就是一个项目上线之后,我们需要记录谁操作了那些功能,......
  • SpringBoot集成webService
    我这里用的cxf框架来搭建webService服务我的SpringBoot版本为2.7.5查看项目springBoot版本的方法:1.写测试类,打印springBoot版本@Testvoidversion(){System.out.pr......
  • 扒一扒Bean注入到Spring的那些姿势,你会几种?
    大家好,我是三友~~这篇文章我准备来扒一扒Bean注入到Spring的那些姿势。其实关于Bean注入Spring容器的方式网上也有很多相关文章,但是很多文章可能会存在以下常见的问题......
  • 基于Spring Cache实现Caffeine、jimDB多级缓存实战
    作者:京东零售王震背景在早期参与涅槃氛围标签中台项目中,前台要求接口性能999要求50ms以下,通过设计Caffeine、ehcache堆外缓存、jimDB三级缓存,利用内存、堆外、jimDB缓存不......
  • 前端VUE+后端springboot实现导入返回excel校验结果demo
    vue代码<!--菜单导入对话框--><el-dialog:title="upload.title":visible.sync="upload.open":close-on-click-modal="false"width="400px"append-to-body>......
  • springboot 统一日志记录 - AOP日志
    参考学习:https://www.bilibili.com/video/BV1bf4y187KX/三步:1.使用日志注解。2.编写日志注解类。3.编写日志注解类的AOP实现。1.在需要记日志处,使用自定义的注解。pac......
  • 将spring boot项目打包成jar包
    在springboot项目的pom文件中加入<packaging>jar</packaging>生成jar包文件右键OpenInExplorer找到文件所在位置可以关闭idea了在文件所在目录下打开cmd,打开命......
  • 基于JAVA springboot+mybatis智慧生活分享平台设计和实现
    基于JAVAspringboot+mybatis智慧生活分享平台设计和实现文章目录​​基于JAVAspringboot+mybatis智慧生活分享平台设计和实现​​​​主要功能模块设计:​​​​系统前端......
  • 基于Java+SpringBoot+vue+element实现新冠疫情物资管理系统详细设计
    基于Java+SpringBoot+vue+element实现新冠疫情物资管理系统详细设计文章目录​​基于Java+SpringBoot+vue+element实现新冠疫情物资管理系统详细设计​​​​前言介绍:​​​......