【Spring Boot + MyBatis|第7篇】JWT 登录认证与拦截器实现

前言

前面我们已经学习了三层架构、参数接收、统一返回结果、动态 SQL、分页查询和全局异常处理。

这些内容基本覆盖了普通 CRUD 接口的开发。但是在真实项目中,还有一个非常重要的问题:用户没有登录,能不能访问后端接口?

答案肯定是不可以。

所以这一篇我们来学习 Spring Boot 项目中常见的登录认证方案:JWT + 拦截器

一、JWT 是什么?

JWT 全称是 JSON Web Token,可以简单理解为一个登录令牌。

用户登录成功后,后端生成一个 token 返回给前端。前端以后每次请求接口时,都把 token 带上。后端拿到 token 后进行校验,如果 token 合法,就允许访问;如果 token 不合法,就拒绝访问。

整体流程是:

text 复制代码
用户登录
  ↓
后端校验用户名和密码
  ↓
登录成功后生成 JWT
  ↓
前端保存 JWT
  ↓
后续请求在请求头中携带 JWT
  ↓
后端拦截器校验 JWT
  ↓
校验通过,放行请求

二、登录接口实现

1. Controller 层

java 复制代码
@RestController
@RequestMapping("/login")
public class LoginController {

    @Autowired
    private EmpService empService;

    @PostMapping
    public Result login(@RequestBody Emp emp) {
        Emp loginEmp = empService.login(emp);

        if (loginEmp != null) {
            Map<String, Object> claims = new HashMap<>();
            claims.put("id", loginEmp.getId());
            claims.put("username", loginEmp.getUsername());

            String token = JwtUtils.generateJwt(claims);
            return Result.success(token);
        }

        return Result.error("用户名或密码错误");
    }
}

2. Service 层

java 复制代码
public interface EmpService {
    Emp login(Emp emp);
}
java 复制代码
@Service
public class EmpServiceImpl implements EmpService {

    @Autowired
    private EmpMapper empMapper;

    @Override
    public Emp login(Emp emp) {
        return empMapper.getByUsernameAndPassword(emp);
    }
}

3. Mapper 层

java 复制代码
@Mapper
public interface EmpMapper {

    @Select("select * from emp where username = #{username} and password = #{password}")
    Emp getByUsernameAndPassword(Emp emp);
}

4. 文字说明

登录接口的核心逻辑是:

  1. 前端传入用户名和密码
  2. Controller 调用 Service 进行登录校验
  3. Service 调用 Mapper 查询数据库
  4. 如果查到了员工信息,说明登录成功
  5. 后端生成 JWT 返回给前端
  6. 如果没有查到,返回错误提示

这里要注意,真实项目中密码一般不会明文存储,通常会使用加密方式保存。学习阶段可以先用明文理解流程。

三、JWT 工具类实现

java 复制代码
public class JwtUtils {

    private static final String SIGN_KEY = "zhiguang_secret_key";
    private static final Long EXPIRE = 43200000L;

    public static String generateJwt(Map<String, Object> claims) {
        return Jwts.builder()
                .addClaims(claims)
                .signWith(SignatureAlgorithm.HS256, SIGN_KEY)
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRE))
                .compact();
    }

    public static Claims parseJwt(String jwt) {
        return Jwts.parser()
                .setSigningKey(SIGN_KEY)
                .parseClaimsJws(jwt)
                .getBody();
    }
}

文字说明

这个工具类主要有两个方法:

java 复制代码
generateJwt()

用来生成 token。

java 复制代码
parseJwt()

用来解析 token。

其中:

java 复制代码
SIGN_KEY

表示签名密钥,真实项目中不要直接写死在代码里,可以放到配置文件或者环境变量中。

java 复制代码
EXPIRE

表示 token 的过期时间,这里设置为 12 小时。

四、拦截器校验 Token

1. LoginCheckInterceptor

