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

Token相关设计

文章目录

    • 1. 双Token 机制概述
      • 1.1 访问令牌(Access Token)
      • 1.2 刷新令牌(Refresh Token)
    • 2. 双Token 认证流程
    • 3. Spring Boot 具体实现
      • 3.1 生成 Token(使用 JWT)
      • 3.2 解析 Token
      • 3.3 登录接口(返回双 Token)
      • 3.4 刷新 Token 接口
      • 3.5 退出登录
    • 4. 总结 🚀

在微服务架构中,Token 认证是保障系统安全性的重要手段,常见的方式包括 JWT(JSON Web Token)基于 Redis 的 Token 认证。本文将介绍 双Token 机制 及其具体实现。


1. 双Token 机制概述

1.1 访问令牌(Access Token)

  • 用途:前端每次请求携带该令牌,用于身份认证。
  • 有效期:较短(如10分钟 - 2小时)。
  • 存储方式:前端存储(如 LocalStorage、SessionStorage、HTTP Only Cookie)。
  • 信息载荷:用户 ID、权限、过期时间等。

1.2 刷新令牌(Refresh Token)

  • 用途:用于获取新的 Access Token,避免用户频繁登录。
  • 有效期:较长(如7天 - 30天)。
  • 存储方式:数据库或 Redis(不能存储在前端,防止滥用)。
  • 信息载荷:用户 ID,仅用于重新获取 Access Token。

2. 双Token 认证流程

  1. 用户登录

    • 用户提交用户名、密码。
    • 后端校验通过后,生成 Access Token(短期) 和 Refresh Token(长期)。
    • Access Token 通过 JWT 方式返回给前端
    • Refresh Token 存储到数据库或 Redis,避免前端篡改。
  2. 请求 API 资源

    • 前端在每次请求时,携带 Authorization: Bearer <Access Token>
    • 后端解析 Access Token,校验有效性。
    • 通过则返回资源,失败则根据错误码处理(如 401 Unauthorized)。
  3. Token 过期时的处理

    • Access Token 过期,但 Refresh Token 仍有效 :

      • 前端调用 刷新接口,携带 Refresh Token 请求新 Access Token。
      • 后端验证 Refresh Token 后,重新生成 Access Token 并返回。
    • Refresh Token 过期或无效:

      • 需要用户重新登录。
  4. 用户登出

    • 后端删除 Refresh Token 记录。
    • 前端删除 Access Token。

3. Spring Boot 具体实现

3.1 生成 Token(使用 JWT)

import io.jsonwebtoken.*;
import java.util.Date;

public class JwtUtil {
    private static final String SECRET_KEY = "your_secret_key";
    private static final long ACCESS_TOKEN_EXPIRATION = 2 * 60 * 60 * 1000; // 2小时
    private static final long REFRESH_TOKEN_EXPIRATION = 7 * 24 * 60 * 60 * 1000; // 7天

    // 生成 Access Token
    public static String generateAccessToken(String userId) {
        return Jwts.builder()
                .setSubject(userId)
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + ACCESS_TOKEN_EXPIRATION))
                .signWith(SignatureAlgorithm.HS256, SECRET_KEY)
                .compact();
    }

    // 生成 Refresh Token
    public static String generateRefreshToken(String userId) {
        return Jwts.builder()
                .setSubject(userId)
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + REFRESH_TOKEN_EXPIRATION))
                .signWith(SignatureAlgorithm.HS256, SECRET_KEY)
                .compact();
    }
}

3.2 解析 Token

public static Claims parseToken(String token) {
    return Jwts.parser()
            .setSigningKey(SECRET_KEY)
            .parseClaimsJws(token)
            .getBody();
}

3.3 登录接口(返回双 Token)

