using Bootstrap.Security.Authentication; using Bootstrap.Security.Mvc; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.IdentityModel.Tokens; using System; using System.Collections.Generic; using System.IdentityModel.Tokens.Jwt; using System.Linq; using System.Security.Claims; using System.Text; namespace Microsoft.AspNetCore.Builder { /// /// BootstrapAdminAuthorization 认证服务扩展类 /// public static class AuthenticationExtensions { #nullable disable private static Func> _urlAuthHandler; private static Func> _appAuthHandler; private static Func> _userAuthHandler; #nullable restore /// /// 添加 BootstrapAdmin 认证授权服务,内部调用 UseAuthentication /// /// IApplicationBuilder 实例 /// 通过用户名称获得角色集合代理方法 /// 通过请求地址获得角色集合代理方法 /// 通过用户名称获得应用程序集合代理方法 public static IApplicationBuilder UseBootstrapAdminAuthentication(this IApplicationBuilder builder, Func> userNameAuthHandler, Func> urlAuthHandler, Func> appAuthHandler) { builder.UseAuthentication(); // 增加模拟用户中间件 builder.Use(async (context, next) => { BootstrapAppContext.SetProvider(context.RequestServices); var userName = context.RequestServices.GetRequiredService().GetValue("SimulateUserName", ""); if (!string.IsNullOrEmpty(userName)) { var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme); identity.AddClaim(new Claim(ClaimTypes.Name, userName)); context.User = new ClaimsPrincipal(identity); } // 检查用户登录名 if (context.User.Identity.IsAuthenticated && (string.IsNullOrEmpty(context.User.Identity.Name) || !userNameAuthHandler(context.User.Identity.Name).Any())) { await context.SignOutAsync(); await context.ChallengeAsync(); return; } if (context.User.Identity.IsAuthenticated && !string.IsNullOrEmpty(context.User.Identity.Name)) { AddRoles(context.User, RetrieveRolesByUserName(context.User.Identity.Name), new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme)); } await next(); }); // Web api Validate builder.UseWhen(context => context.Request.Path.StartsWithSegments("/api"), app => app.Use(async (context, next) => { if (!context.User.Identity.IsAuthenticated) { JwtAuthentication(context); } if (context.User.Identity.IsAuthenticated && !string.IsNullOrEmpty(context.User.Identity.Name)) { AddRoles(context.User, RetrieveRolesByUserName(context.User.Identity.Name), new ClaimsIdentity(JwtBearerDefaults.AuthenticationScheme)); } await next(); })); _urlAuthHandler = urlAuthHandler; _appAuthHandler = appAuthHandler; _userAuthHandler = userNameAuthHandler; return builder; } /// /// 通过指定用户名获取授权角色集合 /// /// /// internal static IEnumerable RetrieveRolesByUserName(string userName) => _userAuthHandler(userName); /// /// 通过指定访问地址获取授权角色集合 /// /// 指定地址 /// 应用程序ID /// 角色集合 internal static IEnumerable RetrieveRolesByUrl(string url, string appId) => _urlAuthHandler(url, appId); /// /// 通过指定用户名获取授权 App 集合 /// /// 用户名 /// 应用程序集合 internal static IEnumerable RetrieveAppsByUserName(string userName) => _appAuthHandler(userName); /// /// 添加 Claim 到当前用户实例中 /// /// /// /// internal static void AddRoles(ClaimsPrincipal user, IEnumerable roles, ClaimsIdentity identity) { roles?.ToList().ForEach(role => identity.AddClaim(new Claim(ClaimTypes.Role, role))); user.AddIdentity(identity); } private static void JwtAuthentication(HttpContext context) { // Jwtbeare authorization var token = context.Request.Headers["Authorization"].LastOrDefault(); if (!string.IsNullOrEmpty(token) && token.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase)) { token = token.Substring(7); // validate token var tokenOption = context.RequestServices.GetRequiredService().GetOption(() => new TokenValidateOption()); var tokenHandler = new JwtSecurityTokenHandler(); context.User = tokenHandler.ValidateToken(token, new TokenValidationParameters() { IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(tokenOption.SecurityKey)), ValidIssuer = tokenOption.Issuer, ValidAudience = tokenOption.Audience }, out var securityToken); } } } }