理解 ASP.NET Core 自定义身份认证与授权

一、身份认证

1.1 什么是身份认证?

初学者很难理解身份认证。微软官方文档只有简单一句话:身份验证是确定用户身份的过程。

以笔者的理解,顾名思义,身份认证就是对一个访问者的身份判别、识别、辨识的过程。识别一个访问者,要么识别出来了,你是某某某,要么没识别出来,你是陌生人,至于怎么识别,就是身份认证的事情。

  

举个例子:

1)我(访问者)要去一栋大楼(应用系统)某个会议室开会。

2)门口保安(身份认证中间件)拦住我。

3)保安(身份认证中间件)不认识我,身份认证失败。至于我能否进去是授权的事了,这点稍后再叙。

4)这时,我拿出了我的邀请函(访问令牌),保安一看,哦,原来是参加会议的人员,随即登记了我的信息(生成身份认证成功结果--身份认证票据)并给了我一张凭票(访问票据)。

5)票上面有门牌号、访问事由、我的姓名、电话等信息(身份证明、身份信息声明信息)。

至此,身份认证过程就结束了。

这个例子中,身份识别主要是保安做的事情,我是领导,保安认识我。保安见我眼生,不认识我,但是我有工牌或邀请函,他也会成功识别我。假如我啥都没有,我就是陌生人,匿名访问者。

1.2 登录与身份认证的关系?

初学者很容易被登录和身份认证框架绕晕,笔者也是如此。

其实登录和身份认证没有关系。

身份认证是针对所有请求,登录只是其中一个请求,只不过这个请求的功能是拿访问令牌。

再接着上面的例子举例:

4)保安不认识我的情况下,根据领导规定(授权方案),不认识的人一律不能进入大楼(授权策略)。

5)我没办法,我给大楼领导打了电话,领导微信给我发了邀请函。(这个过程就是登录过程,登录授权策略要求可以匿名访问,即在没有票据的情况下访问)

如果我不知道领导电话,电话都打不进去,那么我就别想进了,我就不能在大楼办业务。

说到底,登录也是一次普通访问,也要经历身份认证、授权的过程,至于认证和授权细节,由方案而定。

比如,领导规定:成年人才能访问这个网站,哦不。。。。这栋大楼。

这时,保安拦住我不让我进去,我给领导打电话,领导晓得我不是成年人,不给我发送邀请函,我也进不去。(这就是登录接口的授权策略)

二、授权

2.1 什么是授权?

身份认证负责识别,至于能不能进,是授权的事情。 微软官方文档就只有一句话:授权是确定用户是否有权访问资源的过程。

比较形象的说,授权就是用身份认证的结果(无结果、成功、失败),来判定访问者是否可以进入的过程。

接着前面的例子:

5)会议规定(授权方案):必须要凭参会票据才能进入会场,参加会议(授权策略)。

6)我拿着保安给的票据,就进入会议室了。

7)假设我拿的不是邀请函,我拿的是工作牌,保安也认得我,给我登记的是工作凭据,但是不符合会议规定(禁止、ForBidden、无权限参加),所以不能参会,保安不会让我进去,直接就把我处理了。

8)假设我是陌生人,保安认不出我,我偏偏要参会(这就是‘挑战’),保安肯定不会让我进去的,保安(身份认证方案)会处理我。

 

有的小伙伴就说了,我直接拿邀请函进去不就行了嘛,为啥还要换票据呢,不是多此一举嘛?

这个问题就是,为什么要把身份认证和授权分开的问题。

笔者认为,是为了将组件进行解耦,方便更好的扩展和维护。不一定是Http服务程序,其他类型的程序也有用到身份认证和授权。

身份验证和授权之间,通过实体人(身份票据)进行关联。身份验证生成身份票据,授权则根据实体人的信息进行相应的策略规定。

官方文档是这样叙述的:

