.NET 6.0 WebAPI 使用JWT生成Token的验证授权

1.引入相关程序包JwtBearer注意版本:

2.配置文件appsettings.json写相关配置参数(也可不写,写在程序里面,数据库读取也是一样的)

, //JWT加密
  "JWTToken": {
    "SecretKey": "jsaduwqe6asdjewejdue7dfmsdfu0sdfmwmsd8wfsd6", //密钥
    "Issuer": "ZYP", //发行者
    "Audience": "simple", //拥护者
    //"ExpireMinutes": 240 //过期时间
  }

3.在Program配置相关服务。

#region JWT
//获取配置文件
var configuration = builder.Configuration;
string Issuer = configuration["JWTToken:Issuer"];
string Audience = configuration["JWTToken:Audience"];
string SecretKey = configuration["JWTToken:SecretKey"];
//注入jwt
builder.Services.AddAuthentication(options =>
{
    options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
    options.TokenValidationParameters = new TokenValidationParameters()
    {
        //过期时间容错值,解决服务器端时间不同步问题(秒)
        //允许服务器时间偏移量30秒,即我们配置的过期时间加上这个允许偏移的时间值,才是真正过期的时间(过期时间 + 偏移值)你也可以设置为0,ClockSkew = TimeSpan.Zero
        ClockSkew = TimeSpan.FromSeconds(30),
        //要求Token的Claims中必须包含Expires
        RequireExpirationTime = true,
        //是否在令牌期间验证签发者
        ValidateIssuer = true,
        //发行人Issuer
        ValidIssuer = Issuer, 
        //是否验证接收者
        ValidateAudience = true,
        //是否验证失效时间
        ValidateLifetime = true,
        //是否验证签名SecurityKey
        ValidateIssuerSigningKey = true,
        //接收者
        ValidAudience = Audience,
        //密钥SecurityKey
        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(SecretKey)), 
    };
});
//注入JwtHelper
builder.Services.AddSingleton(new JwtHelper(configuration));
#endregion

//注入Swagger,注入这个才能在调试接口时输入token
builder.Services.AddSwaggerGen(options =>
{
    options.AddSecurityRequirement(new Microsoft.OpenApi.Models.OpenApiSecurityRequirement
    {
        {
            new OpenApiSecurityScheme
            {
                Reference=new OpenApiReference{Id="Bearer",Type=ReferenceType.SecurityScheme},
            },
            Array.Empty<string>()
        }
    });

    options.AddSecurityDefinition("Bearer", new Microsoft.OpenApi.Models.OpenApiSecurityScheme
    {
        Description = "请输入文字'Bearer '后面跟空格和token格式  Bearer {token}",
        Name = "Authorization",
        In = Microsoft.OpenApi.Models.ParameterLocation.Header,
        Type = Microsoft.OpenApi.Models.SecuritySchemeType.ApiKey
    });
});

//配置跨域
builder.Services.AddCors(policy =>
{
    policy.AddPolicy("CorsPolicy", opt => opt
    .AllowAnyOrigin()
    .AllowAnyHeader()
    .AllowAnyMethod()
    .WithExposedHeaders("X-Pagination"));
});

//调用中间件:UseAuthentication(认证),
//必须在所有需要身份认证的中间件前调用,比如 UseAuthorization(授权)。
app.UseAuthentication();
//调用中间件:UseAuthorization(授权)。
app.UseAuthorization();

