JWT详解以及在spirngboot中的应用

JWT(JSON Web Token) 是一种用于身份验证和信息传递的轻量级安全协议,常用于用户认证、权限控制信息安全传输

1. JWT 的基本结构

JWT 是一个字符串,由 三部分 组成:Header(头部)、Payload(载荷)、Signature(签名)

2. JWT 结构解析

(1) Header(头部)

Header 主要包含两部分信息:

  • alg(算法):签名算法(如 HS256
  • typ(类型):通常是 "JWT"

(2) Payload(载荷)

Payload 主要包含:

  • 声明(Claims):JWT 存储的用户信息,如 userIdrole 等。
  • 标准声明
    • iss(Issuer):签发者
    • sub(Subject):主题
    • aud(Audience):接收者
    • exp(Expiration Time):过期时间(Unix 时间戳)
    • iat(Issued At):签发时间
    • nbf(Not Before):此时间之前不可用

(3) Signature(签名)

签名用于防止 JWT 被篡改

HMACSHA256(
  base64UrlEncode(header) + "." + base64UrlEncode(payload),
  secret
)

其中 secret 是服务器端的私钥,必须妥善保管

3. JWT 认证流程

✅ 登录时

  1. 用户输入用户名 + 密码,向服务器发起请求
  2. 服务器验证用户名和密码是否正确
  3. 服务器生成 JWT,返回给前端
  4. 前端将 JWT 存储在 localStoragesessionStorage

✅ 访问接口

  1. 前端请求接口时,将 JWT 放入 Authorization 头部:
  2. 服务器验证 JWT 的 签名和过期时间
  3. 验证通过,返回数据;否则,返回 401 Unauthorized

4. JWT 在 Spring Boot 中的使用

(1) 添加依赖

pom.xml 添加 jjwt 依赖:


    io.jsonwebtoken
    jjwt
    0.11.5

(2)配置jwt

package com.sky.properties;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Component
@ConfigurationProperties(prefix = "sky.jwt")
@Data
public class JwtProperties {

    /**
     * 管理端员工生成jwt令牌相关配置
     */
    private String adminSecretKey;
    private long adminTtl;
    private String adminTokenName;

    /**
     * 用户端微信用户生成jwt令牌相关配置
     */
    private String userSecretKey;
    private long userTtl;
    private String userTokenName;

}

(3)生成与解析jwt

public static String createJWT(String secretKey, long ttlMillis, Map claims) {
        // 指定签名的时候使用的签名算法,也就是header那部分
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;

        // 生成JWT的时间
        long expMillis = System.currentTimeMillis() + ttlMillis;
        Date exp = new Date(expMillis);

        // 设置jwt的body
        JwtBuilder builder = Jwts.builder()
                // 如果有私有声明,一定要先设置这个自己创建的私有的声明,这个是给builder的claim赋值,一旦写在标准的声明赋值之后,就是覆盖了那些标准的声明的
                .setClaims(claims)
                // 设置签名使用的签名算法和签名使用的秘钥
                .signWith(signatureAlgorithm, secretKey.getBytes(StandardCharsets.UTF_8))
                // 设置过期时间
                .setExpiration(exp);

        return builder.compact();// 生成签名
    }

    /**
     * Token解密
     *
     * @param secretKey jwt秘钥 此秘钥一定要保留好在服务端, 不能暴露出去, 否则sign就可以被伪造, 如果对接多个客户端建议改造成多个
     * @param token     加密后的token
     * @return
     */
    public static Claims parseJWT(String secretKey, String token) {
        // 得到DefaultJwtParser
        Claims claims = Jwts.parser()
                // 设置签名的秘钥
                .setSigningKey(secretKey.getBytes(StandardCharsets.UTF_8))
                // 设置需要解析的jwt
                .parseClaimsJws(token).getBody();
        return claims;
    }

}

(4)jwt拦截器


/**
 * jwt令牌校验的拦截器
 */
@Component
@Slf4j
public class JwtTokenAdminInterceptor implements HandlerInterceptor {

    @Autowired
    private JwtProperties jwtProperties;

    /**
     * 校验jwt
     *
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        // 判断当前拦截到的是Controller的方法还是其他资源
        if (!(handler instanceof HandlerMethod)) {
            // 当前拦截到的不是动态方法,直接放行
            return true;
        }

        // 1、从请求头中获取令牌
        String token = request.getHeader(jwtProperties.getAdminTokenName());

        // 2、校验令牌
        try {
            log.info("jwt校验:{}", token);
            Claims claims = JwtUtil.parseJWT(jwtProperties.getAdminSecretKey(), token);
            Long empId = Long.valueOf(claims.get(JwtClaimsConstant.EMP_ID).toString());
            BaseContext.setCurrentId(empId);
            // 3、通过,放行
            return true;
        } catch (Exception ex) {
            // 4、不通过,响应401状态码
            response.setStatus(401);
            return false;
        }
    }
}

5.JWT 拦截器的具体作用

  1. 拦截所有需要认证的请求

    • 只有通过身份验证的用户才能访问受保护的 API,未认证的用户直接返回 401 Unauthorized
  2. 从请求头中提取 JWT

    • 解析 Authorization 头部中的 Bearer ,获取 JWT 令牌。
  3. 验证 JWT 的有效性

    • 校验 JWT 的签名,确保令牌未被篡改。
    • 检查 JWT 是否过期,防止使用过期的令牌访问 API。
  4. 解析 JWT 并提取用户信息

    • 解析 JWT 获取用户 ID、角色权限等信息,并存入 request 作用域,以便后续 Controller 使用。
  5. 拒绝非法请求

    • JWT 无效 / 过期:返回 401 Unauthorized
    • 权限不足:返回 403 Forbidden

6. JWT 的优缺点

✅ 优点

  1. 无状态:无需存储用户信息(不像 session 依赖服务器存储)。
  2. 高效:前端携带 JWT 访问接口,减少数据库查询。
  3. 跨平台:支持 Web、移动端、小程序等多种客户端。
  4. 安全性:签名保证 JWT 不被篡改(但不能防止被盗)。

❌ 缺点

  1. 不能撤销:JWT 生成后,无法手动注销(可使用黑名单)。
  2. 体积较大:比传统 session ID 大,占用带宽。
  3. 存储安全问题:若保存在 localStorage,可能被 XSS 攻击窃取。

总结

JWT 适合微服务、移动端、RESTful API 的用户身份验证
推荐后端使用 Spring Boot + JWT 进行认证
JWT 不适用于频繁变更权限的系统(需结合 Redis 方案)

你可能感兴趣的:(状态模式)