授权是指判断用户可执行的操作的过程。授权与身份验证相互独立。 但是,授权需要一种身份验证机制。 身份验证是确定用户标识的一个过程。 身份验证可为当前用户创建一个或多个标识。

三、结合代码理解,自定义身份验证方案

本文代码示例环境:Asp.NetCore 7  

这里引用官方文档的叙述:

3.1 身份验证

身份验证处理程序:

根据身份验证方案的配置和传入的请求上下文,身份验证处理程序:

  • 构造表示用户身份的 AuthenticationTicket 对象(若身份验证成功)。
  • 返回“无结果”或“失败”(若身份验证失败)。
  • 具有用于挑战和禁止操作的方法,供用户在下述情况下访问资源时使用:
    • 他们未获得访问授权(禁止)。
    • 他们未经过身份验证(挑战)。
 1 using Microsoft.AspNetCore.Authentication;
 2 using Microsoft.Extensions.Options;
 3 using System.Security.Claims;
 4 using System.Text.Encodings.Web;
 5 
 6 namespace WebApplication1.Authentication
 7 {
 8 
 9 
10 
11     /// <summary>
12     /// 自定义身份认证方案
13     /// </summary>
14     public class CustomAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>
15     {
16         public CustomAuthenticationHandler(IOptionsMonitor<AuthenticationSchemeOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock)
17         {
18         }
19 
20 
21         /// <summary>
22         /// 身份验证处理 主要逻辑
23         /// </summary>
24         /// <returns></returns>
25         /// <exception cref="NotImplementedException"></exception>
26         protected async override Task<AuthenticateResult> HandleAuthenticateAsync()
27         {
28             var token = Request.Headers["authentication-token"].ToString();
29 
30             //无结果,当然这里也可以作为验证失败
31             if (string.IsNullOrEmpty(token)) 
32             {
33                 var noResult = AuthenticateResult.NoResult();
34                 return await Task.FromResult(noResult);
35             }
36 
37             //身份验证成功
38             //这里有个复杂的验证过程,通常是加解密操作
39             //也可以从数据源读取来判断,自行研究
40             //这里以相等来举例
41             if ("12932894384934".Equals(token)) 
42             {
43 
44                 //通过复杂的判断,这个人是个老师
45                 var principal = new ClaimsPrincipal();
46                 principal.AddIdentity(new ClaimsIdentity(new List<Claim> 
47                 {
48                     //添加老师的角色
49                     new Claim(ClaimTypes.Role, "Teacher") 
50                 }));
51 
52                 var success = AuthenticateResult.Success(new AuthenticationTicket(principal, "CustomAuthentication"));
53                 return await Task.FromResult(success);
54             }
55 
56 
57             //身份验证失败
58             var fail = AuthenticateResult.Fail(new Exception("token错误!"));
59             return await Task.FromResult(fail);
60         }
61 
62 
63         /// <summary>
64         /// 处理挑战  主要逻辑
65         /// </summary>
66         /// <param name="properties"></param>
67         /// <returns></returns>
68         protected override async Task HandleChallengeAsync(AuthenticationProperties properties)
69         {
70             //这里可以拿到认证结果
71             var result = await HandleAuthenticateOnceSafeAsync();
72 
73             //默认返回401状态代码
74             await base.HandleChallengeAsync(properties);
75         }
76 
77 
78         /// <summary>
79         /// 处理禁止   主要逻辑
80         /// </summary>
81         /// <param name="properties"></param>
82         /// <returns></returns>
83         protected override Task HandleForbiddenAsync(AuthenticationProperties properties)
84         {
85             //默认返回403状态代码
86             return base.HandleForbiddenAsync(properties);
87         }
88     }
89 }

 

3.2 授权

授权主要涉及组件:

授权组件(包括 AuthorizeAttribute 和 AllowAnonymousAttribute 属性)

在最基本的使用中,将 [Authorize] 属性应用于控制器、操作或 Razor 页面,将对该组件的访问权限限制为经过身份验证的用户,意思就是通过身份验证的才能访问