4.相关配置结束后,我们得生成Token,这时我们创建一个专门生成Token的类里面有两个生成Token的方法,想用哪个用哪个。该类在Program里有引用。

 /// <summary>
 /// Token生成类
 /// </summary>
 public class JwtHelper
 {
     /// <summary>
     /// 配置文件信息
     /// </summary>
     private readonly IConfiguration _configuration;
     public JwtHelper(IConfiguration configuration)
     {
         _configuration = configuration;
     }

     /// <summary>
     /// 创建一个使用控制器方法授权的Token
     /// </summary>
     /// <returns></returns>
     public string CreatePermissionToken(string UserName, string RoleName, string AppId, Claim[] claims)
     {
         // 1. 定义需要使用到的Claims
         if (claims == null)
         {
             claims = new[]
             {
                 new Claim(ClaimTypes.Name, UserName), //HttpContext.User.Identity.Name
                 new Claim(ClaimTypes.Role, RoleName), //HttpContext.User.IsInRole("r_admin")
                 new Claim(JwtRegisteredClaimNames.Jti, AppId),//分配给订阅着的特定Id
                 new Claim("Permission", Permissions.UserCreate),
                 new Claim("Permission", Permissions.UserDelete),
                 new Claim("Permission", Permissions.UserUpdate),
                 new Claim("Permission", Permissions.UserSelect)
                 //new Claim("Username", "Admin"),//其他荷载信息
             };
         }
         // 2. 从 appsettings.json 中读取密钥SecretKey
         var secretKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["JWTToken:SecretKey"]));

         // 3. 选择加密算法
         var algorithm = SecurityAlgorithms.HmacSha256;

         // 4. 生成Credentials
         var signingCredentials = new SigningCredentials(secretKey, algorithm);

         // 5. 根据以上,生成token
         var Token = new JwtSecurityToken(
             issuer: _configuration["JWTToken:Issuer"], //发布者
             audience: _configuration["JWTToken:Audience"], //接收者
             claims: claims, //存放的用户信息
             notBefore: DateTime.Now, //发布时间
             expires: System.DateTime.Now.AddHours(48), //有效期设置为48小时
             signingCredentials: signingCredentials //数字签名,用于生成Token的Header,其余都是荷载数据
             );
         // 6. 将token变为string
         var token = new JwtSecurityTokenHandler().WriteToken(Token);
         return token;
     }

     /// <summary>
     /// 创建一个使用账号密码授权验证的Token
     /// </summary>
     /// <param name="UserName"></param>
     /// <param name="RoleName"></param>
     /// <param name="AppId"></param>
     /// <param name="Account"></param>
     /// <param name="PassWord"></param>
     /// <returns></returns>
     public string CreateLoginToken(string UserName, string RoleName, string AppId, string Account, string PassWord)
     {
         // 1. 定义需要使用到的Claims
         var claims = new[]
         {
             new Claim(ClaimTypes.Name, UserName),
             new Claim(ClaimTypes.Role, RoleName),
             new Claim(JwtRegisteredClaimNames.Jti, AppId),//分配给订阅着的特定Id
             new Claim("Account", Account),//账号
             new Claim("PassWord", PassWord)//密码,可以要求使用特定加密技术加密
             //new Claim("Username", "Admin"),//其他荷载信息
         };

         // 2. 从 appsettings.json 中读取密钥SecretKey
         var secretKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["JWTToken:SecretKey"]));

         // 3. 选择加密算法
         var algorithm = SecurityAlgorithms.HmacSha256;

         // 4. 生成Credentials
         var signingCredentials = new SigningCredentials(secretKey, algorithm);

         // 5. 根据以上,生成token
         var Token = new JwtSecurityToken(
             issuer: _configuration["JWTToken:Issuer"], //发布者
             audience: _configuration["JWTToken:Audience"], //接收者
             claims: claims, //存放的用户信息
             notBefore: DateTime.Now, //发布时间
             expires: System.DateTime.Now.AddHours(48), //有效期设置为48小时
             signingCredentials: signingCredentials //数字签名,用于生成Token的Header,其余都是荷载数据
             );
         // 6. 将token变为string
         var token = new JwtSecurityTokenHandler().WriteToken(Token);
         return token;
     }
 }

5.这时可以生成Token了,我们来新建一个控制器来生成一个试试:

[Route("api/[controller]")]
[ApiController]
public class GetTokenController : ControllerBase
{
    private readonly JwtHelper _jwtHelper;
    public GetTokenController(JwtHelper jwtHelper)
    {
        _jwtHelper = jwtHelper;
    }
    [HttpPost]
    [Route("Token")]
    public Task<JsonResult> GetToken(UserToken token)
    {
        string thetoken =  _jwtHelper.CreateLoginToken(token.Name, "User", token.AppId, token.Account, token.PassWord);
        var result = new
        {
            success = true,
            msg = "OK",//消息
            token = thetoken
        };
        return Task.FromResult(new JsonResult(result));
    }
}

