|
|
|
@ -3,6 +3,7 @@ using System.Security.Claims;
|
|
|
|
|
using System.Security.Policy;
|
|
|
|
|
using System.Security.Principal;
|
|
|
|
|
using System.Text;
|
|
|
|
|
using System.Text.RegularExpressions;
|
|
|
|
|
using System.Text.Unicode;
|
|
|
|
|
using System.Threading.Tasks.Sources;
|
|
|
|
|
|
|
|
|
@ -20,54 +21,95 @@ namespace AuthStudy.Authentication.Browser
|
|
|
|
|
/// 可实现子接口 IAuthenticationRequestHandler, 以控制后续中间件是否执行.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public class BrowserAuthenticationHandler<TOptions> :
|
|
|
|
|
IAuthenticationHandler, IAuthenticationRequestHandler, IAuthenticationSignInHandler, IAuthenticationSignOutHandler
|
|
|
|
|
IAuthenticationHandler, IAuthenticationRequestHandler,
|
|
|
|
|
IAuthenticationSignInHandler,
|
|
|
|
|
IAuthenticationSignOutHandler
|
|
|
|
|
where TOptions : AuthenticationSchemeOptions, new()
|
|
|
|
|
{
|
|
|
|
|
public const string DefaultAuthenticationScheme = BrowserAuthenticationDefault.AuthenticationSchemeName;
|
|
|
|
|
public string DefaultSchemeName = BrowserAuthenticationDefault.SchemeName;
|
|
|
|
|
|
|
|
|
|
public HttpContext? CurrentHttpContext;
|
|
|
|
|
|
|
|
|
|
public List<string> supportedBrowsers = new() { "Chrome", "Edge" };
|
|
|
|
|
public List<string> AllowBrowsers = BrowserAuthenticationDefault.AllowBrowsers;
|
|
|
|
|
|
|
|
|
|
public BrowserAuthenticationOptions Options = BrowserAuthenticationDefault.DefaultOptions;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public BrowserAuthenticationHandler()
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 认证
|
|
|
|
|
/// </summary>
|
|
|
|
|
public Task<AuthenticateResult> AuthenticateAsync()
|
|
|
|
|
{
|
|
|
|
|
//认证结果
|
|
|
|
|
AuthenticateResult result = AuthenticateResult.NoResult();
|
|
|
|
|
|
|
|
|
|
//属性
|
|
|
|
|
var properties = new AuthenticationProperties();
|
|
|
|
|
properties.Items.Add("x-test", "测试");
|
|
|
|
|
properties.Items.Add("AuthenticationBrowser", "浏览器认证属性");
|
|
|
|
|
|
|
|
|
|
//获取浏览器信息
|
|
|
|
|
CurrentHttpContext?.Request.Headers.TryGetValue("User-Agentx", out StringValues browserInfo);
|
|
|
|
|
//获取请求浏览器信息,如果请头重复则以后面的为准
|
|
|
|
|
var userAgent = CurrentHttpContext?.Request.Headers["User-Agent"].LastOrDefault();
|
|
|
|
|
if (userAgent == null)
|
|
|
|
|
{
|
|
|
|
|
properties.UpdateTokenValue("AuthenticationBrowser", "失败:获取不到浏览器信息");
|
|
|
|
|
result = AuthenticateResult.Fail($"失败:获取不到浏览器信息", properties);
|
|
|
|
|
return Task.FromResult(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ClientInfo clientInfo = Parser.GetDefault().Parse(CurrentHttpContext?.Request.Headers["User-Agent"]);
|
|
|
|
|
if (!supportedBrowsers.Contains(clientInfo.UA.Family))
|
|
|
|
|
ClientInfo clientInfo = Parser.GetDefault().Parse(userAgent);
|
|
|
|
|
|
|
|
|
|
//移动设备认证
|
|
|
|
|
if (!Options.AllowMobile && IsMobile(clientInfo.UA.Family))
|
|
|
|
|
{
|
|
|
|
|
var success = AuthenticateResult.Fail($"不支持的浏览器:{clientInfo.UA.Family}", properties);
|
|
|
|
|
properties.UpdateTokenValue("AuthenticationBrowser", "失败:不被允许的可移动设备");
|
|
|
|
|
result = AuthenticateResult.Fail($"不被允许的可移动设备:{clientInfo.UA.Family}", properties);
|
|
|
|
|
return Task.FromResult(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Task.FromResult(success);
|
|
|
|
|
//爬虫认证
|
|
|
|
|
if (!Options.AllowSpider && clientInfo.Device.IsSpider)
|
|
|
|
|
{
|
|
|
|
|
properties.UpdateTokenValue("AuthenticationBrowser", "失败:不允许爬虫");
|
|
|
|
|
result = AuthenticateResult.Fail($"不允许爬虫", properties);
|
|
|
|
|
return Task.FromResult(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//浏览器类型认证
|
|
|
|
|
if (!AllowBrowsers.Contains(clientInfo.UA.Family))
|
|
|
|
|
{
|
|
|
|
|
properties.UpdateTokenValue("AuthenticationBrowser", "失败:不支持的浏览器");
|
|
|
|
|
result = AuthenticateResult.Fail($"不支持的浏览器:{clientInfo.UA.Family}", properties);
|
|
|
|
|
|
|
|
|
|
return Task.FromResult(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//声明(身份项)
|
|
|
|
|
var role = new Claim(ClaimTypes.Role, "Admin");
|
|
|
|
|
var email = new Claim(ClaimTypes.Email, "bicijinlian@168.com");
|
|
|
|
|
var browser = new Claim("Browser", clientInfo.UA.ToString()); //浏览器
|
|
|
|
|
var os = new Claim("OS", clientInfo.OS.ToString()); //操作系统
|
|
|
|
|
var device = new Claim("Device", clientInfo.Device.ToString()); //设备 //设备
|
|
|
|
|
|
|
|
|
|
//声明集合
|
|
|
|
|
var Claims = new List<Claim>();
|
|
|
|
|
Claims.Add(role);
|
|
|
|
|
Claims.Add(email);
|
|
|
|
|
Claims.Add(browser);
|
|
|
|
|
Claims.Add(os);
|
|
|
|
|
Claims.Add(device);
|
|
|
|
|
|
|
|
|
|
//身份:包含声明集合,是声明集合的包装类,一个身份对应多个声明
|
|
|
|
|
var claimsIdentity = new ClaimsIdentity(Claims, DefaultAuthenticationScheme);
|
|
|
|
|
var claimsIdentity = new ClaimsIdentity(Claims, DefaultSchemeName);
|
|
|
|
|
|
|
|
|
|
//当事人/主角:是身份Identity的包装,对应多个身份
|
|
|
|
|
var claimsPrincipal = new ClaimsPrincipal(claimsIdentity);
|
|
|
|
|
|
|
|
|
|
//票据:对Principal的包装,一对一
|
|
|
|
|
var ticket = new AuthenticationTicket(claimsPrincipal, DefaultAuthenticationScheme);
|
|
|
|
|
var ticket = new AuthenticationTicket(claimsPrincipal, DefaultSchemeName);
|
|
|
|
|
|
|
|
|
|
//认证结果:认证信息会写入 当前请求的 User属性中,供下一个授权中间件使用
|
|
|
|
|
var result = AuthenticateResult.Success(ticket);
|
|
|
|
|
result = AuthenticateResult.Success(ticket);
|
|
|
|
|
|
|
|
|
|
//调用登陆
|
|
|
|
|
//CurrentHttpContext?.SignInAsync(BrowserAuthentication.DefaultAuthenticationScheme, claimsPrincipal, properties);
|
|
|
|
@ -110,7 +152,6 @@ namespace AuthStudy.Authentication.Browser
|
|
|
|
|
/// IAuthenticationRequestHandler
|
|
|
|
|
/// 返回true,立即反回,不执行后续中间件
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
public Task<bool> HandleRequestAsync()
|
|
|
|
|
{
|
|
|
|
|
return Task.FromResult(false);
|
|
|
|
@ -135,8 +176,8 @@ namespace AuthStudy.Authentication.Browser
|
|
|
|
|
/// </summary>
|
|
|
|
|
public Task SignInAsync(ClaimsPrincipal user, AuthenticationProperties? properties)
|
|
|
|
|
{
|
|
|
|
|
//导航到登陆页
|
|
|
|
|
CurrentHttpContext?.Response.Redirect("https://www.baidu.com");
|
|
|
|
|
//导航到主页
|
|
|
|
|
//CurrentHttpContext?.Response.Redirect("/api/app/index");
|
|
|
|
|
|
|
|
|
|
return Task.CompletedTask;
|
|
|
|
|
}
|
|
|
|
@ -151,5 +192,23 @@ namespace AuthStudy.Authentication.Browser
|
|
|
|
|
CurrentHttpContext?.Response.Redirect("/api/auth/login");
|
|
|
|
|
return Task.CompletedTask;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private bool IsMobile(string deviceInfo)
|
|
|
|
|
{
|
|
|
|
|
bool isMobile = false;
|
|
|
|
|
|
|
|
|
|
if (string.IsNullOrWhiteSpace(deviceInfo))
|
|
|
|
|
{
|
|
|
|
|
return isMobile;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Regex phoneRegex = new Regex(@"(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino", RegexOptions.IgnoreCase | RegexOptions.Multiline);
|
|
|
|
|
if (phoneRegex.IsMatch(deviceInfo))
|
|
|
|
|
{
|
|
|
|
|
isMobile = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return isMobile;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|