一、开篇引言

做前后端分离的电商管理系统,登录认证与权限控制是绝对的核心功能,也是 Java 后端面试的高频考点。你是不是也遇到过这些问题:
前后端分离项目,Session 共享困难,无法用传统 Session 做登录认证
不知道如何实现无状态的用户登录,接口安全无法保障
分不清 JWT 的原理、结构,踩坑后无法排查问题
不知道如何结合 SpringBoot 实现登录拦截、权限校验,防止未授权访问
本文结合你正在做的电商管理系统,从原理到实战,完整实现 SpringBoot 集成 JWT 实现登录认证 + 全局权限拦截,包含完整可复制代码、高频踩坑总结,15 分钟就能搭建企业级登录认证体系,完美适配你的项目。
本文基于 SpringBoot 2.7.x + JWT 0.11.5,适合 Java 后端初学者、正在做前后端分离项目的同学,尤其适合大二计算机专业学生练手电商项目。

二、核心知识点(先懂原理,再写代码)

1. JWT 核心原理
JWT(JSON Web Token)是一种无状态的身份认证方案,核心是将用户身份信息加密后生成 Token,前端存储 Token,每次请求携带 Token 到后端,后端解密验证身份,无需在服务端存储 Session,完美适配前后端分离项目。
2. JWT 结构拆解
JWT 由 3 部分组成,用 . 分隔:
Header(头部):存储签名算法(如 HS256)、Token 类型
Payload(载荷):存储用户信息(如用户 ID、用户名)、过期时间等自定义声明
Signature(签名):用密钥对 Header+Payload 加密生成,防止 Token 被篡改
3. 核心优势
无状态:服务端无需存储 Session,扩展性强,适合分布式系统
跨域友好:完美适配前后端分离、移动端等场景
安全性高:签名机制防止 Token 被篡改,可设置过期时间保障安全

三、环境准备(直接复制,无需修改)

<!-- SpringBoot Web 核心依赖 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!-- JWT 核心依赖(0.11.5 稳定版) -->
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-api</artifactId>
    <version>0.11.5</version>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-impl</artifactId>
    <version>0.11.5</version>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-jackson</artifactId>
    <version>0.11.5</version>
    <scope>runtime</scope>
</dependency>

<!-- MyBatis-Plus(复用电商系统已有依赖) -->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.5.3</version>
</dependency>

<!-- Lombok 简化代码 -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>

2. application.yml 配置

# JWT 配置
jwt:
  # 密钥(生产环境请更换为复杂密钥,建议16位以上)
  secret: my-secret-key-1234567890
  # 过期时间(单位:毫秒,7天 = 7*24*60*60*1000 = 604800000)
  expiration: 604800000
  # Token 前缀
  token-prefix: Bearer
  # 请求头名称
  header: Authorization

四、核心代码实现(严格拆分「工具类 + 拦截器 + 接口」)

4.1 JWT 工具类(核心,生成 / 解析 Token)

package com.example.demo.utils;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.crypto.SecretKey;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * JWT 工具类
 * 用于生成 Token、解析 Token、验证 Token 有效性
 */
@Component
public class JwtUtils {

    @Value("${jwt.secret}")
    private String secret;

    @Value("${jwt.expiration}")
    private Long expiration;

    @Value("${jwt.token-prefix}")
    private String tokenPrefix;