控制器的参数类(根据自己需要修改)

public class UserToken
{
    /// <summary>
    /// 给需要访问本系统的程序的唯一标识
    /// </summary>
    public string AppId { get; set; } = string.Empty;
    /// <summary>
    /// 需要访问本系统的程序的名称
    /// </summary>
    public string Name { get; set; } = string.Empty;
    /// <summary>
    /// 分配给需要访问本系统的程序的账号
    /// </summary>
    public string Account { get; set; } = string.Empty;
    /// <summary>
    /// 分配给需要访问本系统的程序的密码
    /// </summary>
    public string PassWord { get; set; } = string.Empty;
}

启动程序测试,生成成功!:

6.既然可以生成Token了,那么就该给控制器授权了,总不能让每个携带Token的用户能访问系统所以的API吧,那样会出现垂直越权的情况,渗透测试过不了哦。

相关标签:Authorize 和 AllowAnonymous

授权方式:介绍三种授权方式(Policy、Role、Scheme)

此处着重说Policy方式,对Role方法感兴趣的可以看我前面的Cookie方式验证。

6.1首先新建一个JwtAuthorizationRequirement类(类名不固定)用于继承IAuthorizationRequirement

public class JwtAuthorizationRequirement : IAuthorizationRequirement
{
    //这里可以扩展一些其他的角色或者需要的东西.
    //txt参数是在使用策略授权时传入进来的,例如:Authorize(Policy= "MyPolicy")的MyPolicy
    public JwtAuthorizationRequirement(string name)
    {
        Name = name;
    }
    public string? Name { get; set; }
}

6.2然后新建一个JwtAuthorizationHandler类继承AuthorizationHandler<JwtAuthorizationRequirement>

/// <summary>
/// 检验策略,相当于.NET MVC继承AuthorizeAttribute实现JWT的拦截器的效果。
/// </summary>
public class JwtAuthorizationHandler : AuthorizationHandler<JwtAuthorizationRequirement>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, JwtAuthorizationRequirement requirement)
    {
        if (context.User == null)
        {
            context.Fail();
            return Task.CompletedTask;
        }
        //requirement.Name 就是在添加策略授权时传入的值 
        string? Account = context.User.Claims.FirstOrDefault(p => p.Type == requirement.Name)?.Value;
        string? PassWord = context.User.Claims.FirstOrDefault(p => p.Type == "PassWord")?.Value;
        //这里时数据库或者其他方式校验 
        if (Account=="1234")
        {
            context.Succeed(requirement);
        }
        else
        {
            context.Fail();
        }
        return Task.CompletedTask;
    }
}

6.3然后将该策略在Program下注入

//注入授权策略(非必要,仅有需要自定义授权规则时使用)
builder.Services.AddSingleton<IAuthorizationHandler, JwtAuthorizationHandler>();
//账号密码验证策略
builder.Services.AddAuthorization(p =>
{
    p.AddPolicy("Account", t =>
    {
        t.Requirements.Add(new JwtAuthorizationRequirement("Account"));
    });
});

6.4 使用:

 [Authorize(policy: "Account")]//主要是这个
 public IEnumerable<WeatherForecast> Get()
 {
     return Enumerable.Range(1, 5).Select(index => new WeatherForecast
     {
         Date = DateTime.Now.AddDays(index),
         TemperatureC = Random.Shared.Next(-20, 55),
         Summary = Summaries[Random.Shared.Next(Summaries.Length)]
     })
     .ToArray();
 }

策略授权基本就是这样了。

