C# 如何實(shí)現(xiàn)Token
什么是JWT
JWT:Json web token (JWT), 是為了在網(wǎng)絡(luò)應(yīng)用環(huán)境間傳遞聲明而執(zhí)行的一種基于JSON的開(kāi)放標(biāo)準(zhǔn)((RFC 7519).該token被設(shè)計(jì)為緊湊且安全的,特別適用于分布式站點(diǎn)的單點(diǎn)登錄(SSO)場(chǎng)景。JWT的聲明一般被用來(lái)在身份提供者和服務(wù)提供者間傳遞被認(rèn)證的用戶身份信息,以便于從資源服務(wù)器獲取資源,也可以增加一些額外的其它業(yè)務(wù)邏輯所必須的聲明信息,該token也可直接被用于認(rèn)證,也可被加密。
傳統(tǒng)的session認(rèn)證
我們知道,http協(xié)議本身是一種無(wú)狀態(tài)的協(xié)議,而這就意味著如果用戶向我們的應(yīng)用提供了用戶名和密碼來(lái)進(jìn)行用戶認(rèn)證,那么下一次請(qǐng)求時(shí),用戶還要再一次進(jìn)行用戶認(rèn)證才行,因?yàn)楦鶕?jù)http協(xié)議,我們并不能知道是哪個(gè)用戶發(fā)出的請(qǐng)求,所以為了讓我們的應(yīng)用能識(shí)別是哪個(gè)用戶發(fā)出的請(qǐng)求,我們只能在服務(wù)器存儲(chǔ)一份用戶登錄的信息,這份登錄信息會(huì)在響應(yīng)時(shí)傳遞給瀏覽器,告訴其保存為cookie,以便下次請(qǐng)求時(shí)發(fā)送給我們的應(yīng)用,這樣我們的應(yīng)用就能識(shí)別請(qǐng)求來(lái)自哪個(gè)用戶了,這就是傳統(tǒng)的基于session認(rèn)證。
但是這種基于session的認(rèn)證使應(yīng)用本身很難得到擴(kuò)展,隨著不同客戶端用戶的增加,獨(dú)立的服務(wù)器已無(wú)法承載更多的用戶,而這時(shí)候基于session認(rèn)證應(yīng)用的問(wèn)題就會(huì)暴露出來(lái).
基于session認(rèn)證所顯露的問(wèn)題
Session: 每個(gè)用戶經(jīng)過(guò)我們的應(yīng)用認(rèn)證之后,我們的應(yīng)用都要在服務(wù)端做一次記錄,以方便用戶下次請(qǐng)求的鑒別,通常而言session都是保存在內(nèi)存中,而隨著認(rèn)證用戶的增多,服務(wù)端的開(kāi)銷(xiāo)會(huì)明顯增大。
擴(kuò)展性: 用戶認(rèn)證之后,服務(wù)端做認(rèn)證記錄,如果認(rèn)證的記錄被保存在內(nèi)存中的話,這意味著用戶下次請(qǐng)求還必須要請(qǐng)求在這臺(tái)服務(wù)器上,這樣才能拿到授權(quán)的資源,這樣在分布式的應(yīng)用上,相應(yīng)的限制了負(fù)載均衡器的能力。這也意味著限制了應(yīng)用的擴(kuò)展能力。
CSRF: 因?yàn)槭腔赾ookie來(lái)進(jìn)行用戶識(shí)別的, cookie如果被截獲,用戶就會(huì)很容易受到跨站請(qǐng)求偽造的攻擊。
基于token的鑒權(quán)機(jī)制
基于token的鑒權(quán)機(jī)制類似于http協(xié)議也是無(wú)狀態(tài)的,它不需要在服務(wù)端去保留用戶的認(rèn)證信息或者會(huì)話信息。這就意味著基于token認(rèn)證機(jī)制的應(yīng)用不需要去考慮用戶在哪一臺(tái)服務(wù)器登錄了,這就為應(yīng)用的擴(kuò)展提供了便利。
流程上是這樣的:
- 用戶使用用戶名密碼來(lái)請(qǐng)求服務(wù)器
- 服務(wù)器進(jìn)行驗(yàn)證用戶的信息
- 服務(wù)器通過(guò)驗(yàn)證發(fā)送給用戶一個(gè)token
- 客戶端存儲(chǔ)token,并在每次請(qǐng)求時(shí)附送上這個(gè)token值
- 服務(wù)端驗(yàn)證token值,并返回?cái)?shù)據(jù)
這個(gè)token必須要在每次請(qǐng)求時(shí)傳遞給服務(wù)端,它應(yīng)該保存在請(qǐng)求頭里, 另外,服務(wù)端要支持CORS(跨來(lái)源資源共享)策略,一般我們?cè)诜?wù)端這么做就可以了Access-Control-Allow-Origin: *。
那么我們現(xiàn)在回到JWT的主題上。
JWT的構(gòu)成
第一部分我們稱它為頭部(header),第二部分我們稱其為載荷(payload, 類似于飛機(jī)上承載的物品),第三部分是簽證(signature).
C# MVC實(shí)現(xiàn)token
1.在NuGet中引用JWT

