Day22:JWT 完整学习笔记 + 原理 + 面试题 + 帮助类封装

一、JWT 是什么

JWT(JSON Web Token) :是一种无状态 的身份认证令牌,登录成功后后端生成一段加密字符串,前端后续请求带 Token 鉴权,不用再每次查库校验账号密码。

二、JWT 三段结构(用 . 分隔)

  1. Header 头部加密算法、令牌类型,默认 HS256 对称加密。
  2. Payload 载荷 存放用户非敏感信息 :用户 ID、账号、角色、过期时间等。禁止存密码、隐私数据
  3. Signature 签名 头部 + 载荷 + 密钥 加密生成,防止 Token 被篡改

三、JWT 认证流程

  1. 前端登录提交账号密码
  2. 后端校验账号密码正确
  3. 后端生成 JWT Token 返回前端
  4. 前端把 Token 存在本地(LocalStorage)
  5. 后续每次请求 Header 携带 Token
  6. 后端拦截器解析、验签、判断是否过期、是否合法
  7. 合法则放行接口,无 Token / 非法 / 过期直接 401

四、JWT 优缺点

优点

  1. 无状态:服务端不用存 Session,分布式、集群友好
  2. 跨域友好:适合前后端分离
  3. 自带信息:Payload 可带用户角色、ID,减少查库
  4. 扩展性强:微服务、网关鉴权通用

缺点

  1. 一旦签发,中途无法作废(只能等过期,需配合黑名单做注销)
  2. Payload Base64 解码就能看,不能存敏感数据
  3. Token 不宜过长,携带太多字段影响请求头大小

五、适用场景

  • 前后端分离项目登录认证
  • 微服务、分布式系统鉴权
  • 第三方授权登录
  • 移动端 App 登录认证

六、JWT 高频面试题(必背)

  1. JWT 由哪几部分组成?

    Header 头部、Payload 载荷、Signature 签名三部分,用点分隔。

  2. 为什么 JWT 不能存敏感信息?

    Payload 只是 Base64 编码,可直接解码查看,不是加密,不能存密码、手机号等隐私。

  3. JWT 是有状态还是无状态?

    无状态,服务端不存储 Token 信息,靠签名校验合法性,适合集群分布式。

  4. JWT 怎么防止篡改?

    后端用密钥对 Header+Payload 生成签名,篡改任意部分签名都会校验失败。

  5. JWT 过期了怎么处理?

    前端捕获 401,跳转登录;企业常用双 Token:AccessToken 短期 + RefreshToken 无感刷新。

  6. JWT 如何实现注销?

    原生无法主动作废,解决方案:

    1. 维护 Token 黑名单
    2. 缩短过期时间 + 强制重新登录
    3. 更换全局密钥批量失效
  7. 对称加密和非对称加密区别?

    1. 对称 HS256:同一密钥加解密,简单适合单体项目
    2. 非对称 RSA:私钥签发、公钥验签,适合微服务跨项目

七、实战练习:封装 JWT 帮助类(.NET 可用直接复制)

1. 安装 NuGet

复制代码
Microsoft.IdentityModel.Tokens
System.IdentityModel.Tokens.Jwt

2. 配置节点(appsettings.json)

cs 复制代码
"Jwt": {
  "Secret": "ABCDEFG123456789ABCDEFG123456789",
  "Issuer": "AdminWeb",
  "Audience": "AdminClient",
  "ExpireMinutes": 120
}

3. 封装 JwtHelper 帮助类

新建 Common/Helper/JwtHelper.cs

cs 复制代码
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using Microsoft.IdentityModel.Tokens;

namespace Admin.NET.Common.Helper;

public static class JwtHelper
{
    /// <summary>
    /// 生成Token
    /// </summary>
    /// <param name="userId">用户ID</param>
    /// <param name="account">账号</param>
    /// <param name="role">角色</param>
    /// <param name="secret">密钥</param>
    /// <param name="issuer">签发人</param>
    /// <param name="audience">受众</param>
    /// <param name="expireMin">过期分钟</param>
    /// <returns>Token</returns>
    public static string GenerateToken(long userId, string account, string role,
        string secret, string issuer, string audience, int expireMin)
    {
        // 1. 声明载荷
        var claims = new List<Claim>
        {
            new Claim(ClaimTypes.NameIdentifier, userId.ToString()),
            new Claim(ClaimTypes.Name, account),
            new Claim(ClaimTypes.Role, role)
        };

        // 2. 密钥
        var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secret));
        var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

        // 3. 生成Token
        var tokenDescriptor = new SecurityTokenDescriptor
        {
            Issuer = issuer,
            Audience = audience,
            Subject = new ClaimsIdentity(claims),
            Expires = DateTime.UtcNow.AddMinutes(expireMin),
            SigningCredentials = creds
        };

        var handler = new JwtSecurityTokenHandler();
        var securityToken = handler.CreateToken(tokenDescriptor);
        return handler.WriteToken(securityToken);
    }

    /// <summary>
    /// 校验Token
    /// </summary>
    public static bool ValidateToken(string token, string secret, string issuer, string audience, out ClaimsPrincipal? claims)
    {
        claims = null;
        try
        {
            var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secret));
            var validateParam = new TokenValidationParameters
            {
                ValidateIssuer = true,
                ValidateAudience = true,
                ValidateLifetime = true,
                ValidateIssuerSigningKey = true,
                ValidIssuer = issuer,
                ValidAudience = audience,
                IssuerSigningKey = key,
                ClockSkew = TimeSpan.Zero
            };

            var handler = new JwtSecurityTokenHandler();
            claims = handler.ValidateToken(token, validateParam, out _);
            return true;
        }
        catch
        {
            return false;
        }
    }
}
相关推荐
余生皆假期-1 小时前
YuanHub 源码分析【六】MIT 模式
笔记·单片机·嵌入式硬件
05候补工程师2 小时前
[408考研笔记] 传输层与网络层核心辨析:从逻辑通信到滑动窗口计算
网络·经验分享·笔记·网络协议·tcp/ip·考研·ip
w2018002 小时前
一至六年级数学下册第二单元测试卷(人教版+北师版+西师版+苏教版+青岛版)2026
笔记
~黄夫人~2 小时前
Kubernetes 入门到实战:概念详解 + kubeadm 安装 + 节点克隆全流程
linux·运维·学习·k8s·集群
1104.北光c°2 小时前
Leetcode215 三种写法完成数组中的第K个最大元素 【hot100算法个人笔记】【java写法】
java·笔记·程序人生·算法·leetcode·排序算法·快速选择
SZUWelclose2 小时前
论文格式——如何设置目录,目录右侧怎么对齐
经验分享·笔记·课程设计
_李小白2 小时前
【android opencv学习笔记】Day 12: HSV 色彩空间
android·opencv·学习
sheeta19983 小时前
苍穹外卖Day10笔记
笔记
南斯拉夫的铁托3 小时前
YOLO学习笔记
笔记·学习·yolo