Microsoft.AspNetCore.Authentication.Cookies从入门到精通 (一)

Microsoft.AspNetCore.Authentication.Cookies从入门到精通 (一)

Microsoft.AspNetCore.Authentication.Cookies是一个存储组件,它的主要作用是通过Cookie机制将授权认证的回话信息保存到客户端中,这与我之前的文章AspNetCore中基于session的身份认证中的HttpSession原理相同,只不过在AspNetCore中基于session的身份认证这篇文章中我们是自己做的一套验证方案,而篇文章使用的是Asp.Net Core提供的认证方案而已。

开始一个Demo

Demo源码地址

1.创建一个Razor Page项目:dotnet new razor -n CookieSample

2.打开Startup.cs文件,添加认证代码

public void ConfigureServices(IServiceCollection services)
{
  	.......
    services.AddAuthentication()
        .AddCookie();
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
	......
    app.UseAuthentication();
	......
}

只需要几行代码我们就在项目中添加了授权认证功能,下面我们看如何在页面中使用授权认证

3.打开Index.cshtml.cs文件,添加我们的认证代码

[Authorize(AuthenticationSchemes = CookieAuthenticationDefaults.AuthenticationScheme)]
public class IndexModel : PageModel
{
	public void OnGet()
}

只需要一行代码,我的页面就成了一个需要授权认证后才能访问的页面,运行项目访问该页面查看效果。

4.添加Login页面,并在该页面实现登陆认证

当你打开Index页面的时候你会发现被重定向了到了/Account/Login,此时我们的项目中还没有这个页面,现在我们创建Login页面并添加一些登陆代码,这里所谓登陆并不是真正的从数据库里面验证用户信息,当然从数据验证的过程应该在该操作之前,这里的登陆是将我们希望保存在Cookie中的信息传递给Asp.Net Core认证组件,并由该组件将信息序列化到Cookie中,由此可见Microsoft.AspNetCore.Authentication.Cookies只是为了存储

public class LoginModel : PageModel
{
    public void OnPost()
    {
    	//这里可能需要去数据验证一些信息
    	......
    	//数据库验证通过后,我们需要将部分信息保存到Cookie中
        var claims = new List<Claim>
        {
            new Claim(ClaimTypes.Name,"Tst")
        };
        var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
        await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(claimsIdentity));

    }
}

这就是我们Demo的全部代码,是不是很简单。

使我们的Demo更接近真实的项目

案例需求

通常我们的网站会区分前台与后台,普通用户可以访问的我们叫前台,管理员可以访问的我们叫后台。

Demo源码地址

1. 我们将基于之前的Demo进行改造,现在我们的项目分为两类用户而且每类用户访问的页面还不相同。

2. 我们来创建管理员可以访问的页面:/Admin/Index,现在我们有了两个页面分别供两类用户访问,但是我们只有一个Login页面那么怎么才能让两类用户公用也一个登陆页面呢?

Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    ......
    services.AddAuthentication()
       	.AddCookie()
        .AddCookie("Admin","Admin",options=>{}); 
    ......
}

上面代码我添加一个了一个Admin认证方案用于管理员访问并在/Admin/Index页面中显式设置认证方案:

/Admin/Index

[Authorize(AuthenticationSchemes = "Admin")]
public class IndexModel : PageModel{}

3. 现在我们来修改Login页面代码,我们需要根据认证方案来做不同的认证登录,目的是为了生成认证的cookie信息

public class LoginModel : PageModel
{
	//保存认证方案以及友好显示名称
    [BindProperty]
    public IDictionary<string, string> Schemes { get; set; } = new Dictionary<string, string>();
	
	//认证后我们要跳转到的页面
    [BindProperty]
    public string RedirectToUrl { get; set; }

    IAuthenticationSchemeProvider _authenticationSchemeProvider;

    //构造函数注入方式获取IAuthenticationSchemeprovider实例
    public LoginModel(IAuthenticationSchemeProvider authenticationSchemeProvider)
    {
        //也可以通过从服务中查找的方式获取IAuthenticationSchemeprovider实例
        //_authenticationSchemeProvider = (IAuthenticationSchemeProvider)HttpContext.RequestServices.GetService(typeof(IAuthenticationSchemeProvider));
        _authenticationSchemeProvider = authenticationSchemeProvider;
    }
    public async Task OnGetAsync(string ReturnUrl)
    {
        RedirectToUrl = ReturnUrl;
        var authenticationSchemes = await _authenticationSchemeProvider.GetAllSchemesAsync();
        foreach (var item in authenticationSchemes)
        {
        	//如果我们没有设置DisplayName,就是用认证方案名称替换
            Schemes.Add(item.Name, item.DisplayName??item.Name);
        }
    }

