C#中运用JWT机制进行安全传输和信息认证

一、概念

JWT是目前最流行的跨域认证解决方案,是一种基于Token的认证授权机制,用于在网络上安全传输和验证信息,通常被用于登录验证。

JWT 由三部分组成(详情参考:https://www.jwt.io):
头部(Header) ------Json对象,通常用于声明类型和声明所使用的算法,如:{ "alg": "HS256", "typ": "JWT"}。
载荷(Payload) ------Json对象,包含了具体的用户信息,例如用户ID、用户名、角色、有效时间等,如:{"name":"user","role":"admin","exp":63082282600,"iat":63082281600}。这部分默认是不加密的,一定不要将隐私信息存放其中。
签名(Signature)------它使用Header和Payload中的数据以及一个密钥使用 Header 里面指定的签名算法(默认是 HMAC SHA256)生成签名。签名的目的是保证消息没有被篡改。

这三部分使用(.)进行分隔,形成一个字符串,生成的字符串通常显示为:xxxxxx.yyyyyy.zzzzzz,即:base64编码的Header.base64编码的Payload.签名字符串,也就是<base64UrlEncoded(Header)>.<base64UrlEncoded(Payload)>.<signature>

用户登录时,服务器根据登录信息,通过 Payload、Header 和 Secret创建Token并将 Token 发送给客户端。客户端接收到 Token 之后,会将其保存在 Cookie 或者 localStorage 里面,以后客户端发出的所有请求都会携带这个令牌,服务器端根据令牌进行身份验证,并从中获取用户相关信息。

二、服务器端代码

1、JwtHelper.cs,JWT主文件,包含生成JWTToken方法、验证签名是否有效的方法等

cs 复制代码
using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;
using System.Web.Script.Serialization;

namespace JwtTest.Helpers{
    public class JwtHelper{
        //HMACSHA256加密的密钥
        private static readonly string secretKey = "YourSecretKey";

        /// <summary>生成JWT Token</summary>
        public static string GenerateToken(string userName,string role,int expireMinutes = 120) {
            var header = new { alg = "HS256",typ = "JWT" };         //构建Header
            var payload = new {                                     //构建Payload
                name = userName,                                    //用户名
                role = role,                                        //用户权限
                //签发时间(UTC时间2000年1月1日以来的秒数,long型整数)
                iat=DateTime.Now.ToUniversalTime().Ticks/10000000-63082281600
                //过期时间(签发时间+expireMinutes参数指定的分钟数)
                exp = DateTime.Now.AddMinutes(expireMinutes).ToUniversalTime().Ticks/10000000-63082281600,
            };
            var serializer = new JavaScriptSerializer();
            string headerJson = serializer.Serialize(header);
            string payloadJson=serializer.Serialize(payload);
            //通过自定义的符合JWT标准的Base64Url编码要求的编码解码方法Base64UrlEncode进行编码
            string headerBase64 = Base64UrlEncode(Encoding.UTF8.GetBytes(headerJson));
            string payloadBase64=Base64UrlEncode(Encoding.UTF8.GetBytes(payloadJson));

            string signature = GenereatSignature($"{headerBase64}.{payloadBase64}",secretKey);  //生成签名

            return $"{headerBase64}.{payloadBase64}.{signature}";
        }

        /// <summary>验证Token是否有效</summary>
        public static bool ValidateToken(string token) {
            try {
                string[] parts = token.Split('.');
                if(parts.Length!=3) return false;
                //验证签名
                string headerPayload = $"{parts[0]}.{parts[1]}";
                string signature = GenereatSignature(headerPayload,secretKey);
                if(signature!=parts[2]) return false;
                //验证过期时间
                string payloadJson = Encoding.UTF8.GetString(Base64UrlDecode(parts[1]));
                var payload = new JavaScriptSerializer().Deserialize<Dictionary<string,object>>(payloadJson);

                if(!payload.ContainsKey("exp")) return false;
                double expTime = Convert.ToDouble(payload["exp"]);
                double currentTime = DateTime.Now.ToUniversalTime().Ticks/10000000-62135596800;

                return currentTime<expTime;
            } catch { return false; }

        }

        /// <summary>从Token中解析用户信息</summary>
        public static Dictionary<string,object> GetUserInfoFromToken(string token) {
            try {
                string[] parts = token.Split('.');
                string payloadJson = Encoding.UTF8.GetString(Base64UrlDecode(parts[1]));
                return new JavaScriptSerializer().Deserialize<Dictionary<string,object>>(payloadJson);
            }catch { return null; }
        }

        /// <summary>用符合JWT标准的要求进行编码解码</summary>
        /*
        符合JWT标准的Base64Url编码要求如下:
        将标准Base64编码中的+替换为-,将/替换为_。 ‌
        去掉编码结果末尾的=填充符。 ‌
        对JWT的Header和Payload部分分别进行Base64Url编码。 ‌
‌        编码目的‌:确保编码后的字符串可以在URL、HTTP头等环境中安全传输,避免特殊字符引起解析问题。 ‌
        */
        private static string Base64UrlEncode(byte[] bytes) {
            string base64=Convert.ToBase64String(bytes);
            return base64.Replace('+','-').Replace('/','_').TrimEnd('=');
        }
        private static byte[] Base64UrlDecode(string input) {
            input=input.Replace('-','+').Replace('_','/');
            //base64编码用4位长度表示一个3字节的字符(UTF8编码中,汉字为3字节,字母为1字节),当字符长度不足3字节时,在末尾用=号进行补足
            while(input.Length%4!=0)
                input+="=";
            return Convert.FromBase64String(input);
        }
        
        /// <summary>生成HMACSHA256签名</summary>
        private static string GenereatSignature(string data,string key) {
            using(var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(key))) {
                byte[] hash=hmac.ComputeHash(Encoding.UTF8.GetBytes(data));
                return Base64UrlEncode(hash);
            }
        }

    }
}