再贴一个策略授权代码(可以忽略):

  /// <summary>
  /// 规则授权参数
  /// </summary>
  public class Permissions
  {
      /// <summary>
      /// 规则受体
      /// </summary>
      public const string User = "User";
      /// <summary>
      /// 增权限
      /// </summary>
      public const string UserCreate = User + ".Create";
      /// <summary>
      /// 删权限
      /// </summary>
      public const string UserDelete = User + ".Delete";
      /// <summary>
      /// 改权限
      /// </summary>
      public const string UserUpdate = User + ".Update";
      /// <summary>
      /// 查权限
      /// </summary>
      public const string UserSelect = User + ".Select";
  }

public class PermissionAuthorizationRequirement : IAuthorizationRequirement
{
    /// <summary>
    /// 参数是在使用策略授权时传入进来的,例如:Authorize(Policy= "MyPolicy")的MyPolicy
    /// </summary>
    /// <param name="name">Authorize(Policy= "MyPolicy")的MyPolicy</param>
    public PermissionAuthorizationRequirement(string name)
    {
        Name = name;
    }
    public string Name { get; set; }
}

    public class PermissionAuthorizationHandler : AuthorizationHandler<PermissionAuthorizationRequirement>
    {
        protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionAuthorizationRequirement requirement)
        {
            //取出当前用户的所有Permission的参数
            var permissions = context.User.Claims.Where(_ => _.Type == "Permission").Select(_ => _.Value).ToList();
            //是否满足授权,满足则运行 context.Succeed 
            if (permissions.Any(_ => _.StartsWith(requirement.Name)))
            {
                context.Succeed(requirement);
            }
            else
            {
                context.Fail();
            }
            return Task.CompletedTask;
        }
    }

Program

builder.Services.AddSingleton<IAuthorizationHandler, PermissionAuthorizationHandler>();
//控制器方法验证策略
builder.Services.AddAuthorization(options =>
{
    options.AddPolicy(Permissions.UserCreate, policy => policy.AddRequirements(new PermissionAuthorizationRequirement(Permissions.UserCreate)));
    options.AddPolicy(Permissions.UserUpdate, policy => policy.AddRequirements(new PermissionAuthorizationRequirement(Permissions.UserUpdate)));
    options.AddPolicy(Permissions.UserDelete, policy => policy.AddRequirements(new PermissionAuthorizationRequirement(Permissions.UserDelete)));
    options.AddPolicy(Permissions.UserSelect, policy => policy.AddRequirements(new PermissionAuthorizationRequirement(Permissions.UserSelect)));
});

7.最后.整个Program

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
//首先引用Microsoft.AspNetCore.Mvc.NewtonsoftJson包。再在builder.Services.AddControllers()后添加相应内容
builder.Services.AddControllers().AddNewtonsoftJson(options =>
{
    //设置JSON返回数据格式大小写与Model一致
    options.SerializerSettings.ContractResolver = new DefaultContractResolver();
    //设置一般API的日期格式
    options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss";
});

#region JWT
//获取配置文件
var configuration = builder.Configuration;
string Issuer = configuration["JWTToken:Issuer"];
string Audience = configuration["JWTToken:Audience"];
string SecretKey = configuration["JWTToken:SecretKey"];
//注入jwt
builder.Services.AddAuthentication(options =>
{
    options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
    options.TokenValidationParameters = new TokenValidationParameters()
    {
        //过期时间容错值,解决服务器端时间不同步问题(秒)
        //允许服务器时间偏移量30秒,即我们配置的过期时间加上这个允许偏移的时间值,才是真正过期的时间(过期时间 + 偏移值)你也可以设置为0,ClockSkew = TimeSpan.Zero
        ClockSkew = TimeSpan.FromSeconds(30),
        //要求Token的Claims中必须包含Expires
        RequireExpirationTime = true,
        //是否在令牌期间验证签发者
        ValidateIssuer = true,
        //发行人Issuer
        ValidIssuer = Issuer, 
        //是否验证接收者
        ValidateAudience = true,
        //是否验证失效时间
        ValidateLifetime = true,
        //是否验证签名SecurityKey
        ValidateIssuerSigningKey = true,
        //接收者
        ValidAudience = Audience,
        //密钥SecurityKey
        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(SecretKey)), 
    };
});
//注入JwtHelper
builder.Services.AddSingleton(new JwtHelper(configuration));
//注入授权策略(非必要,仅有需要自定义授权规则时使用)
builder.Services.AddSingleton<IAuthorizationHandler, JwtAuthorizationHandler>();
//账号密码验证策略
builder.Services.AddAuthorization(p =>
{
    p.AddPolicy("Account", t =>
    {
        t.Requirements.Add(new JwtAuthorizationRequirement("Account"));
    });
});

