WEBAPI中使用JWT生成Token和解密Token

一、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
URLhttp://localhost:端口/api/login
Header
结果返回 JWT Token 字符串

5.2 验证 Token

步骤操作
请求方式GET
URLhttp://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...";
    }
}

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注