using Bowin.Common.Cache; using Bowin.Common.JSON; using Bowin.Common.Utility; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.SignalR; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.IdentityModel.Tokens; using System; using System.Collections.Generic; using System.IdentityModel.Tokens.Jwt; using System.IO; using System.Linq; using System.Linq.Expressions; using System.Runtime.CompilerServices; using System.Security.Claims; using System.Text; using System.Threading; using System.Threading.Tasks; namespace Bowin.Common.ServiceToken { public static class JwtHelper { private static JwtSettings Settings { get; set; } private const string AUTH_SECURITY_KEY = "6bb452e82b0f6ab0253d6db9f384996552dd3afcb9113d760a00a154cabc6cbd56c5f33c246a05090005a1e8dfc148388aa6593fdbb3b5a18a73153c07a5cdd3"; private const string AUTH_REFRESH_KEY = "4e64a7b3614c2710fef2fee37fe1b660302b65ca485fd57c9271e96bc36413e7c9c509a28190154b901d54abd4a45fc9b92f06fc3d798ea4b827f151f1e7c322"; public static Func> GetFunctionCodeMethod { get; set; } internal static Delegate GetRefreshUserFunc { get; set; } static JwtHelper() { var configuration = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile("appsettings.json") .Build(); Settings = configuration.GetSection("JwtSettings").Get(); } public static void AddBowinAuthentication(this IServiceCollection service, Func refreshUserFunc, Func> getFunctionGodeFunc = null) { GetRefreshUserFunc = refreshUserFunc; GetFunctionCodeMethod = getFunctionGodeFunc; service.AddAuthentication(options => { options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }) .AddJwtBearer(options => { options.RequireHttpsMetadata = false; options.SaveToken = true; options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidateIssuerSigningKey = true, ValidateLifetime = false, ValidAudience = Settings.Audience, ValidIssuer = Settings.Issuer, //AudienceValidator = (audiences, securityToken, validationParameters) => { // //var userID = CacheHelper.Get(securityToken.Id); // //if (userID == null) // //{ // // return false; // //} // return audiences != null && audiences.FirstOrDefault().Equals(Settings.Audience); //}, AudienceValidator = AudianValidator, IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(AUTH_SECURITY_KEY)) }; //options.Events = new JwtBearerEvents //{ // OnTokenValidated = (context => { // //var logger = HttpHelper.GetService>(); // var claims = context.Principal.Claims; // claims = claims.Where(x => x.Type != JwtRegisteredClaimNames.Aud && x.Type != JwtRegisteredClaimNames.Nbf && x.Type != JwtRegisteredClaimNames.Jti).ToArray(); // var userID = claims.FirstOrDefault(x => x.Type == ClaimTypes.Name).Value; // var nowTime = DateTime.Now; // nowTime.AddMilliseconds(0 - nowTime.Millisecond); // var jti = GetJTI(userID); // claims = claims//.Append(new Claim(JwtRegisteredClaimNames.Nbf, $"{ new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds() }")) // .Append(new Claim(JwtRegisteredClaimNames.Jti, jti)); // var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(AUTH_SECURITY_KEY)); // var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); // var token = new JwtSecurityToken( // issuer: Settings.Issuer, // audience: Settings.Audience, // claims: claims, // signingCredentials: creds // ); // var newToken = new JwtSecurityTokenHandler().WriteToken(token); // if (jti != context.SecurityToken.Id) // { // CacheHelper.Add(jti, userID, DateTime.Now.AddDays(1)); // CacheHelper.Set(context.SecurityToken.Id, userID, DateTime.Now.AddSeconds(2)); // } // //context.HttpContext.Response.Headers.Add("ServiceToken", newToken); // return System.Threading.Tasks.Task.CompletedTask; // }) //}; }); //service.AddSignalR(options => { // options.ClientTimeoutInterval = TimeSpan.FromHours(2); // options.KeepAliveInterval = TimeSpan.FromMinutes(2); //}); service.AddSingleton(); var serviceProvider = service.BuildServiceProvider(); HttpHelper.Accessor = serviceProvider.GetService(); HttpHelper.ServiceCollection = service; HttpHelper.WebHostEnvironment = serviceProvider.GetService(); } public static bool AudianValidator(IEnumerable audiences, SecurityToken securityToken, TokenValidationParameters validationParameters) { var curJTI = securityToken.Id; var userID = ((JwtSecurityToken)securityToken).Claims.Where(x => x.Type == ClaimTypes.Name).Select(x => x.Value).FirstOrDefault(); if (string.IsNullOrEmpty(userID) || GetJTI(userID) != curJTI) { return false; } var nowId = ((JwtSecurityToken)securityToken).Claims.Where(x => x.Type == ClaimTypes.NameIdentifier).Select(x => x.Value).FirstOrDefault(); if (string.IsNullOrEmpty(nowId)) { return false; } if (CacheHelper.Get(nowId) == null) { return false; } return audiences != null && audiences.FirstOrDefault().Equals(Settings.Audience); } private static string GetTimeStamp() { var nowTime = DateTime.Now; nowTime.AddMilliseconds(0 - nowTime.Millisecond); var timeStamp = new DateTimeOffset(nowTime).ToUnixTimeSeconds(); return timeStamp.ToString(); } private static string GetJTI(string userID) { var ip = "";//HttpHelper.GetUserIp(); return (userID + ip + HttpHelper.Current.Request.Host.ToString() + ip).MD5(); } public static BowinToken GetToken(Func getUserFunc, Expression> keyExp) { var userInfo = getUserFunc.Invoke(); if (userInfo == null) { throw new TokenLoginFailureException("帐号或密码错误。"); } var userID = keyExp.Compile().Invoke(userInfo); var jti = GetJTI(userID.ToString()); return GenerateToken(userInfo, jti, userID); } private static BowinToken GenerateToken(T userInfo, string jti, Guid userID) { var nowId = HttpHelper.Current.User.Claims.FirstOrDefault(x => x.Type == ClaimTypes.NameIdentifier)?.Value; var newId = Guid.NewGuid().ToString(); CacheHelper.Add("uinfo_" + userID.ToString(), userInfo.ToJson()); var claims = new[] { new Claim(JwtRegisteredClaimNames.Jti, jti), new Claim(JwtRegisteredClaimNames.Nbf, $"{ new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds() }"), new Claim(ClaimTypes.NameIdentifier, newId), new Claim(ClaimTypes.Name, userID.ToString()) }; if (GetFunctionCodeMethod != null) { CacheHelper.Add("rinfo_" + userID.ToString(), GetFunctionCodeMethod.Invoke(userID).ToJson()); } var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(AUTH_SECURITY_KEY)); var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); var token = new JwtSecurityToken( issuer: Settings.Issuer, audience: Settings.Audience, expires: DateTime.Now.AddMinutes(20), claims: claims, signingCredentials: creds ); if (nowId != null) { CacheHelper.Set(nowId, userID, DateTime.Now.AddSeconds(30)); } CacheHelper.Add(newId, userID, DateTime.Now.AddDays(1)); var endToken = new BowinToken { Token = new JwtSecurityTokenHandler().WriteToken(token), UserObj = userInfo }; return endToken; } public static BowinToken RefreshToken() { var claims = HttpHelper.Current.User.Claims; var userID = claims.Where(x => x.Type == ClaimTypes.Name).Select(x => x.Value).FirstOrDefault(); if (string.IsNullOrEmpty(userID)) { throw new Exception("无法获取用户信息,请检查访问令牌是否错误。"); } var jti = GetJTI(userID); var userInfo = JwtUser.Current; if (userInfo == null) { throw new Exception("无法获取用户信息,请检查访问令牌是否错误。"); } return GenerateToken(userInfo, jti, new Guid(userID)); } } public class TokenSender : Hub { public override async Task OnConnectedAsync() { await Groups.AddToGroupAsync(Context.ConnectionId, this.Context.GetHttpContext().Request.Query["UserID"]); } } public class TokenLoginFailureException : Exception { public TokenLoginFailureException(string message) : base(message) { } } public class JwtRereshTokenInvalidException : Exception { public JwtRereshTokenInvalidException(string message) : base(message) { } } }