@RestController
@RequestMapping("/auth")
public class AuthController {
    @PostMapping("/login")
    public Map<String, String> login(@RequestBody LoginRequest request) {
        // 1. 校验用户名和密码(省略)
        
        // 2. 生成 Token
        String accessToken = JwtUtil.generateAccessToken(request.getUsername());
        String refreshToken = JwtUtil.generateRefreshToken(request.getUsername());
        
        // 3. 存储 Refresh Token(示例使用 Redis)
        redisTemplate.opsForValue().set("refresh_token:" + request.getUsername(), refreshToken, 7, TimeUnit.DAYS);
        
        // 4. 返回 Token
        Map<String, String> tokens = new HashMap<>();
        tokens.put("accessToken", accessToken);
        tokens.put("refreshToken", refreshToken);
        return tokens;
    }
}

3.4 刷新 Token 接口

@PostMapping("/refresh")
public ResponseEntity<Map<String, String>> refresh(@RequestHeader("Authorization") String refreshToken) {
    // 1. 校验 Refresh Token
    Claims claims = JwtUtil.parseToken(refreshToken);
    String userId = claims.getSubject();

    // 2. 检查 Redis 是否存储该 Refresh Token
    String storedToken = redisTemplate.opsForValue().get("refresh_token:" + userId);
    if (storedToken == null || !storedToken.equals(refreshToken)) {
        return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
    }

    // 3. 生成新的 Access Token
    String newAccessToken = JwtUtil.generateAccessToken(userId);
    
    // 4. 返回新的 Token
    Map<String, String> tokens = new HashMap<>();
    tokens.put("accessToken", newAccessToken);
    return ResponseEntity.ok(tokens);
}

3.5 退出登录

@PostMapping("/logout")
public ResponseEntity<Void> logout(@RequestHeader("Authorization") String accessToken) {
    Claims claims = JwtUtil.parseToken(accessToken);
    String userId = claims.getSubject();
    
    // 删除 Redis 中的 Refresh Token
    redisTemplate.delete("refresh_token:" + userId);
    return ResponseEntity.ok().build();
}

4. 总结 🚀

机制作用过期时间存储位置
Access Token用于 API 认证短(10分钟-2小时)前端 LocalStorage/SessionStorage
Refresh Token用于刷新 Access Token长(7天-30天)Redis/数据库
  • 双 Token 机制 既保证了安全性(短期有效的 Access Token)又提升了用户体验(长期有效的 Refresh Token)。
  • Access Token 过期时,通过 Refresh Token 重新获取,而无需重新登录。
  • Refresh Token 需要存储在后端(如 Redis),避免前端泄露。

博客主页: 总是学不会.

相关文章:

  • Python之使用动态导包优化软件加载速度
  • Elasticsearch:过滤 HNSW 搜索,快速模式
  • 基于Flask的红袖网小说数据可视化分析系统
  • PostgreSQL中的外键与主键
  • ABAP语言的动态程序
  • ES6笔记总结
  • Vue的data配置项
  • springboot之HTML与图片生成
  • IP属地是通过卫星定位的吗?如何保护用户隐私
  • mysql-analyze table导致waiting for table flush
  • 【AI+智造】在阿里云Ubuntu 24.04上部署DeepSeek R1 14B的完整方案
  • Redis---缓存穿透,雪崩,击穿
  • GNN入门与实践——基于GraphSAGE在Cora数据集上的节点分类研究
  • 思维训练(算法+技巧)
  • 大白话TypeScript第八章TypeScript 项目的部署与监控
  • Kafka零拷贝
  • springcloud组件调用顺序
  • 游戏引擎学习第128天
  • 1-3压缩命令
  • 内存中的缓存区
  • 网站将要准备建设的内容有哪些/游戏推广代理app
  • 云主机怎么安装网站/seo教程最新
  • 市场营销成功案例分析/珠海网站seo
  • 什么网站程序适合做seo/seo如何挖掘关键词
  • 北京顺义去哪找做网站的/重庆网站制作公司哪家好
  • 免费网站排名优化/seo需要什么技术