builder.Services.AddSingleton<IAuthorizationHandler, PermissionAuthorizationHandler>();
//控制器方法验证策略
builder.Services.AddAuthorization(options =>
{
    options.AddPolicy(Permissions.UserCreate, policy => policy.AddRequirements(new PermissionAuthorizationRequirement(Permissions.UserCreate)));
    options.AddPolicy(Permissions.UserUpdate, policy => policy.AddRequirements(new PermissionAuthorizationRequirement(Permissions.UserUpdate)));
    options.AddPolicy(Permissions.UserDelete, policy => policy.AddRequirements(new PermissionAuthorizationRequirement(Permissions.UserDelete)));
    options.AddPolicy(Permissions.UserSelect, policy => policy.AddRequirements(new PermissionAuthorizationRequirement(Permissions.UserSelect)));
});
#endregion
//注入Swagger
builder.Services.AddSwaggerGen(options =>
{
    options.AddSecurityRequirement(new Microsoft.OpenApi.Models.OpenApiSecurityRequirement
    {
        {
            new OpenApiSecurityScheme
            {
                Reference=new OpenApiReference{Id="Bearer",Type=ReferenceType.SecurityScheme},
            },
            Array.Empty<string>()
        }
    });

    options.AddSecurityDefinition("Bearer", new Microsoft.OpenApi.Models.OpenApiSecurityScheme
    {
        Description = "请输入文字'Bearer '后面跟空格和token格式  Bearer {token}",
        Name = "Authorization",
        In = Microsoft.OpenApi.Models.ParameterLocation.Header,
        Type = Microsoft.OpenApi.Models.SecuritySchemeType.ApiKey
    });
});

//配置跨域
builder.Services.AddCors(policy =>
{
    policy.AddPolicy("CorsPolicy", opt => opt
    .AllowAnyOrigin()
    .AllowAnyHeader()
    .AllowAnyMethod()
    .WithExposedHeaders("X-Pagination"));
});
var app = builder.Build();
//调用中间件:UseAuthentication(认证),
//必须在所有需要身份认证的中间件前调用,比如 UseAuthorization(授权)。
app.UseAuthentication();
//调用中间件:UseAuthorization(授权)。
app.UseAuthorization();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

app.MapControllers();

app.Run();

参考链接:ASP.NET Core 6.0 添加 JWT 认证和授权 - 芦荟柚子茶 - 博客园

相关推荐
数据的世界0112 小时前
.NET开发人员学习书籍推荐
学习·.net
paixiaoxin14 小时前
CV-OCR经典论文解读|An Empirical Study of Scaling Law for OCR/OCR 缩放定律的实证研究
人工智能·深度学习·机器学习·生成对抗网络·计算机视觉·ocr·.net
1900431 天前
.NET重点
.net
m0_663234011 天前
在 .NET 5.0 运行 .NET 8.0 教程:使用 ASP.NET Core 创建 Web API
前端·asp.net·.net
小码编匠2 天前
.NET 下 RabbitMQ 队列、死信队列、延时队列及小应用
后端·c#·.net
真想骂*2 天前
vmime.net_4.dll详解:它是什么,有何用途?
.net
lzhdim2 天前
2、C#基于.net framework的应用开发实战编程 - 设计(二、二) - 编程手把手系列文章...
开发语言·c#·.net
工业3D_大熊2 天前
HOOPS Communicator功能剖析:3D Web模型树交互的实用指南!
linux·windows·macos·3d·docker·c#·.net
小码编匠3 天前
C# 使用心跳机制实现TCP客户端自动重连
后端·c#·.net