在后端开发中,实现每个用户的鉴权控制是我们要去考虑的,今天给大家分享一下基于JWT的token的鉴权机制。基于token的鉴权机制,它不需要在服务端去保留用户的认证信息或者会话信息
在后端开发中,实现每个用户的鉴权控制是我们要去考虑的,今天给大家分享一下基于JWT的token的鉴权机制。基于token的鉴权机制,它不需要在服务端去保留用户的认证信息或者会话信息。不需要去考虑用户在哪一台服务器登录了,这就为应用的扩展提供了便利。
流程上是这样的:
用户使用用户名密码来请求服务器
服务器进行验证用户的信息
服务器通过验证发送给用户一个token
客户端存储token,并在每次请求时附送上这个token值
服务端验证token值,并返回数据
1.新建一个特性,用于允许未通过身份验证也可以访问的特性
public class SkipAttribute : Attribute, IFilterMetadata { ///允许未通过身份验证也可以访问的特性 }
2.新建一个筛选过滤器
我们通过过滤器来判断请求头中是否有允许未通过身份验证也可以访问的特性,如果有允许未通过身份验证访问的特性,直接进入到下个管道,如果没有则验证token。
public class ApiAuthorize : IAuthorizationFilter { public void OnAuthorization(AuthorizationFilterContext context) { //1.判断是否需要校验 var isDefined = false; var controllerActionDescriptor = context.ActionDescriptor as ControllerActionDescriptor; if (controllerActionDescriptor != null) { isDefined = controllerActionDescriptor.MethodInfo.GetCustomAttributes(inherit: true) .Any(a => a.GetType().Equals(typeof(SkipAttribute))); } if (isDefined) { return; } var token = context.HttpContext.Request.Headers["Auth"].ToString(); //ajax请求传过来 string pattern = "^Bearer (.*?)$"; if (!Regex.IsMatch(token, pattern)) { context.Result = new ContentResult { StatusCode = 401, Content = "token格式不对!格式为:Bearer {token}" }; return; } token = Regex.Match(token, pattern).Groups[1]?.ToString(); if (token == "null" || string.IsNullOrEmpty(token)) { context.Result = new ContentResult { StatusCode = 401, Content = "token不能为空" }; return; } //校验auth的正确性 var result = JWTHelp.JWTJieM(token, ConfigHelp.GetString("AccessTokenKey")); if (result == "expired") { context.Result = new ContentResult { StatusCode = 401, Content = "expired" }; return; } else if (result == "invalid") { context.Result = new ContentResult { StatusCode = 401, Content = "invalid" }; return; } else if (result == "error") { context.Result = new ContentResult { StatusCode = 401, Content = "error" }; return; } else { //表示校验通过,用于向控制器中传值 context.RouteData.Values.Add("auth", result); } } /// <summary> /// 判断该请求是否是ajax请求 /// </summary> /// <param name="request"></param> /// <returns></returns> private bool IsAjaxRequest(HttpRequest request) { string header = request.Headers["X-Requested-With"]; return "XMLHttpRequest".Equals(header); } }
3.JWT加解密
Jwt(json web token),是一种基于 Json 的无状态授权令牌,因为 Jwt 是一种标准的数据传输规范,并不是某家所独有的技术规范,因此非常适用于构建单点登录服务,为 web、client、app 等等各种接口使用方提供授权服务。
public class JWTHelp { /// <summary> /// JWT加密算法 /// </summary> /// <param name="payload">负荷部分,存储使用的信息</param> /// <param name="secret">密钥</param> /// <param name="extraHeaders">存放表头额外的信息,不需要的话可以不传</param> /// <returns></returns> public static string JWTJiaM(IDictionary<string, object> payload, string secret, IDictionary<string, object> extraHeaders = null) { IJwtAlgorithm algorithm = new HMACSHA256Algorithm(); IJsonSerializer serializer = new JsonNetSerializer(); IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder(); IJwtEncoder encoder = new JwtEncoder(algorithm, serializer, urlEncoder); var token = encoder.Encode(payload, secret); return token; } /// <summary> /// JWT解密算法(新的7.x版本写法) /// </summary> /// <param name="token">需要解密的token串</param> /// <param name="secret">密钥</param> /// <returns></returns> public static string JWTJieM(string token, string secret) { try { IJsonSerializer serializer = new JsonNetSerializer(); IDateTimeProvider provider = new UtcDateTimeProvider(); IJwtValidator validator = new JwtValidator(serializer, provider); IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder(); IJwtAlgorithm algorithm = new HMACSHA256Algorithm(); // symmetric IJwtDecoder decoder = new JwtDecoder(serializer, validator, urlEncoder, algorithm); var json = decoder.Decode(token, secret, true); //校验通过,返回解密后的字符串 return json; } catch (TokenExpiredException) { //表示过期 return "expired"; } catch (SignatureVerificationException) { //表示验证不通过 return "invalid"; } catch (Exception) { return "error"; } } }
4.token的存取
token怎么去存取呢,通过jwt加解密校验。
5.注册过滤器
访问接口基本都要鉴权,我们注册在全局,在StartUp类里面注册
services.AddControllers(o => { o.Filters.Add<ApiAuthorize>(); o.Filters.Add<SkipAttribute>(); });
6.生成token和接口token校验
[HttpGet("GetUserInformation")] public RetuenResult<string> GetUserInformation() { return new RetuenResult<string>() { code = 200, msg = "success", data = "hello" }; } [HttpPost("Login")] [SkipAttribute] public RetuenResult<Token> Login(string name,string pwd) { if (name == "admin" && pwd == "123") { Token token = TokenHelp.GetToken(name, pwd); MemoryCacheHelper.AddMemoryCache(token.accessToken, User); return new RetuenResult<Token> { code = 200, msg = "登录成功", data = token }; } else { return new RetuenResult<Token> { code = 201, msg = "登录失败,账号或密码错误", data = new Token { } }; } }
登录获取token
用postman调用GetUserInformation接口
未做header中的token校验
做了token校验的
源码:https://github.com/fraceW/TokenAuthorization.git