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