更轻易地实现 Jwt Token
更輕易地實現(xiàn)一個 Jwt Server
Intro
最近在多個項目中都有用到 Jwt Token 認證,就想著把之前項目里 Jwt Token 的使用封裝一下,以便于之后集成起來更加地方便,不用再拷貝代碼了
JWT
JWT 是 JSON Web Token 的縮寫,是目前最流行的基于 Token 的認證解決方案,JWT 是一種無狀態(tài)的認證方式,我們不需要依賴 Session,更為簡單,對于隨時準備擴容縮容的云原生應用來說更加的友好。
JWT token 的格式分成三個部分,他們之間以 “.” 作為分隔
Header(頭部)
Payload(負載)
Signature(簽名)
我們可以在 https://jwt.io 網(wǎng)站上查看 token 的內(nèi)容,也可以自己寫個小工具來查看,token 的內(nèi)容是 base64 URL 編碼的
JWT 作為一個令牌(token),有些場合可能會放到 URL(比如 api.example.com/?token=xxx)。Base64 有三個字符+、/和=,在 URL 里面有特殊含義,所以要被替換掉:=被省略、+替換成-,/替換成_ 。這就是 Base64URL 算法。
JWT Token 內(nèi)容解析之后如下所示:
Header 是 token 所用到的簽名算法信息,默認是 HMAC SHA 256(HS256) 以及 RSA SHA 256(RS256)
Payload 就是我們 token 中保存的信息,這些信息是可以解析成明文的,所以內(nèi)容上不能保存敏感信息,只能保存一些敏感度不高的信息
Signature 是基于 header 和 payload 生成出來的,例如使用 HMAC SHA 256 簽名
HMACSHA256(base64UrlEncode(header)?+?"."?+base64UrlEncode(payload),secret)Sample
介紹了一些基本知識,我們再來看示例吧
首先需要集成 WeihanLi.Web.Extensions 這個 NuGet 包
我們只需要注冊服務即可:
services.AddJwtTokenService(options?=> {options.SecretKey?=?Guid.NewGuid().ToString();options.Issuer?=?"https://id.weihanli.xyz";options.Audience?=?"SparkTodo"; });SecretKey 是用來生成 token 和 token 簽名校驗的,默認簽名方式 是 HMAC SHA256
Issuer 是簽發(fā)人,是指定誰頒發(fā)的 token
Audience 是受眾,是指 token 是給誰用的
注冊好服務之后,我們就可以從依賴注入服務中獲取 ITokenService 來進行 token 的操作了
[HttpGet("getToken")] public?async?Task<IActionResult>?GetToken([Required]?string?userName,?[FromServices]?ITokenService?tokenService) {var?token?=?await?tokenService.GenerateToken(new?Claim("name",?userName));return?token.WrapResult().GetRestResult(); }[HttpGet("validateToken")] public?async?Task<IActionResult>?ValidateToken(string?token,?[FromServices]?ITokenService?tokenService) {return?await?tokenService.ValidateToken(token).ContinueWith(r?=>r.Result.WrapResult().GetRestResult()); }GetToken:
GetTokenVaidateToken:
ValidateTokenImplement
我們來看一些實現(xiàn)細節(jié)
public?class?JwtTokenService?:?ITokenService {private?readonly?JwtSecurityTokenHandler?_tokenHandler?=?new();private?readonly?JwtTokenOptions?_tokenOptions;private?readonly?Lazy<TokenValidationParameters>_lazyTokenValidationParameters;public?JwtTokenService(IOptions<JwtTokenOptions>?tokenOptions){_tokenOptions?=?tokenOptions.Value;_lazyTokenValidationParameters?=?new(()?=>_tokenOptions.GetTokenValidationParameters());}public?virtual?Task<TokenEntity>?GenerateToken(params?Claim[]?claims)=>?GenerateTokenInternal(claims);public?virtual?Task<TokenValidationResult>?ValidateToken(string?token){return?_tokenHandler.ValidateTokenAsync(token,?_lazyTokenValidationParameters.Value);}private?async?Task<TokenEntity>?GenerateTokenInternal(bool?refreshToken,?Claim[]?claims){var?now?=?DateTimeOffset.UtcNow;var?claimList?=?new?List<Claim>(){new?(JwtRegisteredClaimNames.Iat,?now.ToUnixTimeMilliseconds().ToString(),?ClaimValueTypes.Integer64)};if?(claims?!=?null){claimList.AddRange(claims);}var?jti?=?claimList.FirstOrDefault(c?=>?c.Type?==?JwtRegisteredClaimNames.Jti)?.Value;if?(jti?is?null){jti?=?_tokenOptions.JtiGenerator?.Invoke()????GuidIdGenerator.Instance.NewId();claimList.Add(new(JwtRegisteredClaimNames.Jti,?jti));}var?jwt?=?new?JwtSecurityToken(issuer:?_tokenOptions.Issuer,audience:?_tokenOptions.Audience,claims:?claimList,notBefore:?now.UtcDateTime,expires:?now.Add(_tokenOptions.ValidFor).UtcDateTime,signingCredentials:?_tokenOptions.SigningCredentials);var?encodedJwt?=?_tokenHandler.WriteToken(jwt);var?response?=?new?TokenEntity(){AccessToken?=?encodedJwt,ExpiresIn?=?(int)_tokenOptions.ValidFor.TotalSeconds};return?response;} }More
默認是基于 HS256 的簽名方式,你也可以很輕松地切換成使用基于 RSA 的?RS256 方式,可以自己探索一下
更多實現(xiàn)細節(jié)可以參考源碼:https://github.com/WeihanLi/WeihanLi.Web.Extensions/tree/dev/src/WeihanLi.Web.Extensions/Authorization/Jwt
下一篇文章我們介紹如何使用 RefreshToken
References
https://github.com/WeihanLi/SparkTodo/blob/master/SparkTodo.API/Program.cs#L40
https://github.com/WeihanLi/WeihanLi.Web.Extensions/blob/dev/samples/WeihanLi.Web.Extensions.Samples/Program.cs#L40
https://github.com/WeihanLi/WeihanLi.Web.Extensions
總結
以上是生活随笔為你收集整理的更轻易地实现 Jwt Token的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Jwt Token 的刷新机制设计
- 下一篇: .NET MAUI 已在塔架就位 ,4月