    public IActionResult OnPost(string scheme,string redirectUrl)
    {
        var claims = new List<Claim>
        {
            new Claim(ClaimTypes.Name,scheme)
        };
        //生成不同认证方案的身份信息
        var claimsIdentity = new ClaimsIdentity(claims, scheme);
        //将认证信息保存到cookie中
        HttpContext.SignInAsync(scheme, new ClaimsPrincipal(claimsIdentity));
        return LocalRedirect(redirectUrl);
    }
}

我们来回顾一下代码做了什么事:

首先,我们在构造函数中注入了IAuthenticationSchemeProvider的实例,该实例主要用来获取认所有的证方案。

然后,我们将选择的认证方案以及认证后要跳转的地址Post到Login页面(这里也需要你提交用户名密码等从数据验证),验证后并将一些信息写入Cookie中。

插播一点个人的想法:有人可能会想我们分两个项目不行吗?我们搞成微服务不行吗?我的回答是不行:因为这里的一切教程都是围绕Microsoft.AspNetCore.Authentication.Cookies的功能进行讲解,实现认证的方式有很多种,每个人都会有自己的想法和实现,但我们只讲Asp.Net Core提供的东西,这样有利于统一,也有利于减少学习成本,学习成本少了才有时间去创新。

需求更新:为认证添加过期时间

​ 要求普通用户认证信息在最后一次访问的10分钟后过期,而管理员需要在最后一次访问的1天后过期。

public void ConfigureServices(IServiceCollection services)
{
    ......
    services.AddAuthentication()
        .AddCookie(options=>{
			options.SlidingExpiration = true;
			options.ExpireTimeSpan = TimeSpan.FromMinutes(10);
        })
        .AddCookie("Admin","Admin",options=>{
			options.SlidingExpiration = true;
			options.ExpireTimeSpan = TimeSpan.FromDays(1);
        }); 
    ......
}

我们通过两个属性实现了授权过期

SlidingExpiration:通过设置这个参数为Ture可以将过期时间变成滚动(滑动)过期,也就是按最后一次访问重新计算过期时间,只有设置了滑动Cookie我们的认证信息才更像一个回话状态。

ExpireTimeSpan:设置过期时间

需求更新:要求使用持久Cookie

在上面的需求中,每当我们关闭浏览器,Cookie的状态就丢失了,这不符合我们的要求,现在我们来修改让Cookie持久的保存在客户端。在之前的文章理解cookies中我们介绍了如何让cookie持久化,原理就是显式设置ExpiresMaxAge

public void ConfigureServices(IServiceCollection services)
{
    ......
    services.AddAuthentication()
        .AddCookie(options=>{
			options.SlidingExpiration = true;
			options.ExpireTimeSpan = TimeSpan.FromMinutes(10);
            //这里我们将Cookie过期时间设置成了365天,你也可以通过MaxAge属性来设置,不过这里有点需要注意的是,MaxAge会替代Expiration的值
            options.Cookie.Expiration = TimeSpan.FromDays(365);
            //options.Cookie.MaxAge = TimeSpan.FromDays(365);
        })
        .AddCookie("Admin","Admin",options=>{
			options.SlidingExpiration = true;
			options.ExpireTimeSpan = TimeSpan.FromDays(1);
            options.Cookie.Expiration = TimeSpan.FromDays(365);
        }); 
    ......
}

只需要一行代码我们就让Cookie变成了持久化Cookie,但有个问题,我们将普通用户的认证Cookie设置为10分钟过期,但却将Cookie设置为365天(这里的认证Cookie与Cookie是一个东西,认证Cookie的过期是认证服务验证的,而Cookie本身是浏览器验证的,如果浏览器认为Cookie过期则认证服务根本获取不到Cookie信息),当访问页面的间隔大于10分钟时,Cookie依然会失效,此时虽然Cookie值还在但已经是无意义的一段数据了,所以我们将这三个值options.ExpireTimeSpanoptions.Cookie.Expirationoptions.Cookie.MaxAge设置为相同似乎更合理点。

总结

  1. 我们介绍了如何在项目中使用基于Cookie的授权认证。
  2. 我们介绍了如何配置认证方案以及认证方案的友好提示名称,并介绍了如何获取所有的认证方案展示到页面中。
  3. 我们介绍了如何配置认证的滑动过期时间以及如何配置持久Cookie。

未完待续......

posted @ 2018-09-26 16:53  Net_win  阅读(2477)  评论(5编辑  收藏  举报