2.創(chuàng)建一個(gè)實(shí)體 UserInfo類
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace WebApplication1.model
{
public class UserInfo
{
public string UserName { get; set; }
public string Pwd { get; set; }
}
}
3.創(chuàng)建JWT幫助類
using JWT;
using JWT.Algorithms;
using JWT.Serializers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace WebApplication1.model
{
public class JwtHelp
{
//私鑰 web.config中配置
//"GQDstcKsx0NHjPOuXOYg5MbeJ1XT0uFiwDVvVBrk";
private static string secret = "GQDstcKsx0NHjPOuXOYg5MbeJ1XT0uFiwDVvVBrk";
//ConfigurationManager.AppSettings["Secret"].ToString();
/// <summary>
/// 生成JwtToken
/// </summary>
/// <param name="payload">不敏感的用戶數(shù)據(jù)</param>
/// <returns></returns>
public static string SetJwtEncode(Dictionary<string, object> payload)
{
//格式如下
//var payload = new Dictionary<string, object>
//{
// { "username","admin" },
// { "pwd", "claim2-value" }
//};
IJwtAlgorithm algorithm = new HMACSHA256Algorithm();
IJsonSerializer serializer = new JsonNetSerializer();
IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder();
IJwtEncoder encoder = new JwtEncoder(algorithm, serializer, urlEncoder);
var token = encoder.Encode(payload, secret);
return token;
}
/// <summary>
/// 根據(jù)jwtToken 獲取實(shí)體
/// </summary>
/// <param name="token">jwtToken</param>
/// <returns></returns>
public static UserInfo GetJwtDecode(string token)
{
IJsonSerializer serializer = new JsonNetSerializer();
IDateTimeProvider provider = new UtcDateTimeProvider();
IJwtValidator validator = new JwtValidator(serializer, provider);
IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder();
var algorithm = new HMACSHA256Algorithm();
IJwtDecoder decoder = new JwtDecoder(serializer, validator, urlEncoder, algorithm);
var userInfo = decoder.DecodeToObject<UserInfo>(token, secret, verify: true);//token為之前生成的字符串
return userInfo;
}
}
}
4.創(chuàng)建一個(gè)編碼類DESCryption
using System;
using System.Text;
using System.Security.Cryptography;
using System.IO;
using System.Configuration;
namespace JWT.MvcDemo.Help
{
public class DESCryption
{
/// <summary>
/// //注意了,是8個(gè)字符,64位
/// </summary>
private static string PrivateRsa = ConfigurationManager.AppSettings["PrivateRsa"];
/// <summary>
/// //注意了,是8個(gè)字符,64位
/// </summary>
private static string PublicRsa = ConfigurationManager.AppSettings["PublicRsa"];
/// <summary>
/// 加密
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
public static string Encode(string data)
{
byte[] byKey = Encoding.ASCII.GetBytes(PrivateRsa);
byte[] byIV = Encoding.ASCII.GetBytes(PublicRsa);
DESCryptoServiceProvider cryptoProvider = new DESCryptoServiceProvider();
int i = cryptoProvider.KeySize;
MemoryStream ms = new MemoryStream();
CryptoStream cst = new CryptoStream(ms, cryptoProvider.CreateEncryptor(byKey, byIV), CryptoStreamMode.Write);
StreamWriter sw = new StreamWriter(cst);
sw.Write(data);
sw.Flush();
cst.FlushFinalBlock();
sw.Flush();
return Convert.ToBase64String(ms.GetBuffer(), 0, (int)ms.Length);
}
/// <summary>
/// 解密
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
public static string Decode(string data)
{
byte[] byKey = Encoding.ASCII.GetBytes(PrivateRsa);
byte[] byIV = Encoding.ASCII.GetBytes(PublicRsa);
byte[] byEnc;
try
{
byEnc = Convert.FromBase64String(data);
}
catch
{
return null;
}
DESCryptoServiceProvider cryptoProvider = new DESCryptoServiceProvider();
MemoryStream ms = new MemoryStream(byEnc);
CryptoStream cst = new CryptoStream(ms, cryptoProvider.CreateDecryptor(byKey, byIV), CryptoStreamMode.Read);
StreamReader sr = new StreamReader(cst);
return sr.ReadToEnd();
}
}
}
5.創(chuàng)建一個(gè)返回消息類
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace JWT.MvcDemo.Models
{
public class DataResult
{
/// <summary>
///
/// </summary>
public string Token { get; set; }
public bool Success { get; set; }
public string Message { get; set; }
}
}
6.創(chuàng)建一個(gè)控制器用于生產(chǎn)token
using JWT.MvcDemo.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using WebApplication1.model;
namespace WebApplication1.Controllers
{
public class JwtController : Controller
{
// GET: Jwt
public ActionResult Index()
{
return View();
}
/// <summary>
/// 創(chuàng)建jwtToken
/// </summary>
/// <param name="username"></param>
/// <param name="pwd"></param>
/// <returns></returns>
public ActionResult CreateToken(string username, string pwd)
{
DataResult result = new DataResult();
//假設(shè)用戶名為"admin",密碼為"123"
if (username == "admin" && pwd == "123")
{
var payload = new Dictionary<string, object>
{
{ "username",username },
{ "pwd", pwd }
};
result.Token = JwtHelp.SetJwtEncode(payload);
result.Success = true;
result.Message = "成功";
}
else
{
result.Token = "";
result.Success = false;
result.Message = "生成token失敗";
}
//return Json(result);
//get請(qǐng)求需要修改成這樣
return Json(result,JsonRequestBehavior.AllowGet);
}
}
}
7.創(chuàng)建一個(gè)自定義過(guò)濾器
using JWT.MvcDemo.Help;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Security;
using WebApplication1.model;
namespace JWT.MvcDemo.App_Start
{
public class MyAuthorizeAttribute : AuthorizeAttribute
{
private readonly string TimeStamp = ConfigurationManager.AppSettings["TimeStamp"];
/// <summary>
/// 驗(yàn)證入口
/// </summary>
/// <param name="filterContext"></param>
public override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
}
/// <summary>
/// 驗(yàn)證核心代碼
/// </summary>
/// <param name="httpContext">fbc8ZBLd5ZbtCogcY9NUVV4HZbPln1lb</param>
/// <returns></returns>
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
//前端請(qǐng)求api時(shí)會(huì)將token存放在名為"auth"的請(qǐng)求頭中
var authHeader = httpContext.Request.Headers["auth"];
if (authHeader == null)
return false;
//請(qǐng)求參數(shù)
string requestTime = httpContext.Request["rtime"]; //請(qǐng)求時(shí)間經(jīng)過(guò)DESC簽名
if (string.IsNullOrEmpty(requestTime))
return false;
//模擬生成rtime 時(shí)間戳,即登錄的時(shí)間,加密. //實(shí)際生產(chǎn)中這段代碼應(yīng)該在請(qǐng)求段。此處只為了程序驗(yàn)證通過(guò)
string r= DESCryption.Encode(DateTime.Now.ToString());
requestTime = r;
//請(qǐng)求時(shí)間RSA解密后加上時(shí)間戳的時(shí)間即該請(qǐng)求的有效時(shí)間
DateTime Requestdt = DateTime.Parse(DESCryption.Decode(requestTime)).AddMinutes(int.Parse(TimeStamp));
DateTime Newdt = DateTime.Now; //服務(wù)器接收請(qǐng)求的當(dāng)前時(shí)間
if (Requestdt < Newdt)
{
return false;
}
else
{
if (authHeader != null)
{
//進(jìn)行其他操作
var userinfo = JwtHelp.GetJwtDecode(authHeader);
//舉個(gè)例子 生成jwtToken 存入redis中
//這個(gè)地方用jwtToken當(dāng)作key 獲取實(shí)體val 然后看看jwtToken根據(jù)redis是否一樣
if (userinfo.UserName == "admin" && userinfo.Pwd == "123")
return true;
}
}
return false;
}
/// <summary>
/// 驗(yàn)證失敗處理
/// </summary>
/// <param name="filterContext"></param>
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
base.HandleUnauthorizedRequest(filterContext);
filterContext.Result = new RedirectResult("/Error");
filterContext.HttpContext.Response.Redirect("/Home/Error");
}
}
}
8.在要需要過(guò)濾的控制器方法上添加標(biāo)簽,標(biāo)簽就是自定義過(guò)濾器名稱。
using JWT.MvcDemo.App_Start;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace WebApplication1.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
[HttpPost]
[MyAuthorize]
public string About()
{
string rtJson = "{\"code\": 0}";
try
{
rtJson = "{\"code\":0,\"data\":[],\"msg\":\"Your application description page.\",\"count\":1}";
}
catch
{
rtJson = "{\"code\": 0}";
}
return rtJson;
}
public ActionResult Contact()
{
ViewBag.Message = "Your contact page.";
return View();
}
}
}
9.測(cè)試獲取token

10.客戶端將token放入header中達(dá)到攜帶token目的。

11.需要在web.config 中添加設(shè)置值
<add key="Secret" value="GQDstcKsx0NHjPOuXOYg5MbeJ1XT0uFiwDVvVBrk"/> <add key="PrivateRsa" value="GQDstcKs"/> <add key="PublicRsa" value="DVvVBrkx0"/> <add key="TimeStamp" value="2"/>

以上就是C# 如何實(shí)現(xiàn)Token的詳細(xì)內(nèi)容,更多關(guān)于C# 實(shí)現(xiàn)Token的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- c# 如何對(duì)網(wǎng)絡(luò)信息進(jìn)行相關(guān)設(shè)置(ip,dns,網(wǎng)關(guān)等)
- C# 文件安全管理需要注意的
- c# 如何自己實(shí)現(xiàn)一個(gè)ORM框架
- c# 如何實(shí)現(xiàn)自動(dòng)更新程序
- c# 使用WebRequest實(shí)現(xiàn)多文件上傳
- c# WPF中的TreeView使用詳解
- C# Winform 實(shí)現(xiàn)TCP發(fā)消息
- c# 用ffmpeg從視頻中截圖
- c# wpf如何使用Blend工具繪制Control樣式
- c# 圓形識(shí)別方案和直線識(shí)別方案的參考示例
- c# 在windows中操作IIS設(shè)置FTP服務(wù)器的示例
- 使用 BenchmarkDotNet 對(duì) C# 代碼進(jìn)行基準(zhǔn)測(cè)試
相關(guān)文章
C#實(shí)現(xiàn)驗(yàn)證身份證是否合法的方法
這篇文章主要介紹了C#實(shí)現(xiàn)驗(yàn)證身份證是否合法的方法,實(shí)例分析了通過(guò)自定義函數(shù)實(shí)現(xiàn)針對(duì)身份證合法性驗(yàn)證的技巧,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2015-03-03
unity 如何獲取Text組件里text內(nèi)容的長(zhǎng)度
這篇文章主要介紹了unity 獲取Text組件里text內(nèi)容的長(zhǎng)度操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-04-04
WPF實(shí)現(xiàn)控件拖動(dòng)的示例代碼
這篇文章主要介紹了WPF實(shí)現(xiàn)控件拖動(dòng)的示例代碼,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-08-08
C#使用IComparer自定義List類實(shí)現(xiàn)排序的方法
這篇文章主要介紹了C#使用IComparer自定義List類實(shí)現(xiàn)排序的方法,涉及C#使用IComparer接口定義List類進(jìn)行排序的相關(guān)技巧,需要的朋友可以參考下2015-08-08
c#?使用線程對(duì)串口serialPort進(jìn)行收發(fā)數(shù)據(jù)(四種)
本文主要介紹了c#?使用線程對(duì)串口serialPort進(jìn)行收發(fā)數(shù)據(jù),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-07-07

