今天来做一个webapi的安全管控验证的案例
我们知道webapi密匙验证也有不少,比如OAuth2.0.。。。我们今天用JWT这种方式来做一个账号密码换取Token的案例
基本流程是 输入账号密码--->后台比对==true -->return Token else 未登录
所有控制器必须继承自基础类,基础类我们用权限特性控制
老规矩,需要用到的工具
VS2019 NetCore3.1,Postman 调试用
我们看下主要结构如下
记得引用包
我们一个个类来看
1 /// <summary> 2 /// 基础控制器类 3 /// </summary> 4 [Route("api/[controller]/[action]")] 5 [ApiController] 6 [ActionFilterAttribute] 7 public class BaseController : ControllerBase 8 { 9 /// <summary> 10 /// 获取Token的方法 11 /// </summary> 12 /// <param name="userId"></param> 13 /// <param name="token1"></param> 14 /// <returns></returns> 15 [NonAction] 16 protected string GetJwtToken(string userId,string token1) 17 { 18 var jwtHandle = new JwtSecurityTokenHandler { }; 19 20 var token = jwtHandle.CreateJwtSecurityToken( 21 "test", 22 "test", 23 new ClaimsIdentity(new Claim[]{ 24 new Claim("name2","tt2"), 25 new Claim("role2","user"), 26 new Claim("token",token1), 27 new Claim("c_uid",userId), 28 }), 29 System.DateTime.Now, 30 //设置过期 31 System.DateTime.Now.AddMilliseconds(3000.00), 32 System.DateTime.Now, 33 new SigningCredentials( 34 new SymmetricSecurityKey(System. 35 Text. 36 Encoding. 37 ASCII.GetBytes("6mSzczZX3KgZ3HOX")), 38 SecurityAlgorithms.HmacSha256) 39 ); 40 41 return jwtHandle.WriteToken(token); 42 } 43 }
这个基础控制类我们这里就写一个方法吧,就是生成Token,当然是在匹配用户名密码正确之后调用
接下来是这个权限特性 这里都做了注释,不再一一解释
/// 权限过滤特性 /// </summary> [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)] public class ActionFilterAttribute : Attribute, IAsyncActionFilter, IAsyncAlwaysRunResultFilter { /// <summary> /// 忽略token的方法 /// </summary> public static readonly string[] IgnoreToken = {"Login"}; /// <summary> /// 校验过滤器 /// </summary> /// <param name="context"></param> /// <param name="next"></param> /// <returns></returns> public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) { //获取用户声明中的Token var UserToken = context.HttpContext.User.FindFirstValue("token"); //执行的动作 var action = context.RouteData.Values["Action"].ToString(); if (IgnoreToken.Count(s => s == action) == 0) { if (UserToken is null) { context.Result = new JsonResult("用户未登录!"); return; } //现实中获取数据库对比用户唯一识别token,非加密的那个 if (!UserToken.Equals("123456")) { context.Result = new JsonResult("Token失效!"); return; } } //抛转下一个中间件 var resultContext = await next(); } /// <summary> /// /// </summary> /// <param name="context"></param> /// <param name="next"></param> /// <returns></returns> public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next) { var resultContext = await next(); }
}
然后我们需要看下StartUp类中我们需要使用这个中间件才可以正常使用
public void ConfigureServices(IServiceCollection services) { //注册JWT services.AddAuthentication() .AddIdentityServerJwt(); services.Configure<JwtBearerOptions>( IdentityServerJwtConstants.IdentityServerJwtBearerScheme, options => { var onTokenValidated = options.Events.OnTokenValidated; var onMessageReceived = options.Events.OnMessageReceived; options.Events.OnTokenValidated = async context => { await onTokenValidated(context); }; options.Events.OnMessageReceived = async context => { /* https://www.cnblogs.com/liuww/p/12177272.html https://www.cnblogs.com/RainingNight/p/jwtbearer-authentication-in-asp-net-core.html https://www.cnblogs.com/jesse2013/p/integrate-with-lagacy-auth.html https://www.cnblogs.com/nsky/p/10312101.html */ // await onMessageReceived(context); var token = context.Request.Headers["token"]; if (string.IsNullOrEmpty(token)) { token = context.Request.Query["token"]; } context.Token = token.FirstOrDefault(); // return await System.Threading.Tasks.Task.CompletedTask; }; //Token认证注册 options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters { ValidIssuer = "test", ValidAudience = "test", IssuerSigningKey = new Microsoft.IdentityModel.Tokens.SymmetricSecurityKey(System.Text.Encoding.ASCII.GetBytes("6mSzczZX3KgZ3HOX")) }; }); services.AddControllers(); }
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseRouting(); app.UseAuthentication(); //app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); }
上面都写好之后
我们看下用户类这里只做两个东西,一个登录,一个获取数据,注意登录是公开的,获取用户信息是私密的
我们运行一下调试看看
打开Postman发现直接调用获取用户信息是被拒绝的
好了,我们再尝试通过用户名密码换取Token
我们发现我们成功换取到了token,然后我们再用Token去请求刚才提示拒绝的接口
我们看到已经非常友好的接受了我们的请求。当然这是一个最基本的用户名密码换取Token的验证,如果是前后端分离,我们还要有秘钥等等验证,但是小程序里面的话,那么我们可以通过这种验证实现移动端网页端的调用。