話很少講,也不知道咋講!直接上代碼前端
/// <summary> /// 認證用戶信息 /// </summary> public class DyUser { /// <summary> /// 用戶ID /// </summary> public int UserId { get; set; } /// <summary> /// 所屬商戶ID /// </summary> public int? TenantId { get; set; } }
public class AuthOptions { /// <summary> /// Jwt認證Key /// </summary> public string Security { get; set; } /// <summary> /// 過時時間【天】 /// </summary> public int Expiration { get; set; } }
public interface IAuthManage { /// <summary> /// 生成JwtToken /// </summary> /// <param name="user">用戶信息</param> /// <returns></returns> string GenerateJwtToken(DyUser user); }
暫時是使用微軟提供類庫生成,若是有想法能夠本身生成web
public class MicrosoftJwtAuthManage : IAuthManage { private readonly AuthOptions _authOptions; public MicrosoftJwtAuth(AuthOptions authOptions) { _authOptions = authOptions; } public string GenerateJwtToken(DyUser user) { var tokenHandler = new JwtSecurityTokenHandler(); var key = Encoding.ASCII.GetBytes(_authOptions.Security); var tokenDescriptor = new SecurityTokenDescriptor { Subject = new ClaimsIdentity(new Claim[] { new Claim("user",user.ToJson()) }), Expires = DateTime.UtcNow.AddDays(_authOptions.Expiration),//一週過時 SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature) }; var token = tokenHandler.CreateToken(tokenDescriptor); return tokenHandler.WriteToken(token); } }
這裏借鑑國外大牛的代碼,主要就是驗證jwt而且存把解析出來的數據存放到當前上下文api
public class JwtMiddleware { private readonly RequestDelegate _next; private readonly AuthOptions _authOptions; public JwtMiddleware(RequestDelegate next, AuthOptions authOptions) { _next = next; _authOptions = authOptions; } public async Task Invoke(HttpContext context) { //獲取上傳token,可自定義擴展 var token = context.Request.Headers["Authorization"].FirstOrDefault()?.Split(" ").Last() ?? context.Request.Headers["X-Token"].FirstOrDefault() ?? context.Request.Query["Token"].FirstOrDefault() ?? context.Request.Cookies["Token"]; if (token != null) AttachUserToContext(context, token); await _next(context); } private void AttachUserToContext(HttpContext context, string token) { try { var tokenHandler = new JwtSecurityTokenHandler(); var key = Encoding.ASCII.GetBytes(_authOptions.Security); tokenHandler.ValidateToken(token, new TokenValidationParameters { ValidateIssuerSigningKey = true, IssuerSigningKey = new SymmetricSecurityKey(key), ValidateIssuer = false, ValidateAudience = false, // set clockskew to zero so tokens expire exactly at token expiration time (instead of 5 minutes later) ClockSkew = TimeSpan.Zero }, out SecurityToken validatedToken); var jwtToken = (JwtSecurityToken)validatedToken; var user = jwtToken.Claims.First(x => x.Type == "user").Value.ToJsonEntity<DyUser>(); //寫入認證信息,方便業務類使用 var claimsIdentity = new ClaimsIdentity(new Claim[] { new Claim("user", jwtToken.Claims.First(x => x.Type == "user").Value) }); Thread.CurrentPrincipal = new ClaimsPrincipal(claimsIdentity); // attach user to context on successful jwt validation context.Items["User"] = user; } catch { // do nothing if jwt validation fails // user is not attached to context so request won't have access to secure routes throw; } } }
這個根據剛纔中間件的存放的信息判斷是否受權成功,支持匿名特性session
public class ApiAuthorizeAttribute : Attribute, IAuthorizationFilter { public void OnAuthorization(AuthorizationFilterContext context) { var user = context.HttpContext.Items["User"]; //驗證是否須要受權和受權信息 if (HasAllowAnonymous(context) == false && user == null) { // not logged in context.Result = new JsonResult(new {message = "Unauthorized"}) {StatusCode = StatusCodes.Status401Unauthorized}; } } private static bool HasAllowAnonymous(AuthorizationFilterContext context) { var filters = context.Filters; if (filters.OfType<IAllowAnonymousFilter>().Any()) { return true; } // When doing endpoint routing, MVC does not add AllowAnonymousFilters for AllowAnonymousAttributes that // were discovered on controllers and actions. To maintain compat with 2.x, // we'll check for the presence of IAllowAnonymous in endpoint metadata. var endpoint = context.HttpContext.GetEndpoint(); return endpoint?.Metadata?.GetMetadata<IAllowAnonymous>() != null; } }
方便之後管理和維護,主要就是把須要的對象注入到IOC容器裏面app
public static class AuthServiceExtensions { public static void AddAuth(this IServiceCollection services, Action<AuthOptions> configAction) { var options = new AuthOptions(); configAction(options); services.AddSingleton(options); services.AddSingleton<IAuthManage>(new MicrosoftJwtAuthManage(options)); } }
這裏是爲了在非控制器類獲取用戶信息用async
/// <summary> /// 當前會話對象 /// </summary> public class NullDySession { /// <summary> /// 獲取DySession實例 /// </summary> public static NullDySession Instance { get; } = new NullDySession(); /// <summary> /// 獲取當前用戶信息 /// </summary> public DyUser DyUser { get { var claimsPrincipal = Thread.CurrentPrincipal as ClaimsPrincipal; var claimsIdentity = claimsPrincipal?.Identity as ClaimsIdentity; var userClaim = claimsIdentity?.Claims.FirstOrDefault(c => c.Type == "user"); if (userClaim == null || string.IsNullOrEmpty(userClaim.Value)) { return null; } return userClaim.Value.ToJsonEntity<DyUser>(); } } private NullDySession() { } }
到這爲止準備工做完成,開始用起來吧~ui
//添加全局權限認證過濾器 services.AddControllersWithViews(options => { options.Filters.Add<ApiAuthorizeAttribute>(); }) //添加認證配置信息 services.AddAuth(options => { options.Expiration = 7;//天爲單位 options.Security = apolloConfig.Get("JwtSecret"); });
注意中間件的位置this
//啓用jwt認證中間件 app.UseMiddleware<JwtMiddleware>();
//生成了JwtToken var newToken = _authManage.CreateJwtToken(para.Sn); //Controller裏面獲取用戶信息 public DyUser DyUser => (DyUser)this.HttpContext.Items["User"]; //普通class類獲取用戶信息【若是不是Web應用,須要獨立引用Dymg.Core】 NullDySession.Instance.DyUser.UserId; //若是個別不接口不須要認證,能夠使用AllowAnonymous特性 [HttpPost, AllowAnonymous] public string Noauth() { return "這個不須要受權"; }
//token放在請求頭裏面 Authorization:Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoie1wiVXNlcklkXCI6MTIzNDU2ODcsXCJUZW5hbnRJZFwiOjY1NDMyMSxcIlN0YXRpb25JZFwiOm51bGwsXCJTbWFydEJveFNuXCI6bnVsbH0iLCJuYmYiOjE1OTU5MDAxMzYsImV4cCI6MTU5NjUwNDkzNiwiaWF0IjoxNTk1OTAwMTM2fQ.lkEunspinGeQK9sFoQs2WLpNticqOR4xv_18CQdOE_Y //自定義key x-token:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoie1wiVXNlcklkXCI6MTIzNDU2ODcsXCJUZW5hbnRJZFwiOjY1NDMyMSxcIlN0YXRpb25JZFwiOm51bGwsXCJTbWFydEJveFNuXCI6bnVsbH0iLCJuYmYiOjE1OTU5MDAxMzYsImV4cCI6MTU5NjUwNDkzNiwiaWF0IjoxNTk1OTAwMTM2fQ.lkEunspinGeQK9sFoQs2WLpNticqOR4xv_18CQdOE_Y //使用鏈接字符串方式 https://xxxxx/user/getUser?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoie1wiVXNlcklkXCI6MTIzNDU2ODcsXCJUZW5hbnRJZFwiOjY1NDMyMSxcIlN0YXRpb25JZFwiOm51bGwsXCJTbWFydEJveFNuXCI6bnVsbH0iLCJuYmYiOjE1OTU5MDAxMzYsImV4cCI6MTU5NjUwNDkzNiwiaWF0IjoxNTk1OTAwMTM2fQ.lkEunspinGeQK9sFoQs2WLpNticqOR4xv_18CQdOE_Y