【开发技术】Abp.vnext下的JWT处理,添加多种认证处理方式

目录

一、目的

二、解决方案

2.1 什么是认证和授权

2.2 支持标准 JWT Bearer 认证

2.2.1 引用“Microsoft.AspNetCore.Authentication.JwtBearer”

2.2.2 调用 AddAuthentication 和 AddJwtBearer 注册服务

2.2.3 在 appsettings.json 配置密钥、颁发者等参数

2.3 自定义认证

2.4 自定义授权处理

2.4.1需要实现自定义授权处理器

2.4.2 配置授权策略

2.5 配置授权策略

三、文章总结


一、目的

        当前开发一个Api服务时,可能存在多种服务调用请求场景,例如:App\Web\小程序、内部项目、第三方对接等请求Api。本文展示基于Abp.vnext应用架构下,使用JWT支持多种场景的认证和授权。
         认证流程:        
         多种认证和授权流程:

二、解决方案

2.1 什么是认证和授权

        认证:即身份认证,因为Htpp协议是无状态的,每个Http请求发过来是无法识别身份的。为了进行身份识别,一般需要你提供一个账号和密码进行登录,登录通过则表示你是正常用户,给你颁发一个身份标识。通过这个身份标识去访问其他服务时,会检查你是否有有这身份标识,有则标识为认证用户,运行请求,没有则会拒绝。 举个例子:看演唱会或则坐车,买了票后,需要凭相关票据才可进入会场。
        授权:即授予权限,当你认证通过后需要请求某个页面或则某个服务方法时,会检查分配的唯一标识是否有权限。 举个例子:看演唱会买了看台的票,你想去坐VIP位置,这显然是需要加钱的。

2.2 支持标准 JWT Bearer 认证

使用流程:
  • 显式安装 NuGet 包
  • 调用 AddAuthentication 和 AddJwtBearer 注册服务
  • 在 appsettings.json 配置密钥、颁发者等参数

2.2.1 引用“Microsoft.AspNetCore.Authentication.JwtBearer”

dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer
dotnet add package Microsoft.IdentityModel.Tokens
dotnet add package System.IdentityModel.Tokens.Jwt

2.2.2 调用 AddAuthentication 和 AddJwtBearer 注册服务

基础示例:
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
        .AddJwtBearer(options => { /* 参数配置 */ }); 
实际使用:
namespace Demo.HttpApi.Host;

public class DemoHttpApiHostModule : AbpModule
{
    public override void ConfigureServices(ServiceConfigurationContext context)
    {
        var services = context.Services;
        var configuration = services.GetConfiguration();

        // 跨域
        context.Services.AddCors(options =>
        {
            options.AddPolicy("AllowAll", builder =>
            {
                builder.AllowAnyOrigin()
                    .SetIsOriginAllowedToAllowWildcardSubdomains()
                    .AllowAnyHeader()
                    .AllowAnyMethod();
            });
        });

        // Configure Jwt Authentication
        ConfigureAuthentication(context);
    }

    public override void OnApplicationInitialization(ApplicationInitializationContext context)
    {
        var app = context.GetApplicationBuilder();
        
        // 定义在2者之间
        //app.UseRouting();
        app.UseAuthentication();
        //app.UseConfiguredEndpoints();
    }

    private void ConfigureAuthentication(ServiceConfigurationContext context)
    {
        var services = context.Services;
        var configuration = services.GetConfiguration();
        string issuer = configuration["Jwt:Issuer"];
        string audience = configuration["Jwt:Audience"];
        string expire = configuration["Jwt:ExpireMinutes"];
        TimeSpan expiration = TimeSpan.FromMinutes(Convert.ToDouble(expire));
        SecurityKey key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(configuration["Jwt:SecurityKey"]));

        services.AddAuthentication(s =>
        {
            // 2、Authentication
            s.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            s.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
            s.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
        }).AddJwtBearer(s =>
        {
            // 3、Use Jwt bearer 
            s.TokenValidationParameters = new TokenValidationParameters
            {
                ValidIssuer = issuer,
                ValidAudience = audience,
                IssuerSigningKey = key,
                ClockSkew = expiration,
                ValidateLifetime = true
            };
            s.Events = new JwtBearerEvents
            {
                OnAuthenticationFailed = context =>
                {
                    // Token expired
                    if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
                    {
                        context.Response.Headers.Add("Token-Expired", "true");
                    }
                    return Task.CompletedTask;
                }
            };
        });

    }
}

2.2.3 在 appsettings.json 配置密钥、颁发者等参数

{
    "Jwt": {
    "Issuer": "***.com",//发行人
    "Audience": "***.com",//接收者
    "SecurityKey": "*****",//密钥
    "ExpireMinutes": "60"//过期时间,单位:分钟
  }
}

2.3 自定义认证

        在某些情况下我们需要添加一些内部的自定义认证,用于识别内部系统调用,或则是其他第三方接口,用于执行一些特殊处理逻辑。
         增加自定义的Scheme:“InternalAuth”.通过AddScheme添加认证逻辑,以及认证处理器来处理认证逻辑,完成对于调用端身份认证。
 /// <summary>
 /// jwt设置
 /// </summary>
 /// <param name="context"></param>
 private void ConfigureAuthentication(ServiceConfigurationContext context)
 {
     var services = context.Services;
     var configuration = services.GetConfiguration();
     string issuer = configuration["Jwt:Issuer"];
     string audience = configuration["Jwt:Audience"];
     string expire = configuration["Jwt:ExpireMinutes"];
     TimeSpan expiration = TimeSpan.FromMinutes(Convert.ToDouble(expire));
     SecurityKey key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(configuration["Jwt:SecurityKey"]));

     services.AddAuthentication(s =>
     {
         // 2、Authentication
         s.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
         s.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
         s.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
     }).AddJwtBearer(s =>
     {
         // 3、Use Jwt bearer 
         s.TokenValidationParameters = new TokenValidationParameters
         {
             ValidIssuer = issuer,
             ValidAudience = audience,
             IssuerSigningKey = key,
             ClockSkew = expiration,
             ValidateLifetime = true
         };
         s.Events = new JwtBearerEvents
         {
             OnAuthenticationFailed = context =>
             {
                 // Token expired
                 if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
                 {
                     context.Response.Headers.Add("Token-Expired", "true");
                 }
                 return Task.CompletedTask;
             }
         };
     })// 添加内部认证方案
     .AddScheme<InternalAuthOptions, InternalAuthHandler>(
         "InternalAuth",
         opts => { }); // 可以配置选项如果有需要;
 }
