Overview
身份认证是网站最基本的功能,最近因为业务部门的一个需求,需要对一个已经存在很久的小工具网站进行改造,因为在逐步的将一些离散的系统迁移至 .NET Core,所以趁这个机会将这个老的 .NET Framework 4.0 的项目进行升级
老的项目是一个 MVC 的项目并且有外网访问的需求,大部门的微服务平台因为和内部的业务执行比较密切,介于资安要求与外网进行了隔离,因此本次升级就不会迁移到该平台上进行前后端分离改造
使用频次不高,不存在高并发,实现周期短,所以就没有必要为了用某些组件而用,因此这里还是选择沿用 MVC 框架,对于网站的身份认证则采用单体应用最常见的 Cookie 认证来实现,本篇文章则是如何实现的一个基础的教程,仅供参考
Step by Step
在涉及到系统权限管理的相关内容时,必定会提到两个长的很像的单词,authentication
(认证) 和 authorization
(授权)
- authentication:用一些数据来证明你就是你,登录系统、指纹、面部解锁就是一种认证的过程
- authorization:授予一些用户去访问一些特殊资源或功能的过程,系统包含管理员和普通用户两种角色,只有管理员才可以执行某些操作,赋予管理员角色某些操作的过程就是授权
只有认证和授权一起配合,才可以完成对于整个系统的权限管控
前期准备
假定现在已经存在了一个 ASP.NET Core MVC 应用,这里以 VS 创建的默认项目为例,对于一个 MVC or Web API 应用,要求用户必须登录之后才能进行访问,最简单的方式,在需要认证的 Controller 或 Action 上添加 Authorize
特性,然后在 Startup.Configure
方法中通过 UseAuthorization
添加中间件即可
1 | [ ] |
1 | public class Startup |
当然,当系统只包含一个两个 Controller 时还好,当系统比较复杂的时候,再一个个的添加 Authorize
特性就比较麻烦了,因此这里我们可以通过在 Startup.ConfigureServices
中添加全局的 AuthorizeFilter
过滤器,实现对于全局的认证管控
1 | public class Startup |
此时,对于一些不需要进行认证就可以访问的页面,只需要添加 AllowAnonymous
特性即可
1 | public class AuthenticationController : Controller |
配置认证策略
当然,如果只是这样修改的话,其实是有问题的,可以看到,当添加上全局过滤器后,系统已经无法正常的进行访问
对于 authorization(授权) 来说,它其实是在 authentication(认证)通过之后才会进行的操作,也就是说这里我们缺少了对于系统认证的配置,依据报错信息的提示,我们首先需要通过使用 AddAuthentication
方法来定义系统的认证策略
AddAuthentication
方法位于 Microsoft.AspNetCore.Authentication
类库中,通过在 Nuget 中搜索就可以发现,.NET Core 已经基于业界通用的规范实现了多个认证策略
因为这里使用的 Cookie 认证已经包含在默认的项目模板中了,所以就不需要再引用了
基于 .NET Core 标准的服务使用流程,首先,我们需要在 Startup.ConfigureServices
方法来中通过 AddAuthentication
来定义整个系统所使用的一个授权策略,以及,基于我们采用 Cookie 授权的方式,结合目前互联网针对跨站点请求伪造 (CSRF) 攻击的防范要求,我们需要对网站的 Cookie 进行一些设定
1 | public class Startup |
如代码所示,在定义授权策略时,我们定义了三个重定向的页面,去告诉 Cookie 授权策略这里对应的页面在何处,同时,因为身份验证 Cookie 的默认过期时间会持续到关闭浏览器为止,也就是说,只要用户不点击退出按钮并且不关闭浏览器,用户会一直处于已经登录的状态,所以这里我们设定 20 分钟的过期时间,避免一些不必要的风险
至此,对于 Cookie 认证策略的配置就完成了,现在就可以在 Startup.Configure
方法中添加 UseAuthentication
中间件到 HTTP 管道中,实现对于网站认证的启用,这里需要注意,因为是先认证再授权,所以中间件的添加顺序不可以颠倒
1 | public class Startup |
此时,当我们再次访问系统时,因为没有经过认证,自动触发了重定向到系统登录页面的操作,而这里重定向跳转的页面就是上文代码中配置的 LoginPath
的属性值
登录、登出实现
当认证策略配置完成之后,就可以基于选择的策略来进行登录功能的实现。这里的登录页面上的按钮,模拟了一个登录表单提交,当点击之后会触发系统的认证逻辑,实现代码如下所示。这里别忘了将登录事件的 Action 上加上 AllowAnonymous
特性从而允许匿名访问
1 | [ ] |
在整块的代码中,涉及到三个主要的对象,Claim
、ClaimsIdentity
和 ClaimsPrincipal
,通过对于这三个对象的使用,从而实现将用户登录成功后系统所需的用户信息包含在 Cookie 中
三个对象之间的区别,借用理解ASP.NET Core验证模型(Claim, ClaimsIdentity, ClaimsPrincipal)不得不读的英文博文这篇博客的解释来说明
- Claim:被验证主体特征的一种表述,比如:登录用户名是…,email是…,用户Id是…,其中的“登录用户名”,“email”,“用户Id”就是 ClaimType
- ClaimsIdentity:一组 claims 构成了一个 identity,具有这些 claims 的 identity 就是 ClaimsIdentity ,驾照就是一种 ClaimsIdentity,可以把 ClaimsIdentity理解为“证件”,驾照是一种证件,护照也是一种证件
- ClaimsPrincipal:ClaimsIdentity 的持有者就是 ClaimsPrincipal ,一个 ClaimsPrincipal 可以持有多个 ClaimsIdentity,就比如一个人既持有驾照,又持有护照
最后,通过调用 HttpContext.SignInAsync
方法就可以完成登录功能,可以看到,当 Cookie 被清除后,用户也就处于登出的状态了,当然,我们也可以通过手动的调用 HttpContext.SignOutAsync
来实现登出
获取用户信息
对于添加在 Claim 中的信息,我们可以通过指定 ClaimType 的方式获取到,在 View 和 Controller 中,我们可以直接通过下面的方式进行获取,这里使用到的 User 其实就是上文中提到的 ClaimsPrincipal
1 | var userName = User.FindFirst(ClaimTypes.Name)?.Value; |
而当我们需要在一个独立的类库中获取存储的用户信息时,我们需要进行如下的操作
第一步,在 Startup.ConfigureServices
方法中注入 HttpContextAccessor
服务
1 | public class Startup |
第二步,在你需要使用的类库中通过 Nuget 引用 Microsoft.AspNetCore.Http
,之后就可以在具体的类中通过注入 IHttpContextAccessor
来获取到用户信息,当然,也可以在此处实现登录、登出的方法
1 | namespace Sample.Infrastructure |
至此,整块的认证功能就已经实现了,希望对你有所帮助