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;
        }
    }
}
相关推荐
乔旭伟4 小时前
中小厂生产管理系统选型笔记:驱动方式、主线链路与成本结构
笔记·需求分析·规格说明书
xier_ran4 小时前
【infra之路】AI 编译器学习笔记(十四):为什么要 tiling,以及矩阵计算单元的“固定“
人工智能·笔记·学习
燕落南枝4 小时前
HTML 学习:辅助标签 label 与字符实体
前端·学习·html
hackchen5 小时前
LangChain 学习第 3 课 - 输出解析器(Output Parser)
数据库·学习·langchain
如旧呀5 小时前
origin绘制等距的HSD置信区间
笔记·origin
问心无愧05135 小时前
[极客大挑战 2019]Upload
笔记
wangguanghou15 小时前
【学习成长】深入解析721学习成长模型:70-20-10法则的实践指南与避坑手册
学习
其实防守也摸鱼5 小时前
Linux文本处理命令理论学习
linux·服务器·前端·数据库·sql·学习·安全
dadaobusi5 小时前
学习: Triton
学习
一条泥憨鱼6 小时前
【计算机网络】| 从 MAC 到 NAT 的跨网段通信全链路
服务器·网络·学习·计算机网络·macos