1.跨語言使用。web
2.服務器端無需再保存任何東西,只須要客戶端保存token就能夠。json
3.實現簡單。api
4.統一認證方式,若是是移動端也要驗證的話,jwt也支持就無需修改,不然客戶端 服務器一套,移動端 服務器又是一套數組
固然缺陷也是很明顯,就是退出登陸後,已發放的token沒法銷燬,能夠繼續訪問。就是令牌給你了,若是別人盜取了你的令牌,我也是認的,我只認令牌不認人。也能夠設置令牌有效期,假如設置過時有效時間爲10分鐘,就算你拿到了令牌想用也已通過期了,可是這就要求客戶端每次想要作什麼,先去申請令牌,而後在去操做,這就很麻煩。內部系統的話是能夠用這種模式的,若是對外的不建議使用。對外的可使用傳統的簽名認證方法。安全
第一步:首先要在程序中讀取appSettings配置文件信息服務器
建立一個AppSettings.cs類,存放讀取配置信息的函數 代碼以下,使用Nuget 安裝Microsoft.Extensions.Configuration和Microsoft.Extensions.Configuration.Json,Microsoft.Extensions.Configuration.Binderapp
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration.Json; using System; using System.Collections.Generic; using System.Linq; namespace WebApi.Core.Common { public class AppSettings { static IConfiguration Configuration { get; set; } static string contentPath { get; set; } public AppSettings(string contentPath) { string Path = "appsettings.json"; //若是你把配置文件 是 根據環境變量來分開了,能夠這樣寫 //Path = $"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")}.json"; Configuration = new ConfigurationBuilder() .SetBasePath(contentPath) .Add(new JsonConfigurationSource { Path = Path, Optional = false, ReloadOnChange = true })//這樣的話,能夠直接讀目錄裏的json文件,而不是 bin 文件夾下的,因此不用修改複製屬性 .Build(); } public AppSettings(IConfiguration configuration) { Configuration = configuration; } /// <summary> /// 封裝要操做的字符 /// </summary> /// <param name="sections">節點配置</param> /// <returns></returns> public static string app(params string[] sections) { try { if (sections.Any()) { return Configuration[string.Join(":", sections)]; } } catch (Exception) { } return ""; } /// <summary> /// 遞歸獲取配置信息數組 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="sections"></param> /// <returns></returns> public static List<T> app<T>(params string[] sections) { List<T> list = new List<T>(); Configuration.Bind(string.Join(":", sections), list); return list; } } }
找到webApi項目,打開Startup類,在ConfigureService函數 註冊AppSettings函數
編輯appsettings.json,新增紅色框子的內容。ui
獲取調試一下this
//註冊appsettings讀取類 services.AddSingleton(new Appsettings(Configuration)); var text = Appsettings.app(new string[] { "AppSettings", "ConnectionStringSql" }); Console.WriteLine($"ConnectionString:{text}"); Console.ReadLine();
運行後的結果
接下來,咱們開始正式的在項目中,註冊和使用JWT,首先配置一下jwt須要的參數,在appsettings.json中,SecretKey必須大於16個,是大於,不是大於等於
在Api項目中 添加Nuget 包 IdentityModel,Microsoft.AspNetCore.Authentication.JwtBearer,Microsoft.AspNetCore.Authorization
在model項目中添加一個tokenModel
using System; using System.Collections.Generic; using System.Text; namespace WebApi.Core.Model { /// <summary> /// 令牌 /// </summary> public class TokenModel { /// <summary> /// Id /// </summary> public string Uid { get; set; } /// <summary> /// 角色 /// </summary> public string Role { get; set; } } }
在Api項目中新建一個文件夾 Authorization,新建一個JwtHelper 裏面有生成token 和解析token 兩個方法
using System.Linq; using System.Security.Claims; using System.Text; using System.Threading.Tasks; using WebApi.Core.Common; using WebApi.Core.Model; namespace WebApi.Core.Api.Authorization { public class JwtHelper { /// <summary> /// 獲取token信息 /// </summary> /// <param name="tokenModel"></param> /// <returns></returns> public static string issueJwt(TokenModel tokenModel) { //獲取Appsetting配置信息 string iss = AppSettings.app(new string[] { "AppSettings", "JwtSetting", "Issuer" }); string aud = AppSettings.app(new string[] { "AppSettings", "JwtSetting", "Audience" }); string secret = AppSettings.app(new string[] { "AppSettings", "JwtSetting", "SecretKey" }); var claims = new List<Claim> { /* * 特別重要: 一、這裏將用戶的部分信息,好比 uid 存到了Claim 中,若是你想知道如何在其餘地方將這個 uid從 Token 中取出來,
請看下邊的SerializeJwt() 方法,或者在整個解決方案,搜索這個方法,看哪裏使用了! 二、你也能夠研究下 HttpContext.User.Claims ,具體的你能夠看看 Policys/PermissionHandler.cs 類中是如何使用的。 */
//nbf 生效時間 、Jti 編號、iat 簽發時間、aud 受衆、exp 過時時間 new Claim(JwtRegisteredClaimNames.Jti, tokenModel.Uid.ToString()), new Claim(JwtRegisteredClaimNames.Iat, $"{new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds()}"), new Claim(JwtRegisteredClaimNames.Nbf,$"{new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds()}") , //這個就是過時時間,目前是過時1000秒,可自定義,注意JWT有本身的緩衝過時時間 new Claim (JwtRegisteredClaimNames.Exp,$"{new DateTimeOffset(DateTime.Now.AddSeconds(1000)).ToUnixTimeSeconds()}"), new Claim(ClaimTypes.Expiration, DateTime.Now.AddSeconds(1000).ToString()), new Claim(JwtRegisteredClaimNames.Iss,iss), new Claim(JwtRegisteredClaimNames.Aud,aud), }; // 能夠將一個用戶的多個角色所有賦予; claims.AddRange(tokenModel.Role.Split(',').Select(s => new Claim(ClaimTypes.Role, s))); //祕鑰 (SymmetricSecurityKey 對安全性的要求,密鑰的長度過短會報出異常) var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secret)); var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); var jwt = new JwtSecurityToken( issuer: iss, claims: claims, signingCredentials: creds); var jwtHandler = new JwtSecurityTokenHandler(); var encodedJwt = jwtHandler.WriteToken(jwt); return encodedJwt; } /// <summary> /// 解析 /// </summary> /// <param name="jwtStr"></param> /// <returns></returns> public static TokenModel SerializeJwt(string jwtStr) { var jwtHandler = new JwtSecurityTokenHandler(); JwtSecurityToken jwtToken = jwtHandler.ReadJwtToken(jwtStr); object role; try { jwtToken.Payload.TryGetValue(ClaimTypes.Role, out role); } catch (Exception e) { Console.WriteLine(e); throw; } var tm = new TokenModel { Uid = jwtToken.Id.ToString(), Role = role != null ? role.ToString() : "", }; return tm; } } }
在UserController 新建一個login接口,獲取token
/// <summary> /// 登陸驗證而且獲取token /// </summary> /// <param name="loginModel"></param> /// <returns></returns> [HttpPost] public IActionResult LoginValidate(LoginModel loginModel) { string jwtStr = string.Empty; bool suc = false; if (loginModel != null) { //加登陸驗證 if (loginModel.UserName == "admin" && loginModel.PassWord == "123456") { TokenModel tokenModel = new TokenModel { Uid = loginModel.UserName, Role = loginModel.Role }; jwtStr = JwtHelper.issueJwt(tokenModel); suc = true; } } return Ok(new { success=suc, token = jwtStr }); }
按F5啓動,能夠看到token已經生成,客戶端也已經獲取到。
獲取到了token咱們怎麼使用呢? 下一步 咱們要在Swagger中開啓 JWT服務,先安裝包 Swashbuckle.AspNetCore.Filters
而後找到SwaggerSetUp.cs的AddSwaggerSetUp方法中增長如下代碼
public static void AddSwaggerSetup(this IServiceCollection services) { if (services == null) throw new ArgumentNullException(nameof(services)); var ApiName = "Webapi.Core"; services.AddSwaggerGen(c => { c.SwaggerDoc("V1", new OpenApiInfo { // {ApiName} 定義成全局變量,方便修改 Version = "V1", Title = $"{ApiName} 接口文檔——Netcore 3.0", Description = $"{ApiName} HTTP API V1", }); c.OrderActionsBy(o => o.RelativePath); // 獲取xml註釋文件的目錄 var xmlPath = Path.Combine(AppContext.BaseDirectory, "WebApi.Core.Api.xml"); c.IncludeXmlComments(xmlPath, true);//默認的第二個參數是false,這個是controller的註釋,記得修改 // 獲取xml註釋文件的目錄 var xmlPathModel = Path.Combine(AppContext.BaseDirectory, "WebApi.Core.Model.xml"); c.IncludeXmlComments(xmlPathModel, true);//默認的第二個參數是false,這個是controller的註釋,記得修改 //在 header中添加token,傳遞到後臺 c.OperationFilter<SecurityRequirementsOperationFilter>(); #region Token綁定到configureServices c.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme { Description = "JWT受權(數據將在請求頭中進行傳輸) 直接在下框中輸入Bearer {token}(注意二者之間是一個空格)\"", Name = "Authorization",//jwt默認的參數名稱 In = ParameterLocation.Header,//jwt默認存放Authorization信息的位置(請求頭中) Type = SecuritySchemeType.ApiKey }); #endregion }); }
按F5運行 能夠看到 token入口了,按要求的格式在 value中輸入 token 之後的請求head 就會一直加入token了。
下面該受權token 的認證了,在SetupService 文件夾下 新建一個AuthJwtSetup.cs 類 以下
using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.Extensions.DependencyInjection; using Microsoft.IdentityModel.Tokens; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using WebApi.Core.Common; namespace WebApi.Core.Api.SetUpService { public static class AuthJwtSetup { public static void AddAuthorizationJwtSetUp(this IServiceCollection services) { if (services == null) throw new ArgumentNullException(nameof(services)); //讀取配置文件 var symmetricKeyAsBase64 = AppSettings.app(new string[] { "AppSettings", "JwtSetting", "SecretKey" }); var keyByteArray = Encoding.ASCII.GetBytes(symmetricKeyAsBase64); var signingKey = new SymmetricSecurityKey(keyByteArray); var Issuer = AppSettings.app(new string[] { "AppSettings", "JwtSetting", "Issuer" }); var Audience = AppSettings.app(new string[] { "AppSettings", "JwtSetting", "Audience" }); // 令牌驗證參數 var tokenValidationParameters = new TokenValidationParameters { ValidateIssuerSigningKey = true, IssuerSigningKey = signingKey, ValidateIssuer = true, ValidIssuer = Issuer,//發行人 ValidateAudience = true, ValidAudience = Audience,//訂閱人 ValidateLifetime = true, ClockSkew = TimeSpan.FromSeconds(30), //token過時後 還能夠繼續多訪問30秒 RequireExpirationTime = true, }; //2.1【認證】、core自帶官方JWT認證 // 開啓Bearer認證 services.AddAuthentication("Bearer") // 添加JwtBearer服務 .AddJwtBearer(o => { o.TokenValidationParameters = tokenValidationParameters; o.Events = new JwtBearerEvents { OnAuthenticationFailed = context => { // 若是過時,則把<是否過時>添加到,返回頭信息中 if (context.Exception.GetType() == typeof(SecurityTokenExpiredException)) { context.Response.Headers.Add("Token-Expired", "true"); } return Task.CompletedTask; } }; }); } } }
startup.cs的ConfigureServices 方法添加 服務注入
//jwt受權驗證 services.AddAuthorizationSetup();
Configure方法添加 下面的代碼
接口受權策略驗證,在UserController裏面添加一個方法
按F5啓動調試,先獲取Admin角色權限的token,添加token到header中,而後請求 驗證角色權限那個接口,驗證成功。
若是獲取的token 不是Admin 角色權限咱們再來試一下,看看是什麼結果,結果就是沒有訪問權限。
下面該解析Token 了,試一下,添加一個接口,步驟同上,先獲取token,而後綁定token,最後調用一下解析接口,看結果咱們已經解析成功了。