AspNetCore3.1_Secutiry源码解析_3_Authentication_Cookies
文章目录
- AspNetCore3.1_Secutiry源码解析_1_目录
- AspNetCore3.1_Secutiry源码解析_2_Authentication_核心项目
- AspNetCore3.1_Secutiry源码解析_3_Authentication_Cookies
- AspNetCore3.1_Secutiry源码解析_4_Authentication_JwtBear
- AspNetCore3.1_Secutiry源码解析_5_Authentication_OAuth
- AspNetCore3.1_Secutiry源码解析_6_Authentication_OpenIdConnect
- AspNetCore3.1_Secutiry源码解析_7_Authentication_其他
- AspNetCore3.1_Secutiry源码解析_8_Authorization_授权框架
依赖注入
AuthenticationBuilder AddCookie(this AuthenticationBuilder builder);
AuthenticationBuilder AddCookie(this AuthenticationBuilder builder, string authenticationScheme);
AuthenticationBuilder AddCookie(this AuthenticationBuilder builder, Action<CookieAuthenticationOptions> configureOptions);
提供了几个重载方法,可以使用默认配置,或者通过委托修改配置类CookieAuthenticationOptions的值。
可以定义登录、登出、拒绝登录页面地址、Cookie过期时间、生命周期各阶段事件等。
class CookieAuthenticationOptions{
CookieBuilder Cookie
IDataProtectionProvider DataProtectionProvider
bool SlidingExpiration
PathString LoginPath
PathString LogoutPath
PathString AccessDeniedPath
CookieAuthenticationEvents Events
ISecureDataFormat TicketDataFormat
ITicketStore SessionStore
TimeSpan ExpireTimeSpan
}
class AuthenticationSchemeOptions{
string ClaimsIssuer
object Events
Type EventsType
string ForwardDefault
string ForwardAuthenticate
string ForwardChallenge
string ForwardForbid
string ForwardSignIn
string ForwardSignOut
Func ForwardDefaultSelector
}
CookieAuthenticationOptions-->AuthenticationSchemeOptions
如果没有定义配置,则会使用CookieAuthenticationDefaults定义的默认配置
/// <summary>
/// Default values related to cookie-based authentication handler
/// </summary>
public static class CookieAuthenticationDefaults
{
/// <summary>
/// The default value used for CookieAuthenticationOptions.AuthenticationScheme
/// </summary>
public const string AuthenticationScheme = "Cookies";
/// <summary>
/// The prefix used to provide a default CookieAuthenticationOptions.CookieName
/// </summary>
public static readonly string CookiePrefix = ".AspNetCore.";
/// <summary>
/// The default value used by CookieAuthenticationMiddleware for the
/// CookieAuthenticationOptions.LoginPath
/// </summary>
public static readonly PathString LoginPath = new PathString("/Account/Login");
/// <summary>
/// The default value used by CookieAuthenticationMiddleware for the
/// CookieAuthenticationOptions.LogoutPath
/// </summary>
public static readonly PathString LogoutPath = new PathString("/Account/Logout");
/// <summary>
/// The default value used by CookieAuthenticationMiddleware for the
/// CookieAuthenticationOptions.AccessDeniedPath
/// </summary>
public static readonly PathString AccessDeniedPath = new PathString("/Account/AccessDenied");
/// <summary>
/// The default value of the CookieAuthenticationOptions.ReturnUrlParameter
/// </summary>
public static readonly string ReturnUrlParameter = "ReturnUrl";
}
注册当前schema的处理器类为CookieAuthenticationHandler
处理器类的结构
主干逻辑是层层继承来实现的,CookieAuthenticationHandler主要是重写了父类的五个认证动作的Handle方法来实现自己的处理逻辑。
class CookieAuthenticationHandler{
HandleAuthenticateAsync()
HandleSignInAsync()
HandleSignOutAsync()
HandleForbiddenAsync()
HandleChallengeAsync()
FinishResponseAsync()
}
class SignInAuthenticationHandler{
SignInAsync()
HandleSignInAsync()
}
class IAuthenticationSignInHandler{
SignIn()
HandleSignIn()
}
class SignOutAuthenticationHandler{
SignOutAsync()
HandleSignOutAsync()
}
class IAuthenticationSignOutHandler{
SighOut()
HandleSignOut()
}
class AuthenticationHandler{
AuthenticationScheme Scheme
TOptions Options
HttpContext Context
HttpRequest Request
HttpResponse Response
PathString OriginalPath
PathString OriginalPathBase
ILogger Logger
UrlEncoder UrlEncoder
ISystemClock Clock
object Events
string ClaimsIssuer
string CurrentUri
+Task InitializeAsync(AuthenticationScheme scheme, HttpContext context)
+Task AuthenticateAsync()
+Task ChallengeAsync(AuthenticationProperties properties)
+Task ForbidAsync(AuthenticationProperties properties)
}
class IAuthenticationHandler{
HandleAsync()
}
CookieAuthenticationHandler-->SignInAuthenticationHandler
SignInAuthenticationHandler-->IAuthenticationSignInHandler
SignInAuthenticationHandler-->SignOutAuthenticationHandler
SignOutAuthenticationHandler-->IAuthenticationSignOutHandler
SignOutAuthenticationHandler-->AuthenticationHandler
AuthenticationHandler-->IAuthenticationHandler
处理器类详解
HandleSignInAsync - 处理登录
- 业务方校验完用户之后之后,构造ClaimsPrincipal对象传入SignIn方法,如果user为null则抛出异常
- IssuedUtc如果未指定的话则使用当前时间,ExpiresUtc过期时间如果没有指定的话则用IssuedUtc和ExpireTimeSpan计算出过期时间
- 触发SigningIn事件
- 构造AuthenticationTicket凭证
- 如果SessionStore不为空,将凭证信息存入SessionStore
- TicketDataFormat对ticket进行加密
- CookieManager将t加密后的信息写入cookie
- 触发SignedIn事件
- 如果LoginPath有值并且等于OriginalPath,则需要跳转,跳转地址在Properties.RedirectUri
protected async override Task HandleSignInAsync(ClaimsPrincipal user, AuthenticationProperties properties)
{
if (user == null)
{
throw new ArgumentNullException(nameof(user));
}
properties = properties ?? new AuthenticationProperties();
_signInCalled = true;
// Process the request cookie to initialize members like _sessionKey.
await EnsureCookieTicket();
var cookieOptions = BuildCookieOptions();
var signInContext = new CookieSigningInContext(
Context,
Scheme,
Options,
user,
properties,
cookieOptions);
DateTimeOffset issuedUtc;
if (signInContext.Properties.IssuedUtc.HasValue)
{
issuedUtc = signInContext.Properties.IssuedUtc.Value;
}
else
{
issuedUtc = Clock.UtcNow;
signInContext.Properties.IssuedUtc = issuedUtc;
}
if (!signInContext.Properties.ExpiresUtc.HasValue)
{
signInContext.Properties.ExpiresUtc = issuedUtc.Add(Options.ExpireTimeSpan);
}
await Events.SigningIn(signInContext);
if (signInContext.Properties.IsPersistent)
{
var expiresUtc = signInContext.Properties.ExpiresUtc ?? issuedUtc.Add(Options.ExpireTimeSpan);
signInContext.CookieOptions.Expires = expiresUtc.ToUniversalTime();
}
var ticket = new AuthenticationTicket(signInContext.Principal, signInContext.Properties, signInContext.Scheme.Name);
if (Options.SessionStore != null)
{
if (_sessionKey != null)
{
await Options.SessionStore.RemoveAsync(_sessionKey);
}
_sessionKey = await Options.SessionStore.StoreAsync(ticket);
var principal = new ClaimsPrincipal(
new ClaimsIdentity(
new[] { new Claim(SessionIdClaim, _sessionKey, ClaimValueTypes.String, Options.ClaimsIssuer) },
Options.ClaimsIssuer));
ticket = new AuthenticationTicket(principal, null, Scheme.Name);
}
var cookieValue = Options.TicketDataFormat.Protect(ticket, GetTlsTokenBinding());
Options.CookieManager.AppendResponseCookie(
Context,
Options.Cookie.Name,
cookieValue,
signInContext.CookieOptions);
var signedInContext = new CookieSignedInContext(
Context,
Scheme,
signInContext.Principal,
signInContext.Properties,
Options);
await Events.SignedIn(signedInContext);
// Only redirect on the login path
var shouldRedirect = Options.LoginPath.HasValue && OriginalPath == Options.LoginPath;
await ApplyHeaders(shouldRedirect, signedInContext.Properties);
Logger.AuthenticationSchemeSignedIn(Scheme.Name);
}
HandleAuthentication - 处理认证
- 从Cookie中读取凭证:首先TicketDataFormat类将Cookie解码,如果SessionStore不为null,说明解码值是只是session的key,从SessionStore中取出值。
- 构建CookieValidatePrincipalContext,触发ValidatePrincipal事件
- 如果ShouldRenew位true,则会刷新cookie(ShoudRenew默认为false,可以通过订阅ValidatePrincipal事件来修改)
- 认证成功,发放凭证AuthenticationTicket,包括context.Principal, context.Properties, Scheme.Name这些信息
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
var result = await EnsureCookieTicket();
if (!result.Succeeded)
{
return result;
}
var context = new CookieValidatePrincipalContext(Context, Scheme, Options, result.Ticket);
await Events.ValidatePrincipal(context);
if (context.Principal == null)
{
return AuthenticateResult.Fail("No principal.");
}
if (context.ShouldRenew)
{
RequestRefresh(result.Ticket, context.Principal);
}
return AuthenticateResult.Success(new AuthenticationTicket(context.Principal, context.Properties, Scheme.Name));
}
HandleSignOutAsync - 处理登出
- 获取凭证
- SessionStore不为null的话则从SessionStore移除会话
- 触发SigningOut事件
- CookieManager删除cookie
- 如果源地址是LogoutPath,则跳转到登出后地址
protected async override Task HandleSignOutAsync(AuthenticationProperties properties)
{
properties = properties ?? new AuthenticationProperties();
_signOutCalled = true;
// Process the request cookie to initialize members like _sessionKey.
await EnsureCookieTicket();
var cookieOptions = BuildCookieOptions();
if (Options.SessionStore != null && _sessionKey != null)
{
await Options.SessionStore.RemoveAsync(_sessionKey);
}
var context = new CookieSigningOutContext(
Context,
Scheme,
Options,
properties,
cookieOptions);
await Events.SigningOut(context);
Options.CookieManager.DeleteCookie(
Context,
Options.Cookie.Name,
context.CookieOptions);
// Only redirect on the logout path
var shouldRedirect = Options.LogoutPath.HasValue && OriginalPath == Options.LogoutPath;
await ApplyHeaders(shouldRedirect, context.Properties);
Logger.AuthenticationSchemeSignedOut(Scheme.Name);
}
HandleForbidAsync -- 处理禁止访问
如果是ajax请求会返回403状态码,否则跳转到配置的AccessDeniedPath
protected override async Task HandleForbiddenAsync(AuthenticationProperties properties)
{
var returnUrl = properties.RedirectUri;
if (string.IsNullOrEmpty(returnUrl))
{
returnUrl = OriginalPathBase + OriginalPath + Request.QueryString;
}
var accessDeniedUri = Options.AccessDeniedPath + QueryString.Create(Options.ReturnUrlParameter, returnUrl);
var redirectContext = new RedirectContext<CookieAuthenticationOptions>(Context, Scheme, Options, properties, BuildRedirectUri(accessDeniedUri));
await Events.RedirectToAccessDenied(redirectContext);
}
public Func<RedirectContext<CookieAuthenticationOptions>, Task> OnRedirectToAccessDenied { get; set; } = context =>
{
if (IsAjaxRequest(context.Request))
{
context.Response.Headers[HeaderNames.Location] = context.RedirectUri;
context.Response.StatusCode = 403;
}
else
{
context.Response.Redirect(context.RedirectUri);
}
return Task.CompletedTask;
};
其他
ICookieManager - Cookie管理类
默认实现是ChunkingCookieManager,如果cookie过长,该类会将cookie拆分位多个chunk。
/// <summary>
/// This is used by the CookieAuthenticationMiddleware to process request and response cookies.
/// It is abstracted from the normal cookie APIs to allow for complex operations like chunking.
/// </summary>
public interface ICookieManager
{
/// <summary>
/// Retrieve a cookie of the given name from the request.
/// </summary>
/// <param name="context"></param>
/// <param name="key"></param>
/// <returns></returns>
string GetRequestCookie(HttpContext context, string key);
/// <summary>
/// Append the given cookie to the response.
/// </summary>
/// <param name="context"></param>
/// <param name="key"></param>
/// <param name="value"></param>
/// <param name="options"></param>
void AppendResponseCookie(HttpContext context, string key, string value, CookieOptions options);
/// <summary>
/// Append a delete cookie to the response.
/// </summary>
/// <param name="context"></param>
/// <param name="key"></param>
/// <param name="options"></param>
void DeleteCookie(HttpContext context, string key, CookieOptions options);
}
ITicketStore - 实现Cookie持久化
ITicketStore默认是没有实现的,如果实现该接口并注入的话,可以将cookie持久化,这样暴露在浏览器的只是一个cookie的id。
/// <summary>
/// This provides an abstract storage mechanic to preserve identity information on the server
/// while only sending a simple identifier key to the client. This is most commonly used to mitigate
/// issues with serializing large identities into cookies.
/// </summary>
public interface ITicketStore
{
/// <summary>
/// Store the identity ticket and return the associated key.
/// </summary>
/// <param name="ticket">The identity information to store.</param>
/// <returns>The key that can be used to retrieve the identity later.</returns>
Task<string> StoreAsync(AuthenticationTicket ticket);
/// <summary>
/// Tells the store that the given identity should be updated.
/// </summary>
/// <param name="key"></param>
/// <param name="ticket"></param>
/// <returns></returns>
Task RenewAsync(string key, AuthenticationTicket ticket);
/// <summary>
/// Retrieves an identity from the store for the given key.
/// </summary>
/// <param name="key">The key associated with the identity.</param>
/// <returns>The identity associated with the given key, or if not found.</returns>
Task<AuthenticationTicket> RetrieveAsync(string key);
/// <summary>
/// Remove the identity associated with the given key.
/// </summary>
/// <param name="key">The key associated with the identity.</param>
/// <returns></returns>
Task RemoveAsync(string key);
}
AspNetCore3.1_Secutiry源码解析_3_Authentication_Cookies的更多相关文章
- AspNetCore3.1_Secutiry源码解析_1_目录
文章目录 AspNetCore3.1_Secutiry源码解析_1_目录 AspNetCore3.1_Secutiry源码解析_2_Authentication_核心项目 AspNetCore3.1_ ...
- AspNetCore3.1_Secutiry源码解析_2_Authentication_核心对象
系列文章目录 AspNetCore3.1_Secutiry源码解析_1_目录 AspNetCore3.1_Secutiry源码解析_2_Authentication_核心项目 AspNetCore3. ...
- AspNetCore3.1_Secutiry源码解析_4_Authentication_JwtBear
title: "AspNetCore3.1_Secutiry源码解析_4_Authentication_JwtBear" date: 2020-03-22T16:29:29+08: ...
- AspNetCore3.1_Secutiry源码解析_5_Authentication_OAuth
title: "AspNetCore3.1_Secutiry源码解析_5_Authentication_OAuth" date: 2020-03-24T23:27:45+08:00 ...
- AspNetCore3.1_Secutiry源码解析_6_Authentication_OpenIdConnect
title: "AspNetCore3.1_Secutiry源码解析_6_Authentication_OpenIdConnect" date: 2020-03-25T21:33: ...
- AspNetCore3.1_Secutiry源码解析_8_Authorization_授权框架
目录 AspNetCore3.1_Secutiry源码解析_1_目录 AspNetCore3.1_Secutiry源码解析_2_Authentication_核心流程 AspNetCore3.1_Se ...
- AspNetCore3.1源码解析_2_Hsts中间件
title: "AspNetCore3.1源码解析_2_Hsts中间件" date: 2020-03-16T12:40:46+08:00 draft: false --- 概述 在 ...
- AspNetCore3.1_Middleware源码解析_3_HttpsRedirection
概述 上文提到3.1版本默认没有使用Hsts,但是使用了这个中间件.看名字就很好理解,https跳转,顾名思义,就是跳转到 https地址. 使用场景,当用户使用http访问网站时,自动跳转到http ...
- 【原】Android热更新开源项目Tinker源码解析系列之三:so热更新
本系列将从以下三个方面对Tinker进行源码解析: Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Android热更新开源项目Tinker源码解析系列之二:资源文件热更新 A ...
随机推荐
- 吴裕雄--天生自然python学习笔记:Python MySQL - mysql-connector 驱动
本章节我们为大家介绍使用 mysql-connector 来连接使用 MySQL, mysql-connector 是 MySQL 官方提供的驱动器. 我们可以使用 pip 命令来安装 mysql-c ...
- 吴裕雄--天生自然python学习笔记:Python3 模块
Python3 模块 在前面的几个章节中我们脚本上是用 python 解释器来编程,如果你从 Python 解释器退出再进入,那么你定义的所有的方法和变量就都消失了. 为此 Python 提供了一个办 ...
- haproxy笔记之一:Haproxy基本安装配置(反向代理,类似Nginx,可以代理tcp的连接,不只是http)(注意iptables和selinux的问题)
1.安装haproxy yum -y install haproxy 2.配置文件 # this config needs haproxy- or haproxy- global log 127.0. ...
- Java实现Luhm算法--银行卡号合法性校验
银行卡是由"发卡行标识代码 + 自定义 + 校验码 "等部分组成的. 银联标准卡与以往发行的银行卡最直接的区别就是其卡号前6位数字的不同. 银行卡卡号的前6位是用来表示发卡银行 ...
- vm文件的优点
vm文件的优点 相较于内容写在jsp 文件: 1.在网页上上浏览和下载的内容用的是同一套,也就是说只需要维护一套内容,页面上看到的和下载得到的是一致的. 2.版本控制较为简便, 实现了页面内容和jsp ...
- nodejs+vue.js+webpack
前端: nodejs+vue.js+webpack 后台:ssb(Spring+SpringMVC + mybatis-plus) 开发工具:idea 一.前提 1.安装nodejs 2.安装完nod ...
- python Dom
Dom(Document) 称为:文档对象模型,是一种用于HTML和XML文档的编程接口.它给文档提供了一种结构化的表示方法,可以改变文档的内容和呈现方式.DOM把网页和脚本以及其他的编程语言联系了起 ...
- [PHP] 报错和“@” - 工作
以下几点关于报错和@的关系理解 在PHP中使用@加在某个语句前面,可以抑制错误在脚本的执行过程中输出到IO: 如果在某个语句上使用了@,则可以将语句内的各种嵌套的脚本可能存在的报错都会被抑制住: 如果 ...
- Nginx部署前后端分离服务
飘过... 一,安装Nginx 二,配置nginx 一般nginx配置文件在etc目录下 另,如何找nginx.conf配置文件: 在前后端分离端项目里,前端的代码会被打包成为纯静态文件.使用 Ngi ...
- 烧光百亿的共享单车行业,ofo和摩拜到底该不该合并?
共享经济领域可谓一地鸡毛,除了众多不靠谱的跟风项目外--共享马扎."老公寄存屋",更多的是不绝于耳的倒闭消息.尤其是在共享单车行业,暂且不提那些体量小的项目,单单是倒闭的大型共享单 ...