当前位置: 首页 > news >正文

【Spring Boot 快速入门】八、登录认证(二)统一拦截

目录

  • 统一拦截
    • Filter 过滤器
      • 快速入门
      • 详解
      • Filter 进行登录校验
    • Interceptor 拦截器
      • 快速入门
      • 详解
      • Interceptor 进行登录校验
  • 全局异常处理

统一拦截

Filter 过滤器

快速入门

Filter 是 Java Web 三大组件之一,另外两个是 Servlet 和 Listener(监听器),目前 Filter 使用较多。

过滤器(Filter)可拦截对资源的请求,执行特殊逻辑操作后再放行,能完成登录校验、统一编码处理、敏感字符处理等通用性操作,避免重复编写相同逻辑。

Filter 使用步骤:

  1. 定义 Filter:定义一个类,实现 FIlter 接口,并重写其所有方法
  2. 配置 Filter:Filter 类上加 @WebFilter 注解,配置拦截资源的路径。引导类上加 @ServletComponentScan 开启 Servlet 组件支持
import jakarta.servlet.*;
import jakarta.servlet.annotation.WebFilter;import java.io.IOException;@WebFilter(urlPatterns = "/*")
public class DemoFilter implements Filter {@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {System.out.println("DemoFilter doFilter");chain.doFilter(request, response);}@Overridepublic void init(FilterConfig filterConfig) throws ServletException {System.out.println("DemoFilter init");}@Overridepublic void destroy() {System.out.println("DemoFilter destroy");}
}

init 方法在 web 服务器启动时创建 Filter 对象后调用,仅一次,用于资源和环境准备;destroy 方法在关闭服务器时调用,仅一次,用于资源释放和环境清理;doFilter 方法在每次拦截到请求时调用,是核心方法。

详解

过滤器的执行流程:拦截到请求后,先执行放行前的逻辑(写在调用 doFilter 方法之前),然后调用 FilterChain 对象的 doFilter 方法执行放行,访问对应的 web 资源,之后会回到过滤器执行放行后的逻辑(写在 doFilter 方法之后),最后给浏览器响应数据。

