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();
}
}
}