一、JWT 简介
JWT(JSON Web Token)是一种开放标准(RFC 7519),用于在各方之间以 JSON 对象安全地传输信息。信息经过数字签名,可被验证和信任。
JWT 的结构
JWT 由三部分组成,用 . 分隔:
Header.Payload.SignatureCode language: CSS (css)
| 部分 | 说明 |
|---|---|
| Header | 头部,声明类型(JWT)和签名算法(如 HMAC SHA256) |
| Payload | 载荷,存放声明(claims),未加密,仅 Base64 编码 |
| Signature | 签名,用于验证消息未被篡改 |
重要说明:Payload 部分可以存放自定义字段,不局限于官方定义的 7 个标准字段(iss、sub、aud、exp、nbf、iat、jti)。但由于 Payload 未加密,任何人都可以解码读取,切勿存放密码、身份证号等私密数据。
二、安装 JWT 组件
方式一:NuGet 管理控制台
Install-Package JWT -Version 3.0.0Code language: CSS (css)
方式二:NuGet 包管理器搜索
在 Visual Studio 中打开 NuGet 包管理器,搜索 JWT,选择版本 3.0.0 安装。
安装完成后,项目中将引用以下命名空间:
using JWT;
using JWT.Algorithms;
using JWT.Serializers;
using JWT.Encoders;
using JWT.Decoders;
using JWT.Exceptions;Code language: CSS (css)
三、生成 Token(登录接口)
3.1 登录 API 代码
using System;
using System.Collections.Generic;
using System.Web.Http;
using JWT;
using JWT.Algorithms;
using JWT.Serializers;
using JWT.Encoders;
public class LoginController : ApiController
{
// 密钥(实际项目中应从配置文件读取,且足够复杂)
private string secret = "bamn.cn";
/// <summary>
/// 用户登录,生成并返回 JWT Token
/// </summary>
public string Get()
{
// 获取当前 UTC 时间
IDateTimeProvider provider = new UtcDateTimeProvider();
var now = provider.GetNow();
// Unix 时间戳起点
var unixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
var secondsSinceEpoch = Math.Round((now - unixEpoch).TotalSeconds);
// Payload:存放自定义声明
var payload = new Dictionary<string, object>
{
{ "name", "MrBug" }, // 自定义字段:用户名
{ "exp", secondsSinceEpoch + 100000 }, // 标准字段:过期时间(秒)
{ "jti", "luozhipeng" } // 标准字段:JWT ID(唯一标识)
};
Console.WriteLine("当前时间戳: " + secondsSinceEpoch);
// 创建 JWT 编码器
IJwtAlgorithm algorithm = new HMACSHA256Algorithm();
IJsonSerializer serializer = new JsonNetSerializer();
IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder();
IJwtEncoder encoder = new JwtEncoder(algorithm, serializer, urlEncoder);
// 生成 Token
var token = encoder.Encode(payload, secret);
return token;
}
}Code language: HTML, XML (xml)
3.2 生成结果示例
调用 GET /api/login 返回的 Token 字符串:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiTXJCdWciLCJleHAiOjE1NDQyOTM4NDUuMCwianRpIjoibHVvemhpcGVuZyJ9.Nj-y6VSvfHgqvuaYTmN03yOQzRPv3juWWejSbZRxp04Code language: CSS (css)
3.3 Payload 各部分说明
| 字段 | 类型 | 说明 |
|---|---|---|
name | 自定义 | 用户标识 |
exp | 标准 | 过期时间(Unix 时间戳,秒),verify: true 时自动校验 |
jti | 标准 | JWT 唯一 ID,可用于防重放攻击 |
四、验证/解密 Token
4.1 解密验证 API 代码
using System;
using System.Web.Http;
using JWT;
using JWT.Serializers;
using JWT.Exceptions;
public class ValidController : ApiController
{
private string secret = "bamn.cn";
/// <summary>
/// 验证并解密 JWT Token
/// </summary>
public string Get()
{
try
{
IJsonSerializer serializer = new JsonNetSerializer();
IDateTimeProvider provider = new UtcDateTimeProvider();
IJwtValidator validator = new JwtValidator(serializer, provider);
IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder();
IJwtDecoder decoder = new JwtDecoder(serializer, validator, urlEncoder);
// Decode 方法:解密并验证签名
// 参数1:Token 字符串
// 参数2:密钥
// 参数3:verify = true 表示验证签名和过期时间
var json = decoder.Decode(
"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiTXJCdWciLCJleHAiOjE1NDQyOTM4NDUuMCwianRpIjoibHVvemhpcGVuZyJ9.Nj-y6VSvfHgqvuaYTmN03yOQzRPv3juWWejSbZRxp04",
secret,
verify: true
);
// json 为 Payload 的 JSON 字符串
// 如:{"name":"MrBug","exp":1544293845.0,"jti":"luozhipeng"}
return json;
}
catch (TokenExpiredException)
{
// Token 已过期
return "Token has expired";
}
catch (SignatureVerificationException)
{
// 签名验证失败(Token 被篡改或密钥不匹配)
return "Token has invalid signature";
}
}
}Code language: HTML, XML (xml)
4.2 解密结果
调用 GET /api/valid 返回 Payload 的 JSON 字符串:
{"name":"MrBug","exp":1544293845.0,"jti":"luozhipeng"}Code language: JSON / JSON with Comments (json)
五、使用 Postman 测试
5.1 生成 Token
| 步骤 | 操作 |
|---|---|
| 请求方式 | GET |
| URL | http://localhost:端口/api/login |
| Header | 无 |
| 结果 | 返回 JWT Token 字符串 |
5.2 验证 Token
| 步骤 | 操作 |
|---|---|
| 请求方式 | GET |
| URL | http://localhost:端口/api/valid |
| Header | 无(Token 硬编码在代码中用于演示) |
| 结果 | 返回解密后的 JSON Payload |
5.3 实际项目中的验证流程
实际项目中,Token 应从 HTTP Header 中读取并验证:
public class ValidController : ApiController
{
private string secret = "bamn.cn";
public object Get()
{
try
{
// 从 Header 获取 Token
var token = System.Web.HttpContext.Current.Request.Headers["Token"];
IJsonSerializer serializer = new JsonNetSerializer();
IDateTimeProvider provider = new UtcDateTimeProvider();
IJwtValidator validator = new JwtValidator(serializer, provider);
IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder();
IJwtDecoder decoder = new JwtDecoder(serializer, validator, urlEncoder);
var json = decoder.Decode(token, secret, verify: true);
return new
{
Success = true,
Data = json
};
}
catch (TokenExpiredException)
{
return new { Success = false, Message = "Token 已过期" };
}
catch (SignatureVerificationException)
{
return new { Success = false, Message = "Token 签名无效" };
}
}
}
六、结合自定义 AuthorizeAttribute 使用 JWT
将上节课的 TokenAuthAttribute 改造为 JWT 验证:
using System;
using System.Linq;
using System.Web;
using System.Web.Http;
using System.Web.Http.Controllers;
using JWT;
using JWT.Serializers;
using JWT.Exceptions;
namespace Demo.App_Start
{
public class JwtAuthAttribute : AuthorizeAttribute
{
private readonly string _secret = "bamn.cn";
public override void OnAuthorization(HttpActionContext actionContext)
{
var content = actionContext.Request.Properties["MS_HttpContext"] as HttpContextBase;
var token = content.Request.Headers["Token"];
if (!string.IsNullOrEmpty(token))
{
try
{
IJsonSerializer serializer = new JsonNetSerializer();
IDateTimeProvider provider = new UtcDateTimeProvider();
IJwtValidator validator = new JwtValidator(serializer, provider);
IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder();
IJwtDecoder decoder = new JwtDecoder(serializer, validator, urlEncoder);
// 验证并解密
var payloadJson = decoder.Decode(token, _secret, verify: true);
// 验证通过
base.IsAuthorized(actionContext);
}
catch (TokenExpiredException)
{
HandleUnauthorizedRequest(actionContext);
}
catch (SignatureVerificationException)
{
HandleUnauthorizedRequest(actionContext);
}
catch (Exception)
{
HandleUnauthorizedRequest(actionContext);
}
}
else
{
// 检查是否允许匿名
var attributes = actionContext.ActionDescriptor
.GetCustomAttributes<AllowAnonymousAttribute>()
.OfType<AllowAnonymousAttribute>();
bool isAnonymous = attributes.Any(a => a is AllowAnonymousAttribute);
if (isAnonymous)
{
base.OnAuthorization(actionContext);
}
else
{
HandleUnauthorizedRequest(actionContext);
}
}
}
}
}Code language: HTML, XML (xml)
在 Controller 中使用
using Demo.App_Start;
using System.Web.Http;
using System.Web.Mvc;
public class ValuesController : ApiController
{
[JwtAuth]
public string Get(int id)
{
return "认证通过,返回数据: " + id;
}
[AllowAnonymous]
public string Login()
{
// 登录逻辑,返回 Token
return "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...";
}
}
Previous: WEBAPI加入身份认证