public class InternalAuthOptions : AuthenticationSchemeOptions
{
    // 可以添加自定义选项,比如头名称等
}

public class InternalAuthHandler : AuthenticationHandler<InternalAuthOptions>
{
    public InternalAuthHandler(
        IOptionsMonitor<InternalAuthOptions> options,
        ILoggerFactory logger,
        UrlEncoder encoder,
        ISystemClock clock)
        : base(options, logger, encoder, clock)
    {
    }

    protected override Task<AuthenticateResult> HandleAuthenticateAsync()
    {
        var authHeader = Request.Headers["X-Internal-Auth-Token"].ToString();

        // 这里添加你的内部认证逻辑
        if (string.IsNullOrEmpty(authHeader) || authHeader != "你的内部认证密钥")
        {
            return Task.FromResult(AuthenticateResult.Fail("内部认证令牌无效"));
        }

        // 创建身份主体
        var claims = new[] { new Claim(ClaimTypes.Name, "InternalSystem") };
        var identity = new ClaimsIdentity(claims, Scheme.Name);
        var principal = new ClaimsPrincipal(identity);
        var ticket = new AuthenticationTicket(principal, Scheme.Name);

        return Task.FromResult(AuthenticateResult.Success(ticket));
    }
}

2.4 自定义授权处理

2.4.1需要实现自定义授权处理器

public class InternalRequirement : IAuthorizationRequirement
{
}

public class InternalHandler : AuthorizationHandler<InternalRequirement>
{
    protected override Task HandleRequirementAsync(
        AuthorizationHandlerContext context,
        InternalRequirement requirement)
    {
        // 如果通过JWT或者内部认证任一成功,则授权通过
        if (context.User.Identity.IsAuthenticated)
        {
            context.Succeed(requirement);
        }

        return Task.CompletedTask;
    }
}

2.4.2 配置授权策略

   /// <summary>
 /// jwt授权设置
 /// </summary>
 /// <param name="context"></param>
 private void ConfigureAuthentication(ServiceConfigurationContext context)
 {
     var services = context.Services;
     var configuration = services.GetConfiguration();
     string issuer = configuration["Jwt:Issuer"];
     string audience = configuration["Jwt:Audience"];
     string expire = configuration["Jwt:ExpireMinutes"];
     TimeSpan expiration = TimeSpan.FromMinutes(Convert.ToDouble(expire));
     SecurityKey key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(configuration["Jwt:SecurityKey"]));

     services.AddAuthorization(options =>
      {
          // 1、Definition authorization policy
          options.AddPolicy("Permission", policy => policy.Requirements.Add(new InternalRequirement()));
      }).AddAuthentication(s =>
     {
         // 2、Authentication
         s.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
         s.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
         s.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
     }).AddJwtBearer(s =>
     {
         // 3、Use Jwt bearer 
         s.TokenValidationParameters = new TokenValidationParameters
         {
             ValidIssuer = issuer,
             ValidAudience = audience,
             IssuerSigningKey = key,
             ClockSkew = expiration,
             ValidateLifetime = true
         };
         s.Events = new JwtBearerEvents
         {
             OnAuthenticationFailed = context =>
             {
                 // Token expired
                 if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
                 {
                     context.Response.Headers.Add("Token-Expired", "true");
                 }
                 return Task.CompletedTask;
             }
         };
     })// 添加内部认证方案
     .AddScheme<InternalAuthOptions, InternalAuthHandler>(
         "InternalAuth",
         opts => { }); // 可以配置选项如果有需要;
      
          //注入授权处理器
          services.AddSingleton<IAuthorizationHandler, InternalHandler>();
     }

  
    public override void OnApplicationInitialization(ApplicationInitializationContext context)
    {
        var app = context.GetApplicationBuilder();
        
        // 定义在2者之间
        //app.UseRouting();
        app.UseAuthentication();
        app.UseAuthorization();
        //app.UseConfiguredEndpoints();
    }

2.5 配置授权策略

在控制器或应用服务上使用授权特性:

[Authorize(Policy = "InternalAuth")]
public class MyAppService : ApplicationService
{
    // ...
}
针对特定方法:
[Authorize(Policy = "InternalAuth")]
public async Task<MyDto> GetDataAsync()
{
    // ...
}
若有多个Scheme和Policy :
 [Authorize(AuthenticationSchemes = $"{JwtBearerDefaults.AuthenticationScheme},InternalAuth", Policy = "Permission")]
 public class MyAppService : ApplicationService
{
    // ...
}

三、文章总结

        在.netCore中认证和授权都是通过中间件的方式进行处理,弄清楚实现原理,在今后的开发中会更易于针对不同场景进行扩展和应用。文章内部当前未加入对于Jwt Token生成部分,这块网络挺多的就不重复编写了。
        
        把之所学以文载之,欢迎大家多多交流~
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

勿芮介

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值