    /**
     * 生成密钥
     */
    private SecretKey getSignKey() {
        return Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8));
    }

    /**
     * 生成 Token
     * @param userId 用户ID
     * @param username 用户名
     * @return 生成的 Token
     */
    public String generateToken(Long userId, String username) {
        Map<String, Object> claims = new HashMap<>();
        claims.put("userId", userId);
        claims.put("username", username);
        return createToken(claims, username);
    }

    /**
     * 创建 Token
     */
    private String createToken(Map<String, Object> claims, String subject) {
        Date now = new Date();
        Date expireDate = new Date(now.getTime() + expiration);
        return Jwts.builder()
                .setClaims(claims) // 自定义载荷
                .setSubject(subject) // 主题
                .setIssuedAt(now) // 签发时间
                .setExpiration(expireDate) // 过期时间
                .signWith(getSignKey(), SignatureAlgorithm.HS256) // 签名
                .compact();
    }

    /**
     * 解析 Token,获取载荷
     */
    public Claims parseToken(String token) {
        return Jwts.parserBuilder()
                .setSigningKey(getSignKey())
                .build()
                .parseClaimsJws(token)
                .getBody();
    }

    /**
     * 从 Token 中获取用户ID
     */
    public Long getUserIdFromToken(String token) {
        Claims claims = parseToken(token);
        return Long.valueOf(claims.get("userId").toString());
    }

    /**
     * 验证 Token 是否过期
     */
    public Boolean isTokenExpired(String token) {
        Claims claims = parseToken(token);
        return claims.getExpiration().before(new Date());
    }

    /**
     * 验证 Token 有效性
     */
    public Boolean validateToken(String token) {
        try {
            return !isTokenExpired(token);
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 去除 Token 前缀
     */
    public String removePrefix(String token) {
        if (token.startsWith(tokenPrefix)) {
            return token.substring(tokenPrefix.length()).trim();
        }
        return token;
    }
}

4.2 JWT 拦截器(全局权限拦截,验证 Token)

package com.example.demo.interceptor;

import com.example.demo.utils.JwtUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * JWT 拦截器
 * 拦截所有请求,验证 Token 有效性,实现登录认证
 */
@Component
public class JwtInterceptor implements HandlerInterceptor {

    @Resource
    private JwtUtils jwtUtils;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 1. 处理跨域预检请求(OPTIONS 直接放行)
        if ("OPTIONS".equals(request.getMethod())) {
            return true;
        }

        // 2. 从请求头获取 Token
        String token = request.getHeader("Authorization");

        // 3. Token 为空,返回未授权
        if (token == null || token.isEmpty()) {
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            response.getWriter().write("{\"code\":401,\"msg\":\"未登录,请先登录\"}");
            return false;
        }

        // 4. 去除 Token 前缀
        token = jwtUtils.removePrefix(token);

        // 5. 验证 Token 有效性
        if (!jwtUtils.validateToken(token)) {
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            response.getWriter().write("{\"code\":401,\"msg\":\"Token 已过期,请重新登录\"}");
            return false;
        }

        // 6. Token 有效,放行请求
        return true;
    }
}

4.3 拦截器配置类(注册拦截器,配置放行路径)

package com.example.demo.config;

import com.example.demo.interceptor.JwtInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import javax.annotation.Resource;

/**
 * WebMvc 配置类
 * 注册 JWT 拦截器,配置拦截/放行路径
 */
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Resource
    private JwtInterceptor jwtInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(jwtInterceptor)
                // 拦截所有请求
                .addPathPatterns("/**")
                // 放行登录、注册等接口(电商系统核心放行路径)
                .excludePathPatterns(
                        "/user/login", // 登录接口
                        "/user/register", // 注册接口
                        "/captcha/get", // 验证码接口
                        "/doc.html", // Knife4j 接口文档
                        "/webjars/**",
                        "/v3/api-docs/**"
                );
    }
}

4.4 登录接口实现(电商系统用户登录)

package com.example.demo.controller;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.example.demo.common.Result;
import com.example.demo.entity.User;
import com.example.demo.service.UserService;
import com.example.demo.utils.JwtUtils;
import org.springframework.util.DigestUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;

/**
 * 用户登录控制器
 * 实现电商系统用户登录,生成 JWT Token
 */
@RestController
@RequestMapping("/user")
public class LoginController {

    @Resource
    private UserService userService;

    @Resource
    private JwtUtils jwtUtils;

