接上一篇,在我们创建好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确认了请求者的身份,现在放行请求让他访问我们的接口,成功返回数据。