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;
        }
    }
}
相关推荐
疯狂打码的少年39 分钟前
【数据结构】图的存储结构:邻接矩阵与邻接表
数据结构·笔记
蒸蒸yyyyzwd1 小时前
cpp 选手准备秋招学习笔记 day10
笔记·面试·求职招聘
haerapicom2 小时前
欠定方程里藏着哪几个非零项:OMP 稀疏恢复侦探笔记
笔记
程思扬2 小时前
Android ANR实战排查:主线程SystemClock.sleep导致页面启动偶现无响应
android·笔记
zjnlswd3 小时前
C#学习笔记4
笔记·学习·c#
minglie13 小时前
在linux环境烧录esp8266
学习
xqqxqxxq4 小时前
AI Agent学习:MCP与工具生态:工具选择的挑战(李博杰《深入理解 AI Agent》4.3观后总结)
人工智能·学习
青山是哪个青山4 小时前
LangChain 学习笔记(九):上下文与记忆
笔记·学习·langchain
KaKa_大王5 小时前
关于秒杀项目的一些理解
java·学习
Z5998178416 小时前
c#软件开发学习笔记--Modbus-TCP/UDP网口通讯
笔记·学习·c#