using ERP.Framework.Cache; using ERP.Framework.Config; using ERP.Framework.Constants; using ERP.Framework.Exceptions; using ERP.Framework.Resource; using ERP.Framework.Security; using ERP.Framework.Utils; using ERP.Framework.WebApi; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Configuration; using Newtonsoft.Json; using System.IdentityModel.Tokens.Jwt; using System.Net; using System.Security.Claims; namespace ERP.Framework.Middleware { /// /// 验证 (解签 header附带用户id) useJwt /// public class AuthenticationMiddleware : IMiddleware { private readonly IConfiguration _configuration; public AuthenticationMiddleware(IConfiguration configuration) { this._configuration = configuration; } public async Task InvokeAsync(HttpContext context, RequestDelegate next) { var path = context.Request.Path.ToString(); var securityConfig = _configuration.GetSection(FrameworkConstant.SECURITY_CONFIG).Get() ?? new SecurityConfig(); if (securityConfig.WhiteList != null && UrlUtil.Match(path, securityConfig.WhiteList)) { await next(context); return; } var token = context.Request.Headers[AuthConstant.HEADER].ToString().Replace(FrameworkConstant.TOKEN_PREFIX, "").TrimStart(); var isValid = TokenHelper.ValidateToken(token, securityConfig.JwtSecurityKey); if (!isValid) { // 返回401状态码和自定义错误信息 context.Response.StatusCode = (int)HttpStatusCode.Unauthorized; context.Response.ContentType = "application/json"; var errorJson = JsonConvert.SerializeObject(new Result() { Code = ErrorCode.TOKEN_VALIDATE, Message = FrameworkI18N.TokenValidate! }); await context.Response.WriteAsync(errorJson); return; } var claims = TokenHelper.GetClaims(token); var tokenId = claims.FirstOrDefault(t => t.Type == JwtRegisteredClaimNames.UniqueName)!.Value; var userId = claims.FirstOrDefault(t => t.Type == JwtRegisteredClaimNames.NameId)!.Value; var userName = claims.FirstOrDefault(t => t.Type == ClaimTypes.Name)!.Value; var checkLastActivityResult = CheckLastActivity(tokenId, securityConfig.ActivityTimeOut); if (checkLastActivityResult != null) { context.Response.StatusCode = (int)HttpStatusCode.Unauthorized; context.Response.ContentType = "application/json"; await context.Response.WriteAsync(JsonConvert.SerializeObject(checkLastActivityResult)); return; } context.Items["TokenId"] = tokenId; context.Items["UserId"] = userId; context.Items["UserName"] = userName; await next(context); return; } private static Result? CheckLastActivity(string tokenId, int activityTimeOut) { DateTime date; var result = new Result(); var key = AuthConstant.LAST_ACTIVITY + tokenId; var lastActivity = RedisHelper.GetWithExpire(key); var value = lastActivity.Value.ToString(); var expireTime = lastActivity.Expiry; if (value.IsNullOrEmpty()) { result.Code = ErrorCode.IDLE_TIME_OUT; result.Message = FrameworkI18N.IdleTimeOut!; return result; } if (value == "-1") { result.Code = ErrorCode.KICKED_OUT; result.Message = FrameworkI18N.KickedOut!; return result; } if (value == "-2") { result.Code = ErrorCode.CONCURRENT_LOGIN; result.Message = FrameworkI18N.ConcurrentLogin!; return result; } if (DateTime.TryParse(value, out date)) { var isOnline = DateTime.Now <= date; if (isOnline) { SetLastActivityCache(tokenId, activityTimeOut, expireTime); } else { result.Code = ErrorCode.IDLE_TIME_OUT; result.Message = FrameworkI18N.IdleTimeOut!; return result; } } return null; } /// /// 刷新last Activity /// /// /// private static void SetLastActivityCache( string tokenId , int activityTimeOut , TimeSpan? expireTime) { var key = AuthConstant.LAST_ACTIVITY + tokenId; var val = DateTime.Now.AddMinutes(activityTimeOut).ToDateLongString(); RedisHelper.SaveExpire(key, val, expireTime); } } public static class AuthenticationMiddlewareExtensions { public static IApplicationBuilder UseAuthenticationMiddleware(this IApplicationBuilder builder) { return builder.UseMiddleware(); } } }