每个新技术权限验证都有一套机制,之前项目WebApi接口权限验证用的是Owin做为权限验证,而.netcore权限限制使用的是IdentityServer4,采用JWT的方法验证token.
- 首先使用Guget包管理添加IdentityServer4包的引用,如图
- 生成token方法
/// <summary> /// 生成token /// </summary> /// <param name="userId"></param> /// <param name="key">JwtKey</param> /// <returns></returns> public static string GenerateToken(string userId, string key) { var tokenHandler = new JwtSecurityTokenHandler(); var keyBytes = System.Text.Encoding.Default.GetBytes(key); var authTime = DateTime.UtcNow; var expireaAt = authTime.AddHours(24);//token过期时间 var timestamp = TimeStamp.GenerateTimeStamp(authTime);//生成日间戳 var sign = MyUnity.GetMd5Upper32((userId + "&" + timestamp + "&" + key).ToUpper()); var tokenDescriptor = new SecurityTokenDescriptor() { Subject = new ClaimsIdentity(new Claim[] { new Claim(JwtClaimTypes.Audience,"api"), new Claim(JwtClaimTypes.Issuer,"hengfeng"), new Claim(JwtClaimTypes.Id,userId), new Claim("timestamp",timestamp), new Claim("sign",sign), }), Expires = expireaAt, SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(keyBytes), SecurityAlgorithms.HmacSha256Signature) }; var token = tokenHandler.CreateToken(tokenDescriptor); var tokenString = tokenHandler.WriteToken(token); return tokenString; }
- 创建JwtTokenFilter过滤器
public class JwtTokenFilter : ActionFilterAttribute { private readonly IConfiguration _config; public JwtTokenFilter(IConfiguration configuration) { _config = configuration; } public override void OnActionExecuting(ActionExecutingContext context) { try { var token = context.HttpContext.Request.Headers["token"]; var tokenHandler = new JwtSecurityTokenHandler(); var jwtSecurityToken = tokenHandler.ReadJwtToken(token); var claims = jwtSecurityToken.Claims; var userid = claims.FirstOrDefault(m => m.Type == "id").Value; var sign = claims.FirstOrDefault(m => m.Type == "sign").Value; var expireaAt = claims.FirstOrDefault(m => m.Type == "exp").Value; var key = _config["jwt_key"]; var timestamp = claims.FirstOrDefault(m => m.Type == "timestamp").Value; var sign_new = MyUnity.GetMd5Upper32((userid + "&" + timestamp + "&" + key).ToUpper()); if (!sign_new.Equals(sign, StringComparison.InvariantCultureIgnoreCase)) { context.Result = new JsonResult( "账号未登录"); } else { var expiresTime = TimeStamp.ConvertIntDateTime(expireaAt); if ((DateTime.Now - expiresTime).Minutes > 0) { context.Result = new JsonResult("账号Token失效"); } } } catch (Exception ex) { context.Result = new JsonResult(ex.ToString()); } } }
- 在使用控制器中添加 [ServiceFilter(typeof(JwtTokenFilter))] 特性
// GET: api/Default/5 [HttpGet("{id}", Name = "Get")] [ServiceFilter(typeof(JwtTokenFilter))] public string Get(int id) { return "value"; }
- 如果该控制器不需要验证token,加上允许匿名访问特性即可
[HttpPost] [AllowAnonymous] public void Post([FromBody] string value) { }
最后:注意登录成功后,需要把生成的token返回给前端,前端再一次请求系统其它需要验证token的接口中,在head中加上token值。
用到的加密算法
/// <summary> /// Md5 16位长度 /// </summary> /// <param name="sValue"></param> /// <returns></returns> public static string GetMd5Upper16(string sValue) { MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider(); string s = BitConverter.ToString(md5.ComputeHash(UTF8Encoding.Default.GetBytes(sValue)), 4, 8); s = s.Replace("-", "").ToUpper(); return s; } /// <summary> /// Md5 32位长度 /// </summary> /// <param name="sValue"></param> /// <returns></returns> public static string GetMd5Upper32(string sValue) { MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider(); string s = BitConverter.ToString(md5.ComputeHash(UTF8Encoding.Default.GetBytes(sValue))); s = s.Replace("-", "").ToUpper(); return s; }
生成 十位的时间戳
/// <summary> /// 获取十位的时间戳 /// </summary> /// <param name="dt"></param> /// <returns></returns> public static string GenerateTimeStamp(DateTime dt) { // Default implementation of UNIX time of the current UTC time TimeSpan ts = dt.ToUniversalTime() - new DateTime(1970, 1, 1, 0, 0, 0, 0); return Convert.ToInt32(ts.TotalSeconds).ToString(); }