    /**
     * 用户登录接口
     * @param user 登录信息(username、password)
     * @return 登录结果 + Token
     */
    @PostMapping("/login")
    public Result<Map<String, Object>> login(@RequestBody User user) {
        // 1. 校验用户名密码非空
        if (user.getUsername() == null || user.getPassword() == null) {
            return Result.fail("用户名或密码不能为空");
        }

        // 2. 根据用户名查询用户
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(User::getUsername, user.getUsername())
               .eq(User::getDeleted, 0);
        User existUser = userService.getOne(wrapper);

        // 3. 用户不存在
        if (existUser == null) {
            return Result.fail("用户名不存在");
        }

        // 4. 密码校验(实际项目用 BCrypt 加密,此处用 MD5 演示)
        String inputPwd = DigestUtils.md5DigestAsHex(user.getPassword().getBytes());
        if (!inputPwd.equals(existUser.getPassword())) {
            return Result.fail("密码错误");
        }

        // 5. 生成 JWT Token
        String token = jwtUtils.generateToken(existUser.getId(), existUser.getUsername());

        // 6. 返回登录结果
        Map<String, Object> result = new HashMap<>();
        result.put("token", token);
        result.put("user", existUser);
        return Result.success(result);
    }
}

五、接口测试效果演示

1. 登录请求
请求:POST /user/login请求体:

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

返回

{
    "code": 200,
    "msg": "操作成功",
    "data": {
        "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
        "user": {
            "id": 1,
            "username": "zhangsan",
            "nickname": "张三",
            "status": 1
        }
    }
}

2. 带 Token 请求用户接口
请求头:Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…请求:GET /user/1返回:

{
    "code": 200,
    "msg": "操作成功",
    "data": {
        "id": 1,
        "username": "zhangsan",
        "nickname": "张三"
    }
}

3. 无 Token / 过期 Token 请求
返回:

{
    "code": 401,
    "msg": "未登录,请先登录"
}

六、高频踩坑总结(新手必看)

坑 1:跨域预检请求(OPTIONS)被拦截
原因:拦截器没有放行 OPTIONS 请求,导致跨域失败
解决方案:在拦截器中添加 if (“OPTIONS”.equals(request.getMethod())) { return true; },直接放行预检请求
坑 2:Token 过期时间设置不合理
原因:过期时间太短,用户频繁登录;太长,安全性低
解决方案:电商系统建议设置 7 天过期,同时实现 Token 刷新机制(后续可扩展)
坑 3:密钥泄露,Token 被篡改
原因:密钥过于简单,生产环境硬编码在代码中
解决方案:密钥设置为 16 位以上复杂字符串,生产环境通过配置中心管理,禁止硬编码
坑 4:载荷存储敏感信息
原因:JWT 载荷是 Base64 编码,可直接解码,不能存储密码等敏感信息
解决方案:载荷仅存储用户 ID、用户名等非敏感信息,敏感信息绝对不能存入
坑 5:拦截器放行路径配置错误
原因:登录、验证码等接口被拦截,导致无法登录
解决方案:严格配置放行路径,确保登录、注册、验证码等接口不受拦截

七、总结与延伸

本文完整实现了 SpringBoot 集成 JWT 实现登录认证 + 全局权限拦截,完美适配电商管理系统的登录需求,所有代码可直接复制到你的项目中使用。通过本文的实现,你可以:
彻底解决前后端分离项目的登录认证问题,实现无状态身份校验
掌握 JWT 的核心原理与实战用法,应对面试高频考点
搭建企业级登录认证体系,保障接口安全
为后续权限控制(RBAC)、角色管理打下基础
本文是电商管理系统实战系列的第 4 篇,上一篇讲解了 MyBatis-Plus 条件查询避坑,下一篇将讲解 SpringBoot 集成 Redis 实现缓存优化,感兴趣的同学可以关注我,持续更新 Java 后端实战内容。

八、互动引导

你在集成 JWT 时还遇到过哪些问题?欢迎在评论区留言,我会逐一解答!觉得有用的同学,点赞 + 收藏不迷路,后续更新更多 Java 后端学习、项目实战、面试技巧!关注我,持续分享 Java 后端干货,陪你从大二到秋招拿 offer!

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