SpringBoot+JWT 实现前后端分离登录鉴权

1、业务场景说明

本智慧社区后端接口文档登录模块接口:

GET /captcha:获取 uuid+base64 验证码图片

POST /login:传入 username、password、captcha、uuid,返回 token 与 expire 过期秒数

GET /checkToken:校验 token 有效性返回 ok/error

PUT /sys/user/updatePassword 修改密码

POST /logout 退出登录

传统 session 不适合前后端分离,本项目选择 JWT 做无状态认证,token 放在请求头每次请求携带。

2、JWT 原理

JWT 分为三部分:header、payload、signature。

header:加密算法;

payload:存放用户 id,不要存敏感密码;

signature:秘钥签名防止篡改。

本项目登录返回:token 字符串 + expire 过期时间 1296000 秒。

json

//登录成功返回示例

{

"msg": "登录成功",

"code": 200,

"data": {

"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.xxx",

"expire": 1296000

}

}

3、pom 依赖

复制代码
<!-- jjwt -->
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>
<!-- 验证码 -->
<dependency>
    <groupId>com.github.penggle</groupId>
    <artifactId>kaptcha</artifactId>
    <version>2.3.2</version>
</dependency>

4、Jwt 工具类

复制代码
@Component
public class JwtUtil {
    //密钥,项目放到yml配置
    private static final String SECRET = "zhihuishequ2026secretkey123456";

    //生成token,传入userId,过期时间秒
    public String generateToken(Long userId, long expireSecond){
        Date now = new Date();
        Date expireDate = new Date(System.currentTimeMillis()+expireSecond*1000);
        return Jwts.builder()
                .setSubject(userId.toString())
                .setIssuedAt(now)
                .setExpiration(expireDate)
                .signWith(SignatureAlgorithm.HS512,SECRET)
                .compact();
    }

    //解析token获取用户id
    public Claims getClaimsByToken(String token){
        try {
            return Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token).getBody();
        }catch (Exception e){
            return null;
        }
    }

    //校验token是否合法、过期
    public boolean validateToken(String token){
        Claims claims = getClaimsByToken(token);
        if(claims == null) return false;
        return !claims.getExpiration().before(new Date());
    }
}

5、验证码接口 /captcha

返回 uuid、base64 图片、验证码 code;redis 存储 uuid 作为 key,value 是验证码,设置过期时间。

复制代码
@GetMapping("/captcha")
public Result captcha(){
    //生成uuid
    String uuid = UUID.randomUUID().toString().replace("-","");
    //生成图片验证码字符串
    String code = kaptchaProducer.createText();
    //生成图片,转base64
    BufferedImage image = kaptchaProducer.createImage(code);
    ByteArrayOutputStream stream = new ByteArrayOutputStream();
    ImageIO.write(image,"png",stream);
    String base64 = "data:image/png;base64,"+ Base64.encodeBase64String(stream.toByteArray());
    //存入redis,2分钟过期
    redisTemplate.opsForValue().set("captcha:"+uuid,code,2, TimeUnit.MINUTES);
    Map<String,Object> map = new HashMap<>();
    map.put("uuid",uuid);
    map.put("captcha",base64);
    map.put("code",code);
    return Result.success(map);
}

6、登录接口 /login

接收参数:username、password、captcha、uuid。逻辑:

根据 uuid 从 redis 取出验证码,比对;

查询数据库用户,密码比对(MD5 加密存储,文档示例密码密文);

校验成功调用 jwt 工具生成 token,expire=1296000;

返回 token;删除 redis 验证码。

7、Token 拦截器实现

所有接口除登录、验证码之外,都要校验请求头 token。

复制代码
@Component
public class TokenInterceptor implements HandlerInterceptor {
    @Autowired
    private JwtUtil jwtUtil;
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String token = request.getHeader("token");
        if(token == null || !jwtUtil.validateToken(token)){
            response.setContentType("application/json;charset=utf-8");
            response.getWriter().write(JSON.toJSONString(Result.error(401,"token失效请重新登录")));
            return false;
        }
        //把userId放入request,controller直接获取
        Claims claims = jwtUtil.getClaimsByToken(token);
        request.setAttribute("userId", Long.parseLong(claims.getSubject()));
        return true;
    }
}

8、踩坑 & 安全优化总结

JWT 无状态,本身无法主动失效:logout 退出登录方案:redis 维护黑名单 token,有效期等于 jwt 过期时间;

payload 不要存放密码、手机号等敏感信息,只是 base64 可解码;

验证码一定要绑定 uuid,防止暴力破解;

秘钥不要硬编码代码,放到 application.yml;

前端 token 存储建议 localStorage,请求头携带 token;

修改密码成功之后,建议清空旧 token,强制重新登录。

9、拓展思考

JWT vs Session 各自适用场景;token 黑名单实现主动失效方案;refreshToken 刷新 token 优化用户体验。

相关推荐
Wang's Blog1 小时前
Java 项目部署之 Docker工具快速入门: 自定义镜像的分层结构与写时复制
java·开发语言·docker
WeiXin_DZbishe9 小时前
基于springboot宠物领养系统-计算机毕设 附源码【课程设计】94217
vue.js·spring boot·python·django·php·课程设计
kimnoic10 小时前
Python数据库学习心得:SQLite、MySQL、PostgreSQL优缺点
开发语言·python
vx_bisheyuange10 小时前
基于SpringBoot的体育商品推荐系统
spring boot·毕业设计
别动我齐刘海10 小时前
C++ 工程化学习
java·linux·前端·c++·学习·机器学习·机器人
必须得开心呀11 小时前
python编写带界面的软件,需要安装哪些插件
开发语言·python
Wx-bishekaifayuan11 小时前
express皖美特色农产品网售系统53118-计算机课程设计、毕业设计
人工智能·spring boot·后端·python·课程设计·express·旅游
Wx-bishekaifayuan11 小时前
springboot汉中旅游服务平台41442-计算机课程设计、毕业设计
spring boot·后端·python·django·课程设计·express·旅游
code2cat12 小时前
Java进阶篇之AtomicLongFieldUpdater:把累计额度留在对象字段中
java·cas·并发编程