过滤器拦截路径的配置方式常见的有三种:

  • 拦截具体路径(如拦截 /log,只拦截该路径请求)
  • 目录拦截(如拦截 /dpts/,拦截以 /dpts 开头的路径请求)
  • 拦截所有(用 /* 表示)

在 web 应用中配置多个过滤器形成过滤器链。执行时按顺序依次执行每个过滤器的放行前逻辑,全部放行后访问 web 资源;web 资源访问完毕后,按相反顺序执行各过滤器放行后的逻辑,最后响应浏览器。

过滤器执行优先级的决定因素:以注解方式配置的过滤器,其执行优先级由类名的自然排序决定,类名越靠前,优先级越高。通过修改类名可改变执行顺序。

Filter 进行登录校验

登录校验基本流程:访问后台管理系统需先登录,登录成功后服务端生成 JWT 令牌返回给前端,前端后续每次请求都会携带该令牌,服务端通过过滤器校验令牌有效性,有效则放行,无效则返回错误信息。

登录校验过滤器实现思路:拦截所有请求后,先判断是否为登录请求,是则直接放行;不是则获取请求头中的 JWT 令牌,判断其是否存在,不存在则返回未登录结果;存在则解析令牌,解析失败返回未登录结果,成功则放行。

在这里插入图片描述

Filter 过滤器完整实现代码:

import com.alibaba.fastjson.JSONObject;
import com.example.demo.responed.Result;
import com.example.demo.utils.JwtUtils;
import jakarta.servlet.*;
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.util.StringUtils;import java.io.IOException;
import lombok.extern.slf4j.Slf4j;@Slf4j
@WebFilter(urlPatterns = "/*")
public class LoginFilter implements Filter {@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {HttpServletRequest req = (HttpServletRequest) request;HttpServletResponse resp = (HttpServletResponse) response;// 1.获取请求urlString url = req.getRequestURL().toString();// 2.判断请求url是否包含login,如果包含,放行if(url.contains("login")){chain.doFilter(request, response);return;}// 3.获取请求头中的令牌(token)String jwt = req.getHeader("token");// 4.判断令牌是否存在,如果不存在,返回错误结果(未登录)if(!StringUtils.hasLength(jwt)){Result error = Result.error("NOT_LOGIN");String notLogin = JSONObject.toJSONString( error);resp.getWriter().write(notLogin);return;}// 5.如果令牌存在,需要查询数据库,查询用户是否存在try {JwtUtils.parseJwt(jwt);} catch (Exception e) {e.printStackTrace();log.info("解析令牌失败");Result error = Result.error("未登录");String notLogin = JSONObject.toJSONString( error);resp.getWriter().write(notLogin);return;}// 6.放行chain.doFilter(request, response);}
}

需要在 pom.xml 文件中引入 fastjson 工具包将结果转为 json 字符串并响应:

<dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.83</version>
</dependency>

当未登录(没有 JWT 令牌)时,显示 NOT_LOGIN:

在这里插入图片描述

而当登录后(携带 JWT 令牌),显示出数据:

在这里插入图片描述

Interceptor 拦截器

快速入门

拦截器是 Spring 框架提供的动态拦截控制器方法执行的机制,类似过滤器,可在指定方法运行前后执行预先定义的代码,常用于登录校验等通用性操作,如校验用户是否携带合法的 gwt 令牌,决定是否放行或响应未登录信息。

Interceptor 使用步骤:

  1. 定义拦截器需创建类实现 handler interceptor 接口,重写其中的三个方法

    import jakarta.servlet.http.HttpServletRequest;
    import jakarta.servlet.http.HttpServletResponse;
    import org.springframework.stereotype.Component;
    import org.springframework.web.servlet.HandlerInterceptor;
    import org.springframework.web.servlet.ModelAndView;@Component
    public class LoginCheckInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println("preHandle");return true;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {System.out.println("postHandle");}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {System.out.println("afterCompletion");}
    }
    
  2. 注册拦截器

    import com.example.demo.interceptor.LoginCheckInterceptor;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration
    public class WebConfig implements WebMvcConfigurer {@Autowiredprivate LoginCheckInterceptor loginCheckInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(loginCheckInterceptor).addPathPatterns("/**") // 拦截所有请求.excludePathPatterns("/login"); // 登录接口不拦截WebMvcConfigurer.super.addInterceptors(registry);}
    }
    

详解

拦截器拦截路径配置:

拦截路径配置含义示例匹配情况
/*仅匹配一级路径- 可匹配:/depts/emps/log - 不可匹配:/depts/1(两级路径)
/**匹配任意级路径- 可匹配:/depts(一级)、/depts/1(两级)、/depts/1/2(三级)
/depts/*匹配以/depts开头的一级子路径- 可匹配:/depts/1 - 不可匹配:/depts/1/2(两级子路径)、/emps(非/depts前缀)
/depts/**匹配以/depts开头的任意级子路径- 可匹配:/depts/1/depts/1/2 - 不可匹配:/emps(非/depts前缀)

拦截器执行流程:浏览器访问 web 应用时,过滤器先拦截请求,执行放行前逻辑并放行,请求进入 Spring 环境后经前端控制器 DispatcherServlet 转发,拦截器在执行 Controller 接口方法前拦截,先执行 preHandle 方法,返回 true 则放行,Controller 方法执行后执行 postHandle 和 afterCompletion方法,最后执行过滤器放行后逻辑并响应浏览器。

拦截器与过滤器的区别:

对比维度过滤器(Filter)拦截器(Interceptor)
接口规范实现javax.servlet.Filter接口实现org.springframework.web.servlet.HandlerInterceptor接口
拦截范围拦截所有资源(包括静态资源、非 Spring 管理的请求等)仅拦截进入 Spring 环境的资源(如 Controller 接口请求)
执行时机在请求进入 Servlet 容器后、Spring 处理前执行在 Spring 的DispatcherServlet转发请求后、Controller 方法执行前后执行

Interceptor 进行登录校验

Interceptor 拦截器与 Filter 过滤器的基本流程和实现思路一致,只有代码上的细微区分:

将 LoginCheckInterceptor 改为以下:

import com.alibaba.fastjson.JSONObject;
import com.example.demo.responed.Result;
import com.example.demo.utils.JwtUtils;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;@Component
@Slf4j
public class LoginCheckInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 1.获取请求urlString url = request.getRequestURL().toString();// 2.判断请求url是否包含login,如果包含,放行if(url.contains("login")){return true;}// 3.获取请求头中的令牌(token)String jwt = request.getHeader("token");// 4.判断令牌是否存在,如果不存在,返回错误结果(未登录)if(!StringUtils.hasLength(jwt)){Result error = Result.error("NOT_LOGIN");String notLogin = JSONObject.toJSONString( error);response.getWriter().write(notLogin);return false;}// 5.如果令牌存在,需要查询数据库,查询用户是否存在try {JwtUtils.parseJwt(jwt);} catch (Exception e) {e.printStackTrace();log.info("解析令牌失败");Result error = Result.error("未登录");String notLogin = JSONObject.toJSONString( error);response.getWriter().write(notLogin);return false;}// 6.放行return true;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {System.out.println("postHandle");}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {System.out.println("afterCompletion");}
}

全局异常处理

目前案例中未对异常做任何处理,在三层架构调用(control 调用 service,service 调用 map)过程中,异常会逐层上抛,最终由框架返回不符合规范的错误信息 JSON 数据。

异常处理有两种方案,一是在 control 的每个方法中进行 try catch 捕获异常,但该方案操作繁琐、代码臃肿,不推荐;二是定义全局异常处理器,可捕获整个项目中的所有异常,更简单优雅,是项目推荐方式。

定义一个类,在类上添加@RestControllerAdvice注解,代表这是全局异常处理器。在类中定义方法,方法上添加@ExceptionHandler注解,通过其 value 属性指定要捕获的异常类型(如Exception.class代表捕获所有异常)。方法中可输出异常堆栈信息,并返回符合规范的Result对象封装错误信息。@RestControllerAdvice等同于@ControllerAdvice@ResponseBody,能将方法返回值转为 JSON 响应给前端

import com.example.demo.responed.Result;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;@RestControllerAdvice
public class GlobalExceptionHandler {@ExceptionHandler(Exception.class)public Result error(Exception e) {e.printStackTrace();return Result.error("操作失败");}
}

这样就完成了基础的全局异常处理

http://www.lryc.cn/news/615556.html

相关文章:

  • 环路补偿知识
  • 算法_python_学习记录_01
  • 比较useCallback、useMemo 和 React.memo
  • leetcode 11. 盛最多水的容器 -java
  • 欢迎走进《励曼旋耕》
  • HarvardX TinyML小笔记1(番外2:神经网络)
  • 物联网之常见网络配置
  • UE破碎Chaos分配模型内部面材质
  • 编程速递:2025 年巴西 Embarcadero 会议,期待您的到来
  • 【unitrix数间混合计算】2.10 小数部分特征(bin_frac.rs)
  • 【QT】QMainWindow:打造专业级桌面应用的基石
  • pdf预览Vue-PDF-Embed
  • Linux下管道的实现
  • js获取当前时间
  • 基于dynamic的Druid 与 HikariCP 连接池集成配置区别
  • Web自动化技术选择
  • [Oracle] TRUNC()函数
  • 11. 为什么要用static关键字
  • Qt Graphics View框架概述
  • SpringBoot日志关系
  • 分治-快排-面试题 17.14.最小k个数-力扣(LeetCode)
  • 【Datawhale AI夏令营】让AI读懂财报PDF(多模态RAG)(Task 2)
  • 【无标题】六边形结构在二维拓扑量子色动力学模型中确实具有独特优势,并构建完整的二维拓扑量子色动力学模型。
  • QToolBar 的 addPermanentWidget() 详解与实战场景
  • Python如何将图片转换为PDF格式
  • [SC]SystemC 常见的编译/语法错误与解法(三)
  • PowerShell 入门系列(五):运行命令与命令剖析详解
  • Effective C++ 条款32:确定你的public继承塑模出 is-a 关系
  • pytorch+tensorboard+可视化CNN
  • ubuntu dpkg命令使用指南