1. 程式人生 > >三分鐘學會在ASP.NET Core MVC 中使用Cookie

三分鐘學會在ASP.NET Core MVC 中使用Cookie

一.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://github.com/zaranetCore/aspNetCore_JsonwebToken/tree/master/src/Identity.Cookie/DotNetCore_Cookie_Sa