2、DefaultController.cs,MVC项目中控制器代码,包含登录验证Login方法(返回JWT Token字符器)和带Token访问的GetUserInfo方法

cs 复制代码
using JwtTest.Helpers;
using System.Web.Mvc;

namespace JwtTest.Controllers{
    public class DefaultController : Controller{

        ///客户端登录成功后,可将返回的JWT Token写入Cookie或localStorage或SessionStorage,以便后续将此作为参数放在Header请求头或使用QueryString参数访问其他页面///
        public ActionResult Login(string userName,string role,string password) {
            if(userName=="username"&&password=="password") {
                string token=JwtHelper.GenerateToken(userName,role,120);
                return Json(new { token },JsonRequestBehavior.AllowGet);
            }
            return new HttpStatusCodeResult(401,"登录失败");
        }

        public ActionResult GetUserInfo() {
            string token = null;
            var authheader = Request.Headers["Authorization"];
            if(!string.IsNullOrEmpty(authheader)&&authheader.StartsWith("Bearer "))
                token=authheader.Substring(7);
            else
                token=Request.QueryString["token"];

            var dic=JwtHelper.GetUserInfoFromToken(token);
            return Json(dic,JsonRequestBehavior.AllowGet);
        }
    }
}

在实际使用中,客户端一般是将token存放在请求头的Authorization,并加上Bearer标注(格式:headers: { Authorization: Bearer <token> })

建议创建认证过滤器Filter,拦截需要权限的接口请求并验证Token,控制器通过 [JwtAuthFilter] 特性标记需要认证的接口,[AllowAnonymous] 标记公开接口,实现权限控制。

相关推荐
星河耀银海1 小时前
远控体验分享:安全与实用性参考
人工智能·安全·微服务
赛博云推-Twitter热门霸屏工具2 小时前
Twitter运营完整流程:从0到引流获客全流程拆解(2026)
运维·安全·自动化·媒体·twitter
xixixi777773 小时前
通信领域的“中国速度”:从5G-A到6G,从地面到星空
人工智能·5g·安全·ai·fpga开发·多模态
CV-杨帆5 小时前
ICLR 2026 LLM安全相关论文整理
人工智能·深度学习·安全
hhh3u3u3u5 小时前
Visual C++ 6.0中文版安装包下载教程及win11安装教程
java·c语言·开发语言·c++·python·c#·vc-1
加号36 小时前
【C#】实现沃德普线光控制器通信控制(附完整源码)
开发语言·c#
byoass6 小时前
csdn_upload_005
网络·安全·云计算
lzhdim7 小时前
SharpCompress:跨平台的 C# 压缩与解压库
开发语言·c#
qq_260241238 小时前
将盾CDN:API安全防护与接口防刷实战策略
安全
星幻元宇VR8 小时前
VR科普行走平台适用哪些科普教育主题
科技·学习·安全·vr·虚拟现实