接上一篇,在我們創建好WebApi程序之后,可以通過PostMan或者直接通過瀏覽器去請求我們的接口。這個時候涉及到一個問題,如何確定請求者的身份呢?
在這里我們使用JWT,在登陸后,給用戶頒發一個訪問其他接口的身份令牌,每一次的請求必須帶令牌請求,否則請求無效。
實現步驟如下:
1.首先我們需要攔截到每一次的請求,然后對請求做分析
在這里我們引入using Microsoft.AspNetCore.Http ,主要是要用到HttpContext類
public class JwtAuthorizationFilter { //RequestDelegate請求委托 private readonly RequestDelegate _next; public JwtAuthorizationFilter(RequestDelegate next) { _next = next; } /// <summary> /// /// </summary> /// <param name="httpContext"></param> /// <returns></returns> public Task Invoke(HttpContext httpContext) { //對請求信息進行處理部分 return _next(httpContext); } }
上面代碼里 HttpContext httpContext 可以獲取到對接口的請求,請求信息包括在httpContext里,在獲取到請求之后我們要對請求進行一個處理,即,解析出該請求用戶的身份信息
回到用戶登陸模塊,用在登陸的時候我們要給他授權一個身份令牌,並返回一個token字符串給用戶(后期球球任何接口都要帶上該字符串),我們在用戶登陸寫個登陸實現
/// <summary> /// 用戶登錄實現 /// </summary> /// <param name="parm"></param> /// <returns></returns> public async Task<ApiResult<Result>> LoginAsync(TUserAuthsLogin parm) { var res = new ApiResult<Result>(); //用戶名是否存在 var model = Db.Queryable<T_User_Auths>() .Where(m => m.LoginName == parm.LoginName).First(); //校驗用戶類型和密碼 if (model != null && model.UseState == 1) { if (model.PassWord.Equals(parm.PassWord)) { //校驗過用戶名和密碼之后,給用戶頒發身份令牌並返回token字符串給用戶 TokenModel tokenModel = new TokenModel(); tokenModel.Uid = model.UserId;//UID tokenModel.LoginName = model.LoginName;//用戶名 string tokenStr = JwtHelper.IssueJWT(tokenModel); res.success = true; res.token = tokenStr;//返回token字符串給用戶 res.message = "登錄成功!"; } else { res.success = false; res.statusCode = (int)ApiEnum.Error; res.message = "密碼錯誤~"; } } else { res.success = false; res.statusCode = (int)ApiEnum.Error; res.message = "賬號錯誤~"; } return await Task.Run(() => res); }
在校驗過用戶名和密碼之后,頒發,令牌,將令牌放進JWT幫助類,生成token字符串
JWTHelper如下
using Microsoft.IdentityModel.Tokens; using System; using System.Collections.Generic; using System.IdentityModel.Tokens.Jwt; using System.Linq; using System.Security.Claims; using System.Text; using System.Threading.Tasks; namespace SupplierManagement.Helper.JWT { public class JWTHelper { /// <summary> /// .net core 自帶了jwt幫助類 /// </summary> public class JwtHelper { /// <summary> /// 頒發JWT字符串 /// </summary> /// <param name="tokenModel"></param> /// <returns></returns> public static string IssueJWT(TokenModel tokenModel) { var dateTime = DateTime.UtcNow;//世界時間 var claims = new Claim[] { new Claim(JwtRegisteredClaimNames.Jti,tokenModel.Uid.ToString()),//用戶Id new Claim("TokenType",tokenModel.TokenType), new Claim("LoginName",tokenModel.LoginName), //new Claim("Role", tokenModel.Role),//身份 //new Claim("Project", tokenModel.Project),//訪問項目 new Claim(JwtRegisteredClaimNames.Iat,dateTime.ToString(),ClaimValueTypes.Integer64)//時間戳 }; //秘鑰 var jwtConfig = new JwtAuthConfigModel(); //(獲取用於簽名的驗證) var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtConfig.JWTSecretKey)); //獲取與Microsoft.IdentityModel.Tokens.SecurityKey關聯的密鑰ID。 var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); //過期時間 double exp = 0; switch (tokenModel.TokenType) { case "Web": exp = jwtConfig.WebExp; break; case "App": exp = jwtConfig.AppExp; break; case "MiniProgram": exp = jwtConfig.MiniProgramExp; break; case "Other": exp = jwtConfig.OtherExp; break; } var jwt = new JwtSecurityToken( issuer: "NewNanNingSystem",//項目名稱 claims: claims, //聲明集合(包括用戶和時間戳) expires: dateTime.AddHours(exp),//expires有效分鍾數 signingCredentials: creds);//關聯密鑰id var jwtHandler = new JwtSecurityTokenHandler(); var encodedJwt = jwtHandler.WriteToken(jwt);//將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 = new object(); ; object user = new object(); try { jwtToken.Payload.TryGetValue("TokenType", out role);//沒理解 } catch (Exception e) { Console.WriteLine(e); throw; } var tm = new TokenModel { Uid = jwtToken.Id, LoginName = jwtToken.Payload["LoginName"].ToString() }; return tm; } } /// <summary> /// 令牌 /// </summary> public class TokenModel { /// <summary> /// 用戶Id /// </summary> public string Uid { get; set; } /// <summary> /// 用戶名 /// </summary> public string LoginName { get; set; } /// <summary> /// 身份 /// </summary> public string Role { get; set; } /// <summary> /// 項目名稱 /// </summary> public string Project { get; set; } /// <summary> /// 令牌類型 /// </summary> public string TokenType { get; set; } } } }
相關聯的類,JwtAuthConfigModel
using SupplierManagement.Common; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace SupplierManagement.Helper.JWT { public class JwtAuthConfigModel : BaseConfigModel { /// <summary> /// /// </summary> public JwtAuthConfigModel() { try { //通過Configuration去讀取配置,appsetting里面 JWTSecretKey = ConfigurationManager.Configuration["JwtAuth:SecurityKey"]; WebExp = double.Parse(ConfigurationManager.Configuration["JwtAuth:WebExp"]); AppExp = double.Parse(ConfigurationManager.Configuration["JwtAuth:AppExp"]); MiniProgramExp = double.Parse(ConfigurationManager.Configuration["JwtAuth:MiniProgramExp"]); OtherExp = double.Parse(ConfigurationManager.Configuration["JwtAuth:OtherExp"]); } catch (Exception e) { Console.WriteLine(e); throw; } } /// <summary> /// JWT密鑰 /// </summary> public string JWTSecretKey = "This is JWT Secret Key"; /// <summary> /// /// </summary> public double WebExp = 12; /// <summary> /// /// </summary> public double AppExp = 12; /// <summary> /// /// </summary> public double MiniProgramExp = 12; /// <summary> /// /// </summary> public double OtherExp = 12; } }
到這一步后,我們還無法使用JWT,我們還需要在startup里面對JWT進行注冊
public void ConfigureServices(IServiceCollection services) { services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); //接口注冊 services.AddTransient<ITUserAuthsService, TUserAuthsService>(); //JWT注冊 services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { JwtAuthConfigModel jwtConfig = new JwtAuthConfigModel(); options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true,//是否驗證Issuer ValidateAudience = true,//是否驗證Audience ValidateLifetime = true,//是否驗證失效時間 ValidateIssuerSigningKey = true,//是否驗證SecurityKey ValidAudience = "wangcong",//Audience ValidIssuer = "NewNanNingSystem",//Issuer,這兩項和前面簽發jwt的設置一致 IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtConfig.JWTSecretKey))//拿到SecurityKey }; }); }
注冊完成,我們提交一個登陸請求,
結果如下
,好了,到這一步,我們登陸成功了,現在我們需要訪問其他接口了,我們得把這個驗證身份的字符串帶上,我們先寫一個查詢所有用戶的方法,分別在接口,實現,以及控制器寫上對應方法
我們用postman模擬瀏覽器請求,這個時候記得帶上token字符串
發送請求后,我們進入攔截器
在這里我們看到,請求的接口是/api/login/getAllUsers,並且請求頭里面包含了Authorization屬性,我們token字符串是放在這個屬性里面的。在這里提一下登陸放行,因為任何請求在這里都會被攔截,包括登陸請求和瀏覽器的預請求,第一次登陸的時候是不回有token字符串的,這需要我們登陸后進行頒發,所以判斷地址進行放行,Method=="OPTIONS"是瀏覽器的預請求,我們也放行,瀏覽器的第二次的請求才是真實的接口請求,在這里我們拿到token字符串后,使用JWT幫助類進行解析。結果如下
在這里我們解析出了用戶名和用戶id確認了請求者的身份,現在放行請求讓他訪問我們的接口,成功返回數據。