首先来看AuthorizeAttribute 的定义:

 1 using System;
 2 
 3 namespace Microsoft.AspNetCore.Authorization
 4 {
 5     //
 6     // 摘要:
 7     //     Specifies that the class or method that this attribute is applied to requires
 8     //     the specified authorization.
 9     [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
10     public class AuthorizeAttribute : Attribute, IAuthorizeData
11     {
12         //
13         // 摘要:
14         //     Initializes a new instance of the Microsoft.AspNetCore.Authorization.AuthorizeAttribute
15         //     class.
16         public AuthorizeAttribute();
17         //
18         // 摘要:
19         //     Initializes a new instance of the Microsoft.AspNetCore.Authorization.AuthorizeAttribute
20         //     class with the specified policy.
21         //
22         // 参数:
23         //   policy:
24         //     The name of the policy to require for authorization.
25         public AuthorizeAttribute(string policy);
26 
27         //
28         // 摘要:
29         //     Gets or sets the policy name that determines access to the resource.
30         public string? Policy { get; set; }
31         //
32         // 摘要:
33         //     Gets or sets a comma delimited list of roles that are allowed to access the resource.
34         public string? Roles { get; set; }
35         //
36         // 摘要:
37         //     Gets or sets a comma delimited list of schemes from which user information is
38         //     constructed.
39         public string? AuthenticationSchemes { get; set; }
40     }
41 }

传入不同的策略名称,可以应用不同的授权策略,角色也是如此。

还可以使用 AllowAnonymous 属性来允许未经身份验证的用户访问单个操作,例如:

 1 [Authorize]
 2 public class AccountController : Controller
 3 {
 4     [AllowAnonymous]
 5     public ActionResult Login()
 6     {
 7     }
 8 
 9     public ActionResult Logout()
10     {
11     }
12 }

3.2.1 添加授权策略

在Program.cs 文件中,添加所示代码,添加一个老师或管理员角色才能访问的策略

1 builder.Services.AddAuthorization(option => 
2 {
3     //添加一个授权策略,名称是 requiredAdminOrTeacher
4     option.AddPolicy("requiredAdminOrTeacher", policyBuilder =>
5     {
6         //这个策略是:  角色为Admin和Teacher的访问者可以访问
7         policyBuilder.RequireRole("Admin", "Teacher");
8     });
9 });

将该策略应用到方法或控制器

 1 using Microsoft.AspNetCore.Authorization;
 2 using Microsoft.AspNetCore.Mvc;
 3 
 4 namespace WebApplication1.Controllers
 5 {
 6     [ApiController]
 7     [Route("[controller]")]
 8     public class WeatherForecastController : ControllerBase
 9     {
10 
11 
12 
13         [Authorize(Policy = "requiredAdminOrTeacher")]
14         public IActionResult Get()
15         {
16             var var1 = new JsonResult(new 
17             {
18                 code = 200,
19                 msg = "OK"
20             });
21 
22             return var1;
23         }
24     }
25 }

 

四、后记

前面的思想和理解叙述了很多,核心代码就几行。授权框架替我们做了很多事,又给了足够的自定义和灵活性。

当认真理解了身份认证和授权中的核心思想,代码写起来就得心应手了。

难的是,如何把实际的业务需求转换为代码,与这个框架结合起来。

更多内容请参阅aspnetcore源码。

笔者技术有限,不当之处请广大读者批评指正!

 

4.1 参考资料

主要参阅了微软官方文档:

1. https://learn.microsoft.com/zh-cn/aspnet/core/security/authentication/?view=aspnetcore-7.0

2. https://learn.microsoft.com/zh-cn/aspnet/core/security/authorization/introduction?view=aspnetcore-7.0

 

posted @ 2023-06-21 19:45  稍等片客  阅读(1252)  评论(0编辑  收藏  举报