• 如何在 Net6.0 中对 WebAPI 进行 JWT 认证和授权


    一、简介
        我们做微服务开发,或者说做分布式开发,有一项技术我们是避不开的,那就是WebAPI,在 Net6.0中,有两类 WebAPI,一类是极简 WebAPI,它砍掉了很多冗余的东西,更纯粹的是做 API的,也更适合做微服务的开发。另外一类就是我们通常使用的正常 API,这个没得说,也是我们使用最多的。
        我们开发的API必须做鉴权和授权操作,否则,就成了裸跑了,那对于系统来说是很危险的。总体来说,一般的WebAPI和MinimalAPI鉴权的过程都是差不多的,但是也有微小的差异。今天我们就来详细说一下,内容很简单,大家有了基础可以随意去扩展。

          开发环境详情:
              操作系统:Windows 10 Professional
              开发工具:Visual Studio 2022
              开发语言:C#
              开发平台:Asp.Net Core 6.0 WebAPI
              测试工具:PostMan

    二、具体步骤
        我们在做具体的开发前,也要做一些准备工作,我们要做鉴权和授权,尤其是要使用 JWT 做鉴权和授权,这里面包含两个项目,一个是鉴权服务器,用于提供 JWTToken,另外一个项目是需要做鉴权和授权的具体 WebAPI 项目。有了准备,我们就可以开始了。

        1、第一个项目:PatrickLiu.Net6API.AuthenticationCenter,用于提供获取 Token 接口。
            

            (1)、Program.cs 源码如下,红色是重要代码:            

    复制代码
     1 using PatrickLiu.Net6API.Extensions;
     2 
     3 var builder = WebApplication.CreateBuilder(args);
     4 
     5 builder.Services.AddControllers();
     6 builder.Services.AddEndpointsApiExplorer();
     7 builder.Services.AddSwaggerGen();
     8 
     9 builder.Services.Configure(builder.Configuration.GetSection("JWTTokenOption"));
    10 builder.Services.AddTransient();
    11 
    12 var app = builder.Build();
    13 
    14 app.UseSwagger();
    15 app.UseSwaggerUI();
    16 
    17 app.MapControllers();
    18 
    19 app.Run();
    复制代码

              (2)、AuthenticationController 源码如下:

    复制代码
     1 using Microsoft.AspNetCore.Mvc;
     2 using Newtonsoft.Json;
     3 using PatrickLiu.Net6API.Extensions;
     4 
     5 namespace PatrickLiu.Net6API.AuthenticationCenter.Controllers
     6 {
     7     [ApiController]
     8     [Route("api/[controller]/[action]")]
     9     public class AuthenticationController : ControllerBase
    10     {
    11         private readonly IJWTService _jWTService;
    12 
    13         /// 
    14         /// 实例化。
    15         /// 
    16         /// 注入服务。
    17         public AuthenticationController(IJWTService jWTService)
    18         {
    19             _jWTService = jWTService;
    20         }
    21 
    22         /// 
    23         /// 根据用户名和密码获取 Token。
    24         /// 
    25         /// 用户名。
    26         /// 密码。
    27         /// 
    28         [HttpPost]
    29         public string Login(string userName, string password)
    30         {
    31             if (!string.IsNullOrEmpty(userName) && !string.IsNullOrEmpty(password))
    32             {
    33                 if (string.Compare(userName, "PatrickLiu", true) == 0 && string.Compare(password, "liulei123456", true) == 0)
    34                 {
    35                     string token = _jWTService.GetToken(userName, password);
    36                     return JsonConvert.SerializeObject(new
    37                     {
    38                         Result = true,
    39                         Token = token
    40                     });
    41                 }
    42             }
    43             return string.Empty;
    44         }
    45     }
    46 }
    复制代码

              (3)、appsettings.json 配置文件源码如下,红色字体要注意:

    复制代码
     1 {
     2   "Logging": {
     3     "LogLevel": {
     4       "Default": "Information",
     5       "Microsoft.AspNetCore": "Warning"
     6     }
     7   },
     8   "AllowedHosts": "*",
     9   "JWTTokenOption": {
    10     "Audience": "PatrickLiu.com",
    11     "Issuer": "PatrickLiu.com",
    12     "SecurityKey": "12333456677655ffrrffff"
    13   }
    14 }
    复制代码


        2、还有一个公共项目,用于存放公共类型。项目类型:PatrickLiu.Net6API.Extensions,项目类型:Net 6.0类库。
            

            (1)、IJWTService 源码如下:

    复制代码
     1 namespace PatrickLiu.Net6API.Extensions
     2 {
     3     /// 
     4     /// 提供 Token 的服务。
     5     /// 
     6     public interface IJWTService
     7     {
     8         /// 
     9         /// 获取 Token。
    10         /// 
    11         /// 用户名
    12         /// 密码
    13         /// 
    14         string GetToken(string userName,string password);
    15     }
    16 }
    复制代码

            (2)、JWTService 源码如下:      

    复制代码
     1 using Microsoft.Extensions.Options;
     2 using Microsoft.IdentityModel.Tokens;
     3 using System.IdentityModel.Tokens.Jwt;
     4 using System.Security.Claims;
     5 using System.Security.Cryptography;
     6 using System.Text;
     7 
     8 namespace PatrickLiu.Net6API.Extensions
     9 {
    10     /// 
    11     /// 提供 Token 服务的实现。
    12     /// 
    13     public sealed class JWTService : IJWTService
    14     {
    15         private readonly IOptionsMonitor _option;
    16 
    17         /// 
    18         /// 实例化。
    19         /// 
    20         /// 注入选项。
    21         public JWTService(IOptionsMonitor option)
    22         {
    23             _option = option;
    24         }
    25 
    26         /// 
    27         ///  获取 Token。
    28         /// 
    29         /// 用户名。
    30         /// 密码
    31         /// 
    32         public string GetToken(string userName, string password)
    33         {
    34             #region 有效载荷
    35 
    36             var claims = new[] {
    37             new Claim(ClaimTypes.Name, userName),
    38             new Claim("NickName",userName),
    39             new Claim(ClaimTypes.Role,"Administrator"),
    40             new Claim("Password",password),
    41             };
    42 
    43             #endregion
    44 
    45             SymmetricSecurityKey key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_option.CurrentValue.SecurityKey!));
    46 
    47             SigningCredentials signingCredentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
    48 
    49             JwtSecurityToken token = new JwtSecurityToken(
    50                 issuer: _option.CurrentValue.Issuer!,
    51                 audience: _option.CurrentValue.Audience!,
    52                 claims: claims,
    53                 expires: DateTime.Now.AddMinutes(5),
    54                 signingCredentials: signingCredentials
    55                 );
    56 
    57             string returnToken = new JwtSecurityTokenHandler().WriteToken(token);
    58 
    59             return returnToken;
    60         }
    61     }
    62 }
    复制代码

            (3)、JWTTokenOption 源码如下:

    复制代码
     1 namespace PatrickLiu.Net6API.Extensions
     2 {
     3     /// 
     4     /// 用于接受配置数据实体类型。 
     5     /// 
     6     public sealed class JWTTokenOption
     7     {
     8         /// 
     9         /// 获取或者设置接受者。
    10         /// 
    11         public string? Audience { get; set; }
    12 
    13         /// 
    14         /// 获取或者设置加密 key。
    15         /// 
    16         public string? SecurityKey { get; set; }
    17 
    18         /// 
    19         /// 获取或者设置发布者
    20         /// 
    21         public string? Issuer { get; set; }
    22     }
    23 }
    复制代码

            (4)、授权服务器运行起来如下:

              

              


        3、现在是普通WebAPI类型项目:

            (1)、我们先看一下项目截图,有一个直观感受,截图如下:
                


            (2)、如果想做WebAPI的JWT的鉴权和授权,必须通过 Nuget 加载的程序包,名称如下:
                Microsoft.AspNetCore.Authentication.JwtBearer
                Microsoft.IdentityModel.Tokens
            (3)、Program.cs源码如下,红色部分表示健全和授权的重要部分。

    复制代码
     1 using Microsoft.AspNetCore.Authentication.JwtBearer;
     2 using Microsoft.IdentityModel.Tokens;
     3 using PatrickLiu.Net6API.Extensions;
     4 using System.Text;
     5 
     6 var builder = WebApplication.CreateBuilder(args);
     7 
     8 builder.Services.AddControllers();
     9 builder.Services.AddEndpointsApiExplorer();
    10 builder.Services.AddSwaggerGen();
    11 
    12 #region 配置鉴权
    13 
    14 //增加的鉴权逻辑,角色认证、策略认证都是支持的,和Net Core MVC 支持的一样。
    15 JWTTokenOption tokenOption = new JWTTokenOption();
    16 builder.Configuration.Bind("JWTTokenOption", tokenOption);
    17 //builder.Services.Configure(builder.Configuration.GetSection("JWTTokenOption"));
    18 builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    19     .AddJwtBearer(option =>
    20 {
    21     option.TokenValidationParameters = new TokenValidationParameters()
    22     {
    23         ValidateIssuer = true,//是否验证 Issuer(发行商)
    24         ValidateAudience = true,//是否验证 Audience(受众者)
    25         ValidateLifetime = true,//是否验证失效时间
    26         ValidateIssuerSigningKey = true,//是否验证 Issuer 的签名键
    27         ValidAudience=tokenOption.Audience,
    28         ValidIssuer=tokenOption.Issuer,// ValidAudience,ValidIssuer这两项的值要和验证中心的只保持一致。
    29         IssuerSigningKey=new SymmetricSecurityKey(Encoding.UTF8.GetBytes(tokenOption.SecurityKey!))         
    30     };
    31 });
    32 
    33 #endregion
    34 
    35 var app = builder.Build();
    36 
    37 app.UseSwagger();
    38 app.UseSwaggerUI();
    39 
    40 app.UseAuthentication();
    41 app.UseAuthorization();42 
    43 app.MapControllers();
    44 
    45 app.Run();
    复制代码


            (4)、要实现授权的Controller增加【Authorize】特性,红色部分要注意。

    复制代码
     1 using Microsoft.AspNetCore.Authentication.JwtBearer;
     2 using Microsoft.AspNetCore.Authorization;
     3 using Microsoft.AspNetCore.Mvc;
     4 
     5 namespace PatrickLiu.Net6API.Resouces.Controllers
     6 {
     7     [Route("api/[controller]")]
     8     [ApiController]
     9     public class SecondController : ControllerBase
    10     {
    11         /// 
    12         /// 这里使用 JWT 进行授权检查。
    13         /// 
    14         /// 
    15         [HttpGet]
    16         [Authorize(AuthenticationSchemes =JwtBearerDefaults.AuthenticationScheme)]
    17         public object GetData()
    18         {
    19             return new
    20             {
    21                 Id = 1234,
    22                 Name = "PatrickLiu"
    23             };
    24         }
    25     }
    26 }
    复制代码


            (5)、appsettings.json 配置文件,这里的配置要和【PatrickLiu.Net6API.AuthenticationCenter】项目配置一样,红色部分要注意。

    复制代码
     1 {
     2   "Logging": {
     3     "LogLevel": {
     4       "Default": "Information",
     5       "Microsoft.AspNetCore": "Warning"
     6     }
     7   },
     8   "AllowedHosts": "*",
     9   "JWTTokenOption": {
    10     "Audience": "PatrickLiu.com",
    11     "Issuer": "PatrickLiu.com",
    12     "SecurityKey": "12333456677655ffrrffff"
    13   }
    14 }
    复制代码


        4、现在是Minimal WebAPI类型项目:
            其实,WebAPI和MinimalAPI鉴权和授权总体都是差不多,差别不大。
            (1)、我们先看一下项目截图,有一个直观感受,截图如下:
                
            (2)、如果想做WebAPI的JWT的鉴权和授权,必须通过 Nuget 加载的程序包,名称如下:
                Microsoft.AspNetCore.Authentication.JwtBearer
                Microsoft.IdentityModel.Tokens

            (3)、Program.cs 源码如下,红色标注要特别重要。

    复制代码
     1 using Microsoft.AspNetCore.Authentication.JwtBearer;
     2 using Microsoft.IdentityModel.Tokens;
     3 using PatrickLiu.Net6API.Extensions;
     4 using PatrickLiu.Net6API.MinimalAPI.Extension;
     5 using System.Text;
     6 
     7 var builder = WebApplication.CreateBuilder(args);
     8 
     9 builder.Services.AddEndpointsApiExplorer();
    10 builder.Services.AddSwaggerGen();
    11 
    12 #region 1、配置鉴权逻辑
    13 
    14 //增加的鉴权逻辑,角色认证、策略认证都是支持的,和Net Core MVC 支持的一样。
    15 JWTTokenOption tokenOption = new JWTTokenOption();
    16 builder.Configuration.Bind("JWTTokenOption", tokenOption);
    17 builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    18     .AddJwtBearer(option =>
    19     {
    20         option.TokenValidationParameters = new TokenValidationParameters()
    21         {
    22             ValidateIssuer = true,//是否验证 Issuer(发行商)
    23             ValidateAudience = true,//是否验证 Audience(受众者)
    24             ValidateLifetime = true,//是否验证失效时间
    25             ValidateIssuerSigningKey = true,//是否验证 Issuer 的签名键
    26             ValidAudience = tokenOption.Audience,
    27             ValidIssuer = tokenOption.Issuer,// ValidAudience,ValidIssuer这两项的值要和验证中心的只保持一致。
    28             IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(tokenOption.SecurityKey!))
    29         };
    30     });
    31 
    32 #endregion
    33 
    34 #region 2、配置授权逻辑
    35 
    36 //在这里不支持增加授权信息,比如:builder.Services.AddAuthorization(JwtBearerDefaults.AuthenticationScheme);,这样写是不行的,
      我们可以扩展实现 IAuthorizeData 来达到同样的目的。
    37 builder.Services.AddAuthorization();
    38 
    39 #endregion
    40 
    41 var app = builder.Build();
    42 
    43 app.UseSwagger();
    44 app.UseSwaggerUI();
    45 
    46 #region 3、使用鉴权授权中间件
    47 
    48 app.UseAuthentication();
    49 app.UseAuthorization();
    50 
    51 #endregion
    52 
    53 #region 4、实现授权要求
    54 
    55 app.MapGet("/User", (int id, string name) =>
    56 {
    57     return "Hello World!";
    58 }).RequireAuthorization(new CustomAuthorizeData()
    59 {
    60     AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme
    61     //可以增加角色授权
    62     //,Roles="",
    63     //可以增加策略授权。
    64     //Policy=""
    65 });
    66 
    67 app.MapGet("/Products", (HttpContext context) =>
    68 {
    69     return new
    70     {
    71         Id = 123456,
    72         Name = "IPhone14 Pro Max",
    73         Price = 4839.23,
    74         DateOfProduction = "2023/2/12 23:22"
    75     };
    76 });
    77 
    78 #endregion
    79 
    80 app.Run();
    复制代码


            (4)、扩展 IAuthorizeData 接口实现CustomAuthorizeData,可以实现对MinimalAPI授权。

    复制代码
     1 using Microsoft.AspNetCore.Authorization;
     2 
     3 namespace PatrickLiu.Net6API.MinimalAPI.Extension
     4 {
     5     public sealed class CustomAuthorizeData : IAuthorizeData
     6     {
     7         public string? Policy { get; set; }
     8         public string? Roles { get; set; }
     9         public string? AuthenticationSchemes { get; set; }
    10     }
    11 }
    复制代码


            (5)、appsettings.json 配置文件源码如下,红色部分是重要代码。

    复制代码
     1 {
     2   "Logging": {
     3     "LogLevel": {
     4       "Default": "Information",
     5       "Microsoft.AspNetCore": "Warning"
     6     }
     7   },
     8   "AllowedHosts": "*",
     9   "JWTTokenOption": {
    10     "Audience": "PatrickLiu.com",
    11     "Issuer": "PatrickLiu.com",
    12     "SecurityKey": "12333456677655ffrrffff"
    13   }
    14 }
    复制代码


    三、结束语

           好了,今天就写到这里了,现在总结一下,如果想要给WebAPI或者MinimalAPI实现授权和鉴权,总体步骤是差不多的,第一步,都要启用连个中间件,就是授权和鉴权中间件(app.UseAuthorization()、app.UseAuthentication()),然后要配置鉴权的配置逻辑,差别就在授权方面,普通WebAPI直接通过 AuthorizeAttribute 特性标注在需要授权的方法或者 Controller 类型上就可以。普通WebAPI支持所有的角色授权、策略授权等方法,并且也支持所有的Filter。MinimalAPI要实现授权,要在GetXXX方法后面调用RequireAuthorization()来实现授权的内容。不忘初心,继续努力,老天不会辜负努力的人。

  • 相关阅读:
    【C++】函数模版和类模版详解
    【下载器篇】获取微软应用商店应用安装包的方法
    用递归函数和栈操作逆序栈
    同一个接口时快时慢。一次慢的,三四次是快的。怎么回事?
    如何利用CodeQL挖掘CVE-2020-10199
    安卓手机抓包配置
    Taro,小程序scroll-view 填满剩下的高度空间,关闭页面回跳(部分ios机型 滚动不到底部)
    Linux安装 vmware workstation
    王道数据结构7(查找)
    Nginx 出现403 Forbidden 的几种解决方案
  • 原文地址:https://www.cnblogs.com/PatrickLiu/p/17167558.html