java 复制代码
@Component
public class LoginCheckInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response,
                             Object handler) throws Exception {

        String token = request.getHeader("token");

        if (token == null || token.isEmpty()) {
            response.setStatus(401);
            response.getWriter().write("NOT_LOGIN");
            return false;
        }

        try {
            JwtUtils.parseJwt(token);
        } catch (Exception e) {
            response.setStatus(401);
            response.getWriter().write("NOT_LOGIN");
            return false;
        }

        return true;
    }
}

2. 文字说明

拦截器中的 preHandle 方法会在 Controller 执行之前运行。

这里的逻辑是:

  1. 从请求头中获取 token
  2. 如果 token 为空,说明用户没有登录
  3. 如果 token 解析失败,说明 token 不合法或已过期
  4. 校验通过后,返回 true 放行请求
  5. 校验失败后,返回 false 拦截请求

五、注册拦截器

java 复制代码
@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Autowired
    private LoginCheckInterceptor loginCheckInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginCheckInterceptor)
                .addPathPatterns("/**")
                .excludePathPatterns("/login");
    }
}

文字说明

这里表示拦截所有请求:

java 复制代码
.addPathPatterns("/**")

但是登录接口不能拦截,否则用户还没登录就访问不了登录接口,所以要排除:

java 复制代码
.excludePathPatterns("/login")

六、前端请求应该怎么带 Token?

登录成功后,后端返回 token。

后续请求时,前端需要在请求头中携带:

text 复制代码
token: 后端返回的JWT字符串

比如请求员工列表时:

text 复制代码
GET /emps
token: eyJhbGciOiJIUzI1NiJ9...

后端拦截器就可以从请求头中拿到 token,并进行校验。

七、常见问题

1. 为什么登录接口要排除拦截?

因为登录接口本身就是用来获取 token 的。

如果登录接口也被拦截,用户没有 token,就永远无法登录。

2. token 过期后怎么办?

token 过期后,解析时会抛出异常,拦截器会返回未登录。

前端收到未登录状态后,一般会跳转到登录页面,让用户重新登录。

3. JWT 里面能不能放密码?

不建议。

JWT 中可以放用户 id、用户名、权限标识等信息,不要放密码、手机号、身份证号等敏感数据。

八、总结

这一篇主要学习了 Spring Boot 项目中 JWT 登录认证和拦截器的实现。

登录成功后,后端生成 JWT 返回给前端;前端后续请求携带 JWT;后端通过拦截器统一校验 token。这样就可以避免每个接口都手动判断用户是否登录。

这一篇相比前面的 CRUD 更贴近真实项目,因为它解决的是接口安全访问的问题。


相关推荐
步步为营DotNet2 小时前
借助 C# 14 特性强化 .NET 后端数据验证的深度实践
java·c#·.net
西安邮电大学2 小时前
有关栈的经典算法题
java·后端·其他·算法·面试
手握风云-2 小时前
ProtoBuf:从序列化原理到高性能架构底座(一)
java·网络·架构
摇滚侠2 小时前
SpringMVC 入门到实战 配置类替换 XML 配置文件 86-91
xml·java·后端·spring·maven·intellij-idea
栗子~~2 小时前
金融场景下BigDecimal 运算规范 + 常用场景使用 + 数据库字段设计详解
java·数据库·金融
我登哥MVP2 小时前
SpringCloud Alibaba 核心组件解析:服务注册与发现(Nacos)
java·spring boot·后端·spring·spring cloud·java-ee·maven
兰令水2 小时前
leecodecode【单调栈】【2026.6.12打卡-java版本】
java·开发语言·算法
云烟成雨TD2 小时前
Agent Scope Java 2.x 系列【8】工具调用
java·人工智能·agent
AI人工智能+电脑小能手3 小时前
【大白话说Java面试题 第112题】【并发篇】第12题:AQS 中节点的入队时机有哪些?
java·开发语言·面试