You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

155 lines
5.8 KiB
C#

using System.Security;
using System.Security.Claims;
using System.Security.Policy;
using System.Security.Principal;
using System.Text;
using System.Text.Unicode;
using System.Threading.Tasks.Sources;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Primitives;
using UAParser;
namespace AuthStudy.Authentication.Browser
{
/// <summary>
/// 浏览器认证 处理器
/// 可实现子接口 IAuthenticationRequestHandler, 以控制后续中间件是否执行.
/// </summary>
public class BrowserAuthenticationHandler<TOptions> :
IAuthenticationHandler, IAuthenticationRequestHandler, IAuthenticationSignInHandler, IAuthenticationSignOutHandler
where TOptions : AuthenticationSchemeOptions, new()
{
public const string DefaultAuthenticationScheme = BrowserAuthenticationDefault.AuthenticationSchemeName;
public HttpContext? CurrentHttpContext;
public List<string> supportedBrowsers = new() { "Chrome", "Edge" };
/// <summary>
/// 认证
/// </summary>
public Task<AuthenticateResult> AuthenticateAsync()
{
var properties = new AuthenticationProperties();
properties.Items.Add("x-test", "测试");
//获取浏览器信息
CurrentHttpContext?.Request.Headers.TryGetValue("User-Agentx", out StringValues browserInfo);
ClientInfo clientInfo = Parser.GetDefault().Parse(CurrentHttpContext?.Request.Headers["User-Agent"]);
if (!supportedBrowsers.Contains(clientInfo.UA.Family))
{
var success = AuthenticateResult.Fail($"不支持的浏览器:{clientInfo.UA.Family}", properties);
return Task.FromResult(success);
}
//声明(身份项)
var role = new Claim(ClaimTypes.Role, "Admin");
var email = new Claim(ClaimTypes.Email, "bicijinlian@168.com");
//声明集合
var Claims = new List<Claim>();
Claims.Add(role);
Claims.Add(email);
//身份:包含声明集合,是声明集合的包装类,一个身份对应多个声明
var claimsIdentity = new ClaimsIdentity(Claims, DefaultAuthenticationScheme);
//当事人/主角是身份Identity的包装对应多个身份
var claimsPrincipal = new ClaimsPrincipal(claimsIdentity);
//票据对Principal的包装一对一
var ticket = new AuthenticationTicket(claimsPrincipal, DefaultAuthenticationScheme);
//认证结果:认证信息会写入 当前请求的 User属性中供下一个授权中间件使用
var result = AuthenticateResult.Success(ticket);
//调用登陆
//CurrentHttpContext?.SignInAsync(BrowserAuthentication.DefaultAuthenticationScheme, claimsPrincipal, properties);
return Task.FromResult(result);
}
/// <summary>
/// 无认证:服务端向客户端(浏览器)发质询(要求提供一个新票据),质询体现为 htpp请求的响应。
/// </summary>
public Task ChallengeAsync(AuthenticationProperties? properties)
{
properties?.Parameters.Add("x-itme", "无效的认证");
CurrentHttpContext!.Response.StatusCode = 401;
if (CurrentHttpContext?.Response.Body.CanWrite ?? false)
{
var msg = UTF8Encoding.UTF8.GetBytes("认证无效");
CurrentHttpContext!.Response.Body.WriteAsync(msg);
}
CurrentHttpContext?.Items.Add("认证结束时间", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
return Task.CompletedTask;
}
/// <summary>
/// 无权限:服务端向客户端(浏览器)发质询(要求提供一个新票据),质询体现为 htpp请求的响应。
/// </summary>
public Task ForbidAsync(AuthenticationProperties? properties)
{
CurrentHttpContext!.Response.StatusCode = 403;
if (CurrentHttpContext?.Response.Body.CanWrite ?? false)
{
var msg = UTF8Encoding.UTF8.GetBytes("无权访问");
CurrentHttpContext!.Response.Body.WriteAsync(msg);
}
return Task.CompletedTask;
}
/// <summary>
/// IAuthenticationRequestHandler
/// 返回true,立即反回,不执行后续中间件
/// </summary>
/// <returns></returns>
public Task<bool> HandleRequestAsync()
{
return Task.FromResult(false);
}
/// <summary>
/// 初始化
/// </summary>
public async Task InitializeAsync(AuthenticationScheme scheme, Microsoft.AspNetCore.Http.HttpContext context)
{
//初始化工作,传递给认证方法和授权中间件
CurrentHttpContext = context;
context.Items.Add("认证初始时间", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
await Task.CompletedTask;
}
/// <summary>
/// 登陆方法
/// 写入Cookie和Session认证信息持久化等
/// </summary>
public Task SignInAsync(ClaimsPrincipal user, AuthenticationProperties? properties)
{
//导航到登陆页
CurrentHttpContext?.Response.Redirect("https://www.baidu.com");
return Task.CompletedTask;
}
/// <summary>
/// 退出方法: 反操作登陆方法
/// 清除Cookie和Session删除认证信息的持久化作废票据等
/// </summary>
public Task SignOutAsync(AuthenticationProperties? properties)
{
//导航到登陆页
CurrentHttpContext?.Response.Redirect("/api/auth/login");
return Task.CompletedTask;
}
}
}