一. Cookie 是什么?
我的朋友问我 cookie 是什么, 用来干什么的, 可是我居然无法清楚明白简短地向其阐述 cookie, 这不禁让我陷入了沉思: 为什么我无法解释清楚, 我对学习的方法产生了怀疑! 所以我们在学习一个东西的时候, 一定要做到知其然知其所以然.
HTTP 协议本身是无状态的. 什么是无状态呢, 即服务器无法判断用户身份. Cookie 实际上是一小段的文本信息). 客户端向服务器发起请求, 如果服务器需要记录该用户状态, 就使用 response 向客户端浏览器颁发一个 Cookie. 客户端浏览器会把 Cookie 保存起来. 当浏览器再请求该网站时, 浏览器把请求的网址连同该 Cookie 一同提交给服务器. 服务器检查该 Cookie, 以此来辨认用户状态.
打个比方, 这就犹如你办理了银行卡, 下次你去银行办业务, 直接拿银行卡就行, 不需要身份证.
二. 在. NET Core 中尝试
废话不多说, 干就完了, 现在我们创建 ASP.NET Core MVC 项目, 撰写该文章时使用的. NET Core SDK 3.0 构建的项目, 创建完毕之后我们无需安装任何包,
但是我们需要在 Startup 中添加一些配置, 用于 Cookie 相关的.
- //public const string CookieScheme = "YourSchemeName";
- public Startup(IConfiguration configuration)
- {
- Configuration = configuration;
- }
- public IConfiguration Configuration { get; }
- // This method gets called by the runtime. Use this method to add services to the container.
- public void ConfigureServices(IServiceCollection services)
- {
- //CookieAuthenticationDefaults.AuthenticationScheme Cookies Default Value
- //you can change scheme
- services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
- .AddCookie(options => {
- options.LoginPath = "/LoginOrSignOut/Index/";
- });
- services.AddControllersWithViews();
- // is able to also use other services.
- //services.AddSingleton<IConfigureOptions<CookieAuthenticationOptions>, ConfigureMyCookie>();
- }
在其中我们配置登录页面, 其中 AddAuthentication 中是我们的方案名称, 这个是做什么的呢? 很多小伙伴都懵懵懂懂表示很懵逼啊, 我看很多人也是都写得默认, 那它到底有啥用, 经过我看 AspNetCore 源码发现它这个是可以做一些配置的. 看下面的代码:
- internal class ConfigureMyCookie : IConfigureNamedOptions<CookieAuthenticationOptions>
- {
- // You can inject services here
- public ConfigureMyCookie()
- {}
- public void Configure(string name, CookieAuthenticationOptions options)
- {
- // Only configure the schemes you want
- //if (name == Startup.CookieScheme)
- //{
- // options.LoginPath = "/someotherpath";
- //}
- }
- public void Configure(CookieAuthenticationOptions options)
- => Configure(Options.DefaultName, options);
- }
在其中你可以定义某些策略, 随后你直接改变 CookieScheme 的变量就可以替换某些配置, 在配置中一共有这几项, 这无疑是帮助我们快速使用 Cookie 的好帮手~ 点个赞.
在源码中可以看到 Cookie 默认保存的时间是 14 天, 这个时间我们可以去选择, 支持 TimeSpan 的那些类型.
- public CookieAuthenticationOptions()
- {
- ExpireTimeSpan = TimeSpan.FromDays(14);
- ReturnUrlParameter = CookieAuthenticationDefaults.ReturnUrlParameter;
- SlidingExpiration = true;
- Events = new CookieAuthenticationEvents();
- }
接下来 LoginOrOut Controller, 我们模拟了登录和退出, 通过 SignInAsync 和 SignOutAsync 方法.
- [HttpPost]
- public async Task<IActionResult> Login(LoginModel loginModel)
- {
- if (loginModel.Username == "haozi zhang" &&
- loginModel.Password == "123456")
- {
- var claims = new List<Claim>
- {
- new Claim(ClaimTypes.Name, loginModel.Username)
- };
- ClaimsPrincipal principal = new ClaimsPrincipal(new ClaimsIdentity(claims, "login"));
- await HttpContext.SignInAsync(principal);
- //Just redirect to our index after logging in.
- return Redirect("/Home/Index");
- }
- return View("Index");
- }
- /// <summary>
- /// this action for web lagout
- /// </summary>
- [HttpGet]
- public IActionResult Logout()
- {
- Task.Run(async () =>
- {
- // 注销登录的用户, 相当于 ASP.NET 中的 FormsAuthentication.SignOut
- await HttpContext.SignOutAsync();
- }).Wait();
- return View();
- }
就拿出推出的源码来看, 其中获取了 Handler 的某些信息, 随后将它转换为 IAuthenticationSignOutHandler 接口类型, 这个接口 as 接口, 像是在地方实现了这个接口, 然后将某些运行时的值引用传递到该接口上.
- public virtual async Task SignOutAsync(HttpContext context, string scheme, AuthenticationProperties properties)
- {
- if (scheme == null)
- {
- var defaultScheme = await Schemes.GetDefaultSignOutSchemeAsync();
- scheme = defaultScheme?.Name;
- if (scheme == null)
- {
- throw new InvalidOperationException($"No authenticationScheme was specified, and there was no DefaultSignOutScheme found. The default schemes can be set using either AddAuthentication(string defaultScheme) or AddAuthentication(Action<AuthenticationOptions> configureOptions).");
- }
- }
- var handler = await Handlers.GetHandlerAsync(context, scheme);
- if (handler == null)
- {
- throw await CreateMissingSignOutHandlerException(scheme);
- }
- var signOutHandler = handler as IAuthenticationSignOutHandler;
- if (signOutHandler == null)
- {
- throw await CreateMismatchedSignOutHandlerException(scheme, handler);
- }
- await signOutHandler.SignOutAsync(properties);
- }
其中 GetHandlerAsync 中根据认证策略创建了某些实例, 这里不再多说, 因为源码深不见底, 我也说不太清楚... 只是想表达一下看源码的好处和坏处....
- public async Task<IAuthenticationHandler> GetHandlerAsync(HttpContext context, string authenticationScheme)
- {
- if (_handlerMap.ContainsKey(authenticationScheme))
- {
- return _handlerMap[authenticationScheme];
- }
- var scheme = await Schemes.GetSchemeAsync(authenticationScheme);
- if (scheme == null)
- {
- return null;
- }
- var handler = (context.RequestServices.GetService(scheme.HandlerType) ??
- ActivatorUtilities.CreateInstance(context.RequestServices, scheme.HandlerType))
- as IAuthenticationHandler;
- if (handler != null)
- {
- await handler.InitializeAsync(scheme, context);
- _handlerMap[authenticationScheme] = handler;
- }
- return handler;
- }
最后我们在页面上想要获取登录的信息, 可以通过 HttpContext.User.Claims 中的签名信息获取.
- @using Microsoft.AspNetCore.Authentication
- <h2>HttpContext.User.Claims</h2>
- <dl>
- @foreach (var claim in User.Claims)
- {
- <dt>@claim.Type</dt>
- <dd>@claim.Value</dd>
- }
- </dl>
- <h2>AuthenticationProperties</h2>
- <dl>
- @foreach (var prop in (await Context.AuthenticateAsync()).Properties.Items)
- {
- <dt>@prop.Key</dt>
- <dd>@prop.Value</dd>
- }
- </dl>
三. 最后效果以及源码地址
GitHub 地址:
来源: https://www.cnblogs.com/ZaraNet/p/12099286.html