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

2.創(chuàng)建一個實體 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 獲取實體
/// </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)建一個編碼類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個字符,64位
/// </summary>
private static string PrivateRsa = ConfigurationManager.AppSettings["PrivateRsa"];
/// <summary>
/// //注意了,是8個字符,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)建一個返回消息類
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)建一個控制器用于生產(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();
//假設用戶名為"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請求需要修改成這樣
return Json(result,JsonRequestBehavior.AllowGet);
}
}
}
7.創(chuàng)建一個自定義過濾器
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>
/// 驗證入口
/// </summary>
/// <param name="filterContext"></param>
public override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
}
/// <summary>
/// 驗證核心代碼
/// </summary>
/// <param name="httpContext">fbc8ZBLd5ZbtCogcY9NUVV4HZbPln1lb</param>
/// <returns></returns>
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
//前端請求api時會將token存放在名為"auth"的請求頭中
var authHeader = httpContext.Request.Headers["auth"];
if (authHeader == null)
return false;
//請求參數(shù)
string requestTime = httpContext.Request["rtime"]; //請求時間經(jīng)過DESC簽名
if (string.IsNullOrEmpty(requestTime))
return false;
//模擬生成rtime 時間戳,即登錄的時間,加密. //實際生產(chǎn)中這段代碼應該在請求段。此處只為了程序驗證通過
string r= DESCryption.Encode(DateTime.Now.ToString());
requestTime = r;
//請求時間RSA解密后加上時間戳的時間即該請求的有效時間
DateTime Requestdt = DateTime.Parse(DESCryption.Decode(requestTime)).AddMinutes(int.Parse(TimeStamp));
DateTime Newdt = DateTime.Now; //服務器接收請求的當前時間
if (Requestdt < Newdt)
{
return false;
}
else
{
if (authHeader != null)
{
//進行其他操作
var userinfo = JwtHelp.GetJwtDecode(authHeader);
//舉個例子 生成jwtToken 存入redis中
//這個地方用jwtToken當作key 獲取實體val 然后看看jwtToken根據(jù)redis是否一樣
if (userinfo.UserName == "admin" && userinfo.Pwd == "123")
return true;
}
}
return false;
}
/// <summary>
/// 驗證失敗處理
/// </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.在要需要過濾的控制器方法上添加標簽,標簽就是自定義過濾器名稱。
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.測試獲取token

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

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

以上就是C# 如何實現(xiàn)Token的詳細內(nèi)容,更多關于C# 實現(xiàn)Token的資料請關注腳本之家其它相關文章!
相關文章
unity 如何獲取Text組件里text內(nèi)容的長度
這篇文章主要介紹了unity 獲取Text組件里text內(nèi)容的長度操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-04-04
C#使用IComparer自定義List類實現(xiàn)排序的方法
這篇文章主要介紹了C#使用IComparer自定義List類實現(xiàn)排序的方法,涉及C#使用IComparer接口定義List類進行排序的相關技巧,需要的朋友可以參考下2015-08-08
c#?使用線程對串口serialPort進行收發(fā)數(shù)據(jù)(四種)
本文主要介紹了c#?使用線程對串口serialPort進行收發(fā)數(shù)據(jù),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2022-07-07

