【JavaWeb】Tlias智能学习辅助系统|后端Web实战(登录认证)

目录

一、登录功能

[1.1 需求分析](#1.1 需求分析)

[1.2 思路分析](#1.2 思路分析)

[1.3 接口设计](#1.3 接口设计)

[1.4 代码实现](#1.4 代码实现)

第一步:准备响应实体类

[第二步:编写 Controller](#第二步:编写 Controller)

[第三步:编写 Service](#第三步:编写 Service)

[第四步:编写 Mapper](#第四步:编写 Mapper)

[1.5 功能测试](#1.5 功能测试)

二、登录校验

[2.1 什么是登录校验?](#2.1 什么是登录校验?)

[2.2 核心问题:HTTP是无状态的](#2.2 核心问题:HTTP是无状态的)

[2.3 会话跟踪技术](#2.3 会话跟踪技术)

方案一:Cookie(客户端存储)

方案二:Session(服务端存储)

方案三:令牌技术(当前主流)

[三、JWT 令牌](#三、JWT 令牌)

[3.1 什么是 JWT?](#3.1 什么是 JWT?)

[3.2 JWT 的组成](#3.2 JWT 的组成)

[3.3 生成和校验 JWT](#3.3 生成和校验 JWT)

引入依赖

[生成 JWT](#生成 JWT)

[校验 JWT](#校验 JWT)

[3.4 在登录时下发 JWT](#3.4 在登录时下发 JWT)

[四、使用 Filter 实现登录校验](#四、使用 Filter 实现登录校验)

[4.1 什么是 Filter(过滤器)?](#4.1 什么是 Filter(过滤器)?)

[4.2 Filter 快速入门](#4.2 Filter 快速入门)

第一步:定义过滤器

第二步:配置过滤器

[第三步:开启 Servlet 组件支持](#第三步:开启 Servlet 组件支持)

[4.3 实现登录校验 Filter](#4.3 实现登录校验 Filter)

业务逻辑

代码实现

[4.4 Filter 详解](#4.4 Filter 详解)

执行流程

拦截路径配置

过滤器链

[五、使用 Interceptor 实现登录校验](#五、使用 Interceptor 实现登录校验)

[5.1 什么是 Interceptor?](#5.1 什么是 Interceptor?)

[5.2 Interceptor 快速入门](#5.2 Interceptor 快速入门)

第一步:定义拦截器

第二步:注册配置拦截器

[5.3 实现登录校验 Interceptor](#5.3 实现登录校验 Interceptor)

[5.4 Interceptor 详解](#5.4 Interceptor 详解)

拦截路径配置

执行流程

[六、Filter vs Interceptor](#六、Filter vs Interceptor)

七、最终效果


在前面的课程中,我们已经实现了部门管理、员工管理的基本功能。但是大家有没有发现一个问题:我们并没有登录,就直接访问到了Tlias智能学习辅助系统的后台

这是一个非常严重的安全隐患。如果任何人都能直接访问后台管理系统,那数据安全就无从谈起。所以今天的主题就是------登录认证

最终我们要实现的效果是:

  • 如果用户名或密码错误,不允许登录系统

  • 如果用户名和密码都正确,则登录成功,可以正常访问系统

接下来,就让我们一步步来实现这个完整的登录认证功能。


一、登录功能

1.1 需求分析

在登录界面中,用户输入用户名和密码,点击"登录"按钮后,请求会发送到服务器。服务端判断用户名和密码是否正确:

  • 正确:返回成功结果,前端跳转至系统首页

  • 错误:返回错误提示,用户无法进入系统

1.2 思路分析

登录功能的本质是什么?其实就是一个查询操作------根据用户名和密码去数据库查询员工信息:

  • 如果查询到了数据,说明用户名和密码都正确,登录成功

  • 如果没查到数据,说明用户名或密码错误,登录失败

1.3 接口设计

  • 请求路径/login

  • 请求方式POST

  • 请求参数(JSON格式):

    {
    "username": "admin",
    "password": "123456"
    }

  • 响应数据(JSON格式):

    {
    "code": 1,
    "msg": "success",
    "data": {
    "id": 1,
    "username": "songjiang",
    "name": "宋江",
    "token": "..."
    }
    }

1.4 代码实现

第一步:准备响应实体类

我们先创建一个 LoginInfo 类,用来封装登录成功后返回给前端的数据:

java 复制代码
/**
 * 登录成功结果封装类
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class LoginInfo {
    private Integer id; //员工ID
    private String username; //用户名
    private String name; //姓名
    private String token; //令牌
}
第二步:编写 Controller

LoginController 中接收登录请求:

java 复制代码
@Slf4j
@RestController
public class LoginController {

    @Autowired
    private EmpService empService;

    @PostMapping("/login")
    public Result login(@RequestBody Emp emp){
        log.info("员工来登录啦 , {}", emp);
        LoginInfo loginInfo = empService.login(emp);
        if(loginInfo != null){
            return Result.success(loginInfo);
        }
        return Result.error("用户名或密码错误~");
    }

}

解释@RequestBody 将前端传来的JSON数据自动封装到 Emp 对象中。如果登录成功返回用户信息,否则返回错误提示。

第三步:编写 Service

接口 EmpService

java 复制代码
/**
 * 登录
 */
LoginInfo login(Emp emp);

实现类 EmpServiceImpl

java 复制代码
@Override
public LoginInfo login(Emp emp) {
    Emp empLogin = empMapper.getUsernameAndPassword(emp);
    if(empLogin != null){
        LoginInfo loginInfo = new LoginInfo(empLogin.getId(), empLogin.getUsername(), empLogin.getName(), null);
        return loginInfo;
    }
    return null;
}
第四步:编写 Mapper

使用 @Select 注解直接编写SQL:

java 复制代码
/**
 * 根据用户名和密码查询员工信息
 */
@Select("select * from emp where username = #{username} and password = #{password}")
Emp getUsernameAndPassword(Emp emp);

1.5 功能测试

重启服务,使用 Apifox 发送 POST 请求测试:

  • 输入正确的用户名和密码 → 返回用户信息,登录成功

  • 输入错误的用户名或密码 → 返回"用户名或密码错误~"

前端联调测试通过后,我们发现一个严重问题 :虽然登录功能可以正常工作,但即使在未登录状态下,直接在浏览器地址栏输入后台页面地址,依然可以访问系统

这是因为我们只在登录接口做了验证,但其他所有接口(部门管理、员工管理等)都没有做任何登录判断。也就是说,登录功能只是徒有其表

要解决这个问题,我们需要引入登录校验机制。


二、登录校验

2.1 什么是登录校验?

所谓登录校验,就是在服务器端接收到请求后,先验证用户是否已经登录

  • 如果已登录 → 正常执行业务操作

  • 如果未登录 → 拒绝访问,返回错误信息,前端跳转到登录页面

2.2 核心问题:HTTP是无状态的

为什么需要登录校验?因为 HTTP 协议是无状态的------每一次请求都是独立的,下一次请求不会携带上一次请求的任何数据。

当你登录成功后,服务器虽然知道"你登录了",但当你再次发起请求(比如查询部门列表)时,服务器已经不记得你之前登录过了。

所以我们需要一种机制,让服务器能够识别:这个请求到底是哪个用户发来的?这个用户有没有登录过?

2.3 会话跟踪技术

**会话跟踪:**一种维护浏览器状态的方法,服务器需要识别多次请求是否来自于同一浏览器,以便在同一次会话的多次请求间共享数据。

为了解决"多次请求间共享数据"的问题,我们需要使用会话跟踪技术。主要有三种方案:

方案一:Cookie(客户端存储)

Cookie 是存储在客户端浏览器中的。服务器设置 Cookie 后,浏览器会自动保存,并在后续请求中自动携带。

流程

  1. 用户登录成功 → 服务器设置 Cookie(包含用户信息)

  2. 浏览器自动保存 Cookie

  3. 后续每次请求 → 浏览器自动携带 Cookie

  4. 服务器读取 Cookie → 识别用户身份

优点 :浏览器自动支持,无需手动操作

缺点

  • 移动端APP无法使用

  • 用户可以禁用 Cookie

  • 无法跨域

java 复制代码
@Slf4j
@RestController
public class SessionController {

    //设置Cookie
    @GetMapping("/c1")
    public Result cookie1(HttpServletResponse response){
        response.addCookie(new Cookie("login_username","itheima")); //设置Cookie/响应Cookie
        return Result.success();
    }
        
    //获取Cookie
    @GetMapping("/c2")
    public Result cookie2(HttpServletRequest request){
        Cookie[] cookies = request.getCookies();
        for (Cookie cookie : cookies) {
            if(cookie.getName().equals("login_username")){
                System.out.println("login_username: "+cookie.getValue()); //输出name为login_username的cookie
            }
        }
        return Result.success();
    }
}    
方案二:Session(服务端存储)

Session 是存储在服务器端的。它的底层基于 Cookie 实现。

流程

  1. 首次请求 → 服务器创建 Session 对象,并生成唯一 ID

  2. 服务器通过 Cookie 将 Session ID 返回给浏览器

  3. 后续请求 → 浏览器携带 Cookie(包含 Session ID)

  4. 服务器根据 Session ID 找到对应的 Session 对象

优点 :数据存储在服务端,安全性更高

缺点

  • 集群环境下无法直接使用(多台服务器间 Session 不共享)

  • 同样存在 Cookie 的所有缺点

注意:Session 依赖于 Cookie,如果 Cookie 不可用,Session 也就失效了。

java 复制代码
@Slf4j
@RestController
public class SessionController {

    @GetMapping("/s1")
    public Result session1(HttpSession session){
        log.info("HttpSession-s1: {}", session.hashCode());

        session.setAttribute("loginUser", "tom"); //往session中存储数据
        return Result.success();
    }

    @GetMapping("/s2")
    public Result session2(HttpServletRequest request){
        HttpSession session = request.getSession();
        log.info("HttpSession-s2: {}", session.hashCode());

        Object loginUser = session.getAttribute("loginUser"); //从session中获取数据
        log.info("loginUser: {}", loginUser);
        return Result.success(loginUser);
    }
}
方案三:令牌技术(当前主流)

令牌就是一个用户身份的标识 ,本质就是一个字符串

流程

  1. 登录成功 → 服务器生成一个令牌(包含用户信息),返回给前端

  2. 前端存储令牌(可存 Cookie 或 localStorage)

  3. 后续请求 → 前端将令牌携带在请求头中

  4. 服务器校验令牌有效性 → 有效则放行,无效则拒绝

优点

  • 支持 PC 端和移动端

  • 解决集群环境下的认证问题

  • 减轻服务器存储压力(令牌由客户端保存)

缺点:需要自己实现生成、传递和校验的逻辑

在现在的企业开发中,令牌技术是主流选择 。而我们使用的令牌类型是 JWT


三、JWT 令牌

3.1 什么是 JWT?

JWT (JSON Web Token)(https://jwt.io/)是一种简洁的、自包含的令牌格式,用于在通信双方安全地传输信息。

  • 简洁:只是一个字符串,可以在请求头或参数中轻松传递

  • 自包含:令牌本身包含了用户信息,无需额外查询数据库

3.2 JWT 的组成

JWT 由三个部分组成,用英文句号 . 分隔:

java 复制代码
xxxxx.yyyyy.zzzzz

第一部分:Header(头)

记录令牌类型和签名算法,例如:

java 复制代码
{"alg":"HS256", "type":"JWT"}

第二部分:Payload(有效载荷)

携带自定义信息(如用户ID、用户名等),例如:

java 复制代码
{"id":1, "username":"Tom"}

第三部分:Signature(签名)

防止令牌被篡改。将 Header 和 Payload 结合,并使用密钥和指定算法计算得出。

安全原理:一旦 JWT 中的任何字符被篡改,签名验证就会失败,从而保证了令牌的完整性和安全性。
JWT是如何将原始的JSON格式数据,转变为字符串的呢?

  • 其实在生成JWT令牌时,会对JSON格式的数据进行一次编码:进行base64编码

  • Base64:是一种基于64个可打印的字符来表示二进制数据的编码方式。既然能编码,那也就意味着也能解码。所使用的64个字符分别是A到Z、a到z、 0- 9,一个加号,一个斜杠,加起来就是64个字符。任何数据经过base64编码之后,最终就会通过这64个字符来表示。当然还有一个符号,那就是等号。等号它是一个补位的符号

  • 需要注意的是Base64是编码方式,而不是加密方式。

3.3 生成和校验 JWT

引入依赖
XML 复制代码
<!-- JWT依赖-->
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>
生成 JWT
java 复制代码
@Test
public void testGenJwt() {
    Map<String, Object> claims = new HashMap<>();
    claims.put("id", 10);
    claims.put("username", "itheima");

    String jwt = Jwts.builder().signWith(SignatureAlgorithm.HS256, "aXRjYXN0")
        .addClaims(claims)
        .setExpiration(new Date(System.currentTimeMillis() + 12 * 3600 * 1000))
        .compact();

    System.out.println(jwt);
}
校验 JWT
java 复制代码
@Test
public void testParseJwt() {
    Claims claims = Jwts.parser().setSigningKey("aXRjYXN0")
        .parseClaimsJws("eyJhbGciOiJIUzI1NiJ9.eyJpZCI6MTAsInVzZXJuYW1lIjoiaXRoZWltYSIsImV4cCI6MTcwMTkwOTAxNX0.N-MD6DmoeIIY5lB5z73UFLN9u7veppx1K5_N_jS9Yko")
        .getBody();
    System.out.println(claims);
}

注意事项

  1. 签名密钥必须保持一致

  2. 令牌过期后解析会报错

  3. 令牌被篡改后解析会报错

3.4 在登录时下发 JWT

我们创建一个 JwtUtils 工具类来简化操作:

java 复制代码
public class JwtUtils {

    private static String signKey = "SVRIRUlNQQ==";
    private static Long expire = 43200000L;

    /**
     * 生成JWT令牌
     * @return
     */
    public static String generateJwt(Map<String,Object> claims){
        String jwt = Jwts.builder()
                .addClaims(claims)
                .signWith(SignatureAlgorithm.HS256, signKey)
                .setExpiration(new Date(System.currentTimeMillis() + expire))
                .compact();
        return jwt;
    }

    /**
     * 解析JWT令牌
     * @param jwt JWT令牌
     * @return JWT第二部分负载 payload 中存储的内容
     */
    public static Claims parseJWT(String jwt){
        Claims claims = Jwts.parser()
                .setSigningKey(signKey)
                .parseClaimsJws(jwt)
                .getBody();
        return claims;
    }
}

修改 EmpServiceImpllogin 方法:

java 复制代码
@Override
public LoginInfo login(Emp emp) {
    Emp empLogin = empMapper.getUsernameAndPassword(emp);
    if(empLogin != null){
        //1. 生成JWT令牌
        Map<String,Object> dataMap = new HashMap<>();
        dataMap.put("id", empLogin.getId());
        dataMap.put("username", empLogin.getUsername());
        
        String jwt = JwtUtils.generateJwt(dataMap);
        LoginInfo loginInfo = new LoginInfo(empLogin.getId(), empLogin.getUsername(), empLogin.getName(), jwt);
        return loginInfo;
    }
    return null;
}

现在登录成功后会返回 JWT 令牌,前端会将其存储在 localStorage 中,并在后续请求中通过请求头 token 携带回来。


四、使用 Filter 实现登录校验

现在前端已经在每次请求中携带了令牌,我们需要在服务端统一拦截所有请求,校验令牌的有效性。

4.1 什么是 Filter(过滤器)?

Filter 是 JavaWeb 三大组件之一,它可以在请求到达资源之前进行拦截,完成一些通用操作(如登录校验、编码处理等)。

4.2 Filter 快速入门

第一步:定义过滤器
java 复制代码
public class DemoFilter implements Filter {
    //初始化方法, web服务器启动, 创建Filter实例时调用, 只调用一次
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("init ...");
    }

    //拦截到请求时,调用该方法,可以调用多次
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
        System.out.println("拦截到了请求...");
    }

    //销毁方法, web服务器关闭时调用, 只调用一次
    public void destroy() {
        System.out.println("destroy ... ");
    }
}
第二步:配置过滤器

只需要在Filter类上添加一个注解:@WebFilter,并指定属性urlPatterns,通过这个属性指定过滤器要拦截哪些请求

java 复制代码
@WebFilter(urlPatterns = "/*") //配置过滤器要拦截的请求路径( /* 表示拦截浏览器的所有请求 )
public class DemoFilter implements Filter {
    //初始化方法, web服务器启动, 创建Filter实例时调用, 只调用一次
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("init ...");
    }

    //拦截到请求时,调用该方法,可以调用多次
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
        System.out.println("拦截到了请求...");
    }

    //销毁方法, web服务器关闭时调用, 只调用一次
    public void destroy() {
        System.out.println("destroy ... ");
    }
}
第三步:开启 Servlet 组件支持

在启动类上添加 @ServletComponentScan

java 复制代码
@ServletComponentScan //开启对Servlet组件的支持
@SpringBootApplication
public class TliasManagementApplication {
    public static void main(String[] args) {
        SpringApplication.run(TliasManagementApplication.class, args);
    }
}

注意事项: 在过滤器Filter中,如果不执行放行操作,将无法访问后面的资源。 放行操作:chain.doFilter(request, response);
大概清楚了在Filter过滤器的实现步骤了,那在正式开发登录校验过滤器之前,我们思考两个问题:

1.所有的请求,拦截到了之后,都需要校验令牌吗 ?

答案:登录请求例外

2.拦截到请求后,什么情况下才可以放行,执行业务操作 ?

答案:有令牌,且令牌校验通过(合法);否则都返回未登录错误结果

4.3 实现登录校验 Filter

业务逻辑
  1. 获取请求 URL

  2. 判断是否是登录请求 → 是则直接放行

  3. 获取请求头中的 token

  4. 判断 token 是否存在 → 不存在则返回 401

  5. 解析 token → 解析失败则返回 401

  6. 放行

代码实现
java 复制代码
/**
 * 令牌校验过滤器
 */
@Slf4j
@WebFilter(urlPatterns = "/*")
public class TokenFilter implements Filter {

    @Override
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) resp;
        //1. 获取请求url。
        String url = request.getRequestURL().toString();

        //2. 判断请求url中是否包含login,如果包含,说明是登录操作,放行。
        if(url.contains("login")){ //登录请求
            log.info("登录请求 , 直接放行");
            chain.doFilter(request, response);
            return;
        }

        //3. 获取请求头中的令牌(token)。
        String jwt = request.getHeader("token");

        //4. 判断令牌是否存在,如果不存在,返回错误结果(未登录)。
        if(!StringUtils.hasLength(jwt)){ //jwt为空
            log.info("获取到jwt令牌为空, 返回错误结果");
            response.setStatus(HttpStatus.SC_UNAUTHORIZED);
            return;
        }

        //5. 解析token,如果解析失败,返回错误结果(未登录)。
        try {
            JwtUtils.parseJWT(jwt);
        } catch (Exception e) {
            e.printStackTrace();
            log.info("解析令牌失败, 返回错误结果");
            response.setStatus(HttpStatus.SC_UNAUTHORIZED);
            return;
        }

        //6. 放行。
        log.info("令牌合法, 放行");
        chain.doFilter(request , response);
    }

}

4.4 Filter 详解

执行流程
java 复制代码
请求 → Filter(放行前逻辑)→ 放行 → 访问资源 → Filter(放行后逻辑)→ 响应
拦截路径配置

|------------|------------------|-----------------------|
| 拦截路径 | urlPatterns值 | 含义 |
| 拦截具体路径 | /login | 只有访问 /login 路径时,才会被拦截 |
| 目录拦截 | /emps/* | 访问/emps下的所有资源,都会被拦截 |
| 拦截所有 | /* | 访问所有资源,都会被拦截 |

过滤器链

过滤器链上过滤器的执行顺序:注解配置的Filter,优先级是按照过滤器类名(字符串)的自然排序。 比如:

  • AbcFilter

  • DemoFilter

这两个过滤器来说,AbcFilter 会先执行,DemoFilter会后执行。


五、使用 Interceptor 实现登录校验

除了 Filter,我们还可以使用 Spring 框架提供的 Interceptor(拦截器) 来实现登录校验。

5.1 什么是 Interceptor?

Interceptor 是 Spring 框架提供的,用于动态拦截控制器方法执行的机制。它比 Filter 更精细,只拦截 Spring 管理的资源(Controller 方法)。

5.2 Interceptor 快速入门

第一步:定义拦截器
java 复制代码
//自定义拦截器
@Component
public class DemoInterceptor implements HandlerInterceptor {
    //目标资源方法执行前执行。 返回true:放行    返回false:不放行
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle .... ");
        
        return true; //true表示放行
    }

    //目标资源方法执行后执行
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle ... ");
    }

    //视图渲染完毕后执行,最后执行
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion .... ");
    }
}
第二步:注册配置拦截器
java 复制代码
@Configuration  
public class WebConfig implements WebMvcConfigurer {

    //自定义的拦截器对象
    @Autowired
    private DemoInterceptor demoInterceptor;

    
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
       //注册自定义拦截器对象
        registry.addInterceptor(demoInterceptor).addPathPatterns("/**");//设置拦截器拦截的请求路径( /** 表示拦截所有请求)
    }
}

5.3 实现登录校验 Interceptor

java 复制代码
@Slf4j
@Component
public class TokenInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //1. 获取请求url。
        String url = request.getRequestURL().toString();

        //2. 判断请求url中是否包含login,如果包含,说明是登录操作,放行。
        if(url.contains("login")){ //登录请求
            log.info("登录请求 , 直接放行");
            return true;
        }

        //3. 获取请求头中的令牌(token)。
        String jwt = request.getHeader("token");

        //4. 判断令牌是否存在,如果不存在,返回错误结果(未登录)。
        if(!StringUtils.hasLength(jwt)){ //jwt为空
            log.info("获取到jwt令牌为空, 返回错误结果");
            response.setStatus(HttpStatus.SC_UNAUTHORIZED);
            return false;
        }

        //5. 解析token,如果解析失败,返回错误结果(未登录)。
        try {
            JwtUtils.parseJWT(jwt);
        } catch (Exception e) {
            e.printStackTrace();
            log.info("解析令牌失败, 返回错误结果");
            response.setStatus(HttpStatus.SC_UNAUTHORIZED);
            return false;
        }

        //6. 放行。
        log.info("令牌合法, 放行");
        return true;
    }

}

注册配置:

java 复制代码
@Configuration  
public class WebConfig implements WebMvcConfigurer {
    //拦截器对象
    @Autowired
    private TokenInterceptor tokenInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
       //注册自定义拦截器对象
        registry.addInterceptor(tokenInterceptor).addPathPatterns("/**");
    }
}

5.4 Interceptor 详解

拦截路径配置
java 复制代码
@Configuration  
public class WebConfig implements WebMvcConfigurer {

    //拦截器对象
    @Autowired
    private DemoInterceptor demoInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //注册自定义拦截器对象
        registry.addInterceptor(demoInterceptor)
                .addPathPatterns("/**")//设置拦截器拦截的请求路径( /** 表示拦截所有请求)
                .excludePathPatterns("/login");//设置不拦截的请求路径
    }
}

|-----------------|---------------|-------------------------------------------|
| 拦截路径 | 含义 | 举例 |
| /* | 一级路径 | 能匹配/depts,/emps,/login,不能匹配 /depts/1 |
| /** | 任意级路径 | 能匹配/depts,/depts/1,/depts/1/2 |
| /depts/* | /depts下的一级路径 | 能匹配/depts/1,不能匹配/depts/1/2,/depts |
| /depts/** | /depts下的任意级路径 | 能匹配/depts,/depts/1,/depts/1/2,不能匹配/emps/1 |

执行流程
java 复制代码
请求 → Filter → DispatcherServlet → Interceptor(preHandle) → Controller
→ Interceptor(postHandle) → 视图渲染 → Interceptor(afterCompletion) → 响应

六、Filter vs Interceptor

对比项 Filter(过滤器) Interceptor(拦截器)
来源 JavaWeb 规范 Spring 框架
拦截范围 所有 Web 资源(包括静态资源) 仅 Spring 管理的 Controller
接口 javax.servlet.Filter org.springframework.web.servlet.HandlerInterceptor
执行时机 请求进入 Web 容器后,进入 Servlet 前 请求进入 DispatcherServlet 后,进入 Controller 前
使用场景 通用拦截(编码、日志、权限) 业务相关拦截(登录校验、权限验证)

建议 :对于登录校验这种业务相关的拦截,推荐使用 Interceptor,因为它更加灵活,能精确控制到 Controller 方法级别。


七、最终效果

完成以上所有步骤后:

  1. 未登录用户访问后台页面 → 被拦截 → 自动跳转到登录页面

  2. 登录成功 → 服务端生成 JWT 令牌 → 前端存储令牌

  3. 已登录用户访问后台功能 → 请求携带令牌 → 服务端校验通过 → 正常访问

完整流程回顾

  1. 实现登录功能(查询数据库验证用户名密码)

  2. 引入 JWT 令牌技术(登录成功后生成令牌返回给前端)

  3. 使用 Filter 或 Interceptor 统一拦截请求

  4. 在拦截器中校验 JWT 令牌的有效性

  5. 校验通过放行,不通过返回未登录状态

相关推荐
岁月留痕1681 小时前
11 实战项目一:搭建“一言”项目骨架
前端
用户2181697049301 小时前
Flutter (十五) CustomScrollView PageView
前端
WILLF1 小时前
Python vs JavaScript 异常处理对比
前端·python
__sjfzllv___1 小时前
在职前端Leader学习/转行 AI Agent -DAY31
前端
岁月留痕1681 小时前
4 Dart 篇:异步(并发)
前端
MartinYeung51 小时前
[论文学习]AdInject:通过广告投放对Web代理发起真实世界黑盒攻击
前端·学习
用户2181697049301 小时前
Flutter (十六) 组件通信
前端
mONESY1 小时前
React 前端如何不傻等后端接口?
前端·javascript·后端
岁月留痕1681 小时前
6 Flutter 篇:Flutter 分层式架构设计
前端