ASP.NET Core 8.0 JWT安全实践:从认证到授权的全链路实现

一、认证与授权的区别

1、概念核心差异‌


认证(Authentication)‌

验证用户身份的过程,确认用户是否合法且"是否是他所声称的人"。例如用户通过用户名密码登录系统时,系统会验证凭证的有效性。

  • 在.NET中,认证的底层实现通常与 IIdentity 接口相关,该接口存储用户的基本身份信息(如用户名)。
  • 常见的认证方式包括:Cookie 认证、JWT(JSON Web Tokens)、OAuth 2.0 等。

授权(Authorization)‌

确定用户是否有权限访问特定资源或执行操作。例如,管理员登录后可管理系统设置,而普通用户只能查看基础数据。

  • 在.NET中,授权依赖于 IPrincipal 接口,该接口包含用户的角色或权限信息。
  • 实现方式包括:基于角色的授权、策略授权、声明(Claims)授权等。

2、功能阶段划分‌


认证是前置条件‌

用户必须先通过认证(如登录成功)后,系统才进一步处理授权逻辑。例如,ASP.NET Core 中的认证中间件会先验证请求中的身份标识(如 Cookie 或 Token),若无效则直接拒绝访问。


授权是后续决策‌

认证成功后,系统根据用户身份(角色、权限等)动态控制其对资源的访问权限。例如,通过 Authorize(Roles = "Admin") 属性限制只有管理员角色才能访问某个接口。

3、技术实现差异‌

维度‌ 认证(Authentication)‌ 授权(Authorization)‌
核心接口‌ ‌IIdentity(存储用户身份信息) ‌ IPrincipal(存储用户角色/权限信息)
‌典型场景‌ ‌用户登录、Token 签发 ‌权限校验、资源访问控制
‌配置方式‌ ‌通过 AddAuthentication 配置认证方案 ‌ 通过 AddAuthorization 定义策略或角色要求

4、实际应用示例‌

  • 认证示例‌
    用户通过表单提交用户名密码,系统验证后生成 Cookie 或 JWT,并返回给客户端。
  • 授权示例‌
    已认证的用户访问管理页面时,系统检查其角色是否为"Admin",若否则返回 403 错误。

总结

认证解决"你是谁",授权解决"你能做什么"。在.NET中,二者通过不同接口和中间件分离实现,但需协同工作以构建安全的应用程序。

二、认证(Authenication)

1、创建项目

bash 复制代码
dotnet new webapi -controllers -f net8.0

2、安装 Nuget 包

bash 复制代码
# Microsoft.AspNetCore.Authentication.JwtBearer
dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer -v 8.0.7

3、生成秘钥

我们需要一个密钥用于加密和验证 JWT,可以使用使用 RandomNumberGenerator 类生成随机字节。

csharp 复制代码
using System;
using System.Security.Cryptography;

// 使用示例:
var secretKey = JwtKeyGenerator.GenerateSecureSecret();
Console.WriteLine($"Generated SecretKey: {secretKey}");

public class JwtKeyGenerator
{
    public static string GenerateSecureSecret(int keyLengthInBytes = 32)
    {
        // 生成加密安全的随机字节数组(推荐256位即32字节)
        byte[] keyBytes = new byte[keyLengthInBytes];
        using (var rng = RandomNumberGenerator.Create())
        {
            rng.GetBytes(keyBytes);
        }

        // 将字节数组转换为 Base64 字符串(更紧凑且适合配置)
        return Convert.ToBase64String(keyBytes);
    }
}
bash 复制代码
Generated SecretKey: pEZADfw6uJe8MOAozDoaAuXC+/ATW4XysMtQwH7wFmw=

4、配置信息

在 appsettings.json 中新增节点信息:

bash 复制代码
  "JwtConfig": {
    "SecretKey": "pEZADfw6uJe8MOAozDoaAuXC+/ATW4XysMtQwH7wFmw=",
    "Audience": "test",
    "Issuer": "test", 
    "Expired": 5 
  }

5、JwtConfig

新增 JwtConfig 对象类,用于读取配置文件信息:

csharp 复制代码
using System.Text;
using Microsoft.IdentityModel.Tokens;

namespace JwtDemo.Config
{
    public class JwtConfig
    {
        /// <summary>
        /// 原始密钥
        /// </summary>
        public string? SecretKey { get; set; }

        /// <summary>
        /// 接收者
        /// </summary>
        public string? Issuer { get; set; }

        /// <summary>
        /// 生效时间(分钟)
        /// </summary>
        public int Expired { get; set; }

        /// <summary>
        /// 颁发者
        /// </summary>
        public string? Audience { get; set; }

        /// <summary>
        /// 生效时间
        /// </summary>
        public DateTime NoteBeofre => DateTime.Now;

        /// <summary>
        /// 过期时间
        /// </summary>
        public DateTime Expiration => DateTime.Now.AddMinutes(Expired);

        /// <summary>
        /// 安全密钥
        /// </summary>
        public SecurityKey SignKey => new SymmetricSecurityKey(Encoding.UTF8.GetBytes(SecretKey));

        /// <summary>
        /// 签名凭证
        /// </summary>
        public SigningCredentials SigningCredentials => new SigningCredentials(SignKey, SecurityAlgorithms.HmacSha256);
    }

}

6、JwtHelper

新增 JwtHelper 工具类,主要用于生成和解析 Token:

csharp 复制代码
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using JwtDemo.Config;

namespace JwtDemo.Helper
{
    public class JwtHelper
    {
        private JwtConfig _jwtConfig;

        public JwtHelper(JwtConfig config)
        {
            _jwtConfig = config;
        }

        public string GetAccessToken(string userId,string password)
        {
            var claims = new List<Claim>() {
                new Claim("UserId", userId),
                new Claim("Password", password)
            };
            var jwtSecurityToken = new JwtSecurityToken(
                    issuer: _jwtConfig.Issuer,
                    audience: _jwtConfig.Audience,
                    claims,
                    notBefore: _jwtConfig.NoteBeofre,
                    expires: _jwtConfig.Expiration,
                    signingCredentials: _jwtConfig.SigningCredentials
                );
            var token = new JwtSecurityTokenHandler().WriteToken(jwtSecurityToken);

            return token;
        }

        public List<Claim> GetClaims(HttpRequest request)
        {
            var authorization = request.Headers["Authorization"].ToString();
            var auth = authorization.Split(" ")[1];
            var handler = new JwtSecurityTokenHandler();
            //反解密,获取其中的Claims
            var payload = handler.ReadJwtToken(auth).Payload;
            var claims = payload.Claims;
            return claims.ToList();
        }
    }

}

7、JwtService

在这里针对 services 新增一个扩展方法 JwtService,用于注册 Jwt 服务:

csharp 复制代码
using JwtDemo.Config;
using JwtDemo.Helper;
using Microsoft.AspNetCore.Authentication.JwtBearer;

namespace JwtDemo.Service
{
    public static class JwtService
    {
        public static void AddJwtService(this IServiceCollection services, ConfigurationManager config)
        {
            var jwtConfig = new JwtConfig();
            // 新增 JwtConfig 节点信息与 JwtConfig对象类做绑定,方便读取数据。
            config.Bind("JwtConfig", jwtConfig);
            services.AddSingleton(jwtConfig);

            // 将 JwtHelper 工具类注册为单例模式
            services.AddSingleton<JwtHelper>();

            services.AddAuthentication(option =>
            {
                // 认证配置
                option.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                option.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            })
            .AddJwtBearer(options =>
            {
                options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
                {

                    ValidIssuer = jwtConfig.Issuer,             // 发行人
                    ValidAudience = jwtConfig.Audience,         // 订阅人
                    IssuerSigningKey = jwtConfig.SignKey,       // SecurityKey
                    ValidateLifetime = true,                    // 验证Token有效期
                    ValidateIssuer = true,                      // 是否验证Issure
                    ValidateAudience = true,                    // 是否验证 Audience
                    ValidateIssuerSigningKey = true,            // 是否验证SecurityKey
                    RequireExpirationTime = true,               // 是否验证失效时间
                    ClockSkew = TimeSpan.FromSeconds(30) // 缓冲时间,默认5分钟
                };
            });
        }
    }

}

8、调用中间件

csharp 复制代码
// Program.cs

// ...
// JWT 认证服务
builder.Services.AddJwtService(builder.Configuration);
// ...

// 先认证
app.UseAuthentication();
// 再授权
app.UseAuthorization();

9、AccountController

csharp 复制代码
using JwtDemo.DTO;
using JwtDemo.Helper;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

namespace JwtDemo.Controllers
{

    [ApiController]
    [Route("api/[controller]")]
    public class AccountController : ControllerBase
    {
        private readonly JwtHelper _jwtHelper;
        public AccountController(JwtHelper jwtHelper)
        {
            _jwtHelper = jwtHelper;
        }

        [HttpPost("login")]
        public IResult Login(AccountDTO dto)
        {
            if (string.IsNullOrEmpty(dto.UserId) || string.IsNullOrEmpty(dto.Password))
            {
                return TypedResults.BadRequest("用户名或密码为空");
            }

            var token = _jwtHelper.GetAccessToken(dto.UserId,dto.Password);
            return TypedResults.Ok(token);
        }

        [HttpGet("test")]
        [Authorize]
        public IResult Test()
        {
            return TypedResults.Ok("success");
        }

    }
}

10、认证测试

访问 http://localhost:5076/swagger/index.html ,使用 Swagger 进行测试,

可以看到,使用账号密码登录,成功返回 Token。

当密码为 null 时返回报错 "用户名或密码为空"。

不携带 Token 访问 http://localhost:5076/api/Account/test 时,报错 401 Error: Unauthorized。

携带 Token 访问时,返回成功。

11、从 Token 中解析信息

直接通过 Request 属性调用:

csharp 复制代码
namespace JwtDemo.Controllers
{

    [ApiController]
    [Route("api/[controller]")]
    public class AccountController : ControllerBase
    {
        // ...
        
        [HttpGet("getClaims")]
        [Authorize]
        public IResult GetClaims()
        {
            var claims = _jwtHelper.GetClaims(Request);
            // 将 claims 转换为简单对象返回
            var claimData = claims.Select(c => new { Type = c.Type, Value = c.Value });
            return TypedResults.Ok(claimData);
        }

    }
}

三、授权(Authorization)

1、基本属性

Authorize 允许经过身份验证的用户访问该组件,AllowAnoymous允许未身份验证的用户访问单个操作。如果这两个属性结合使用,系统会忽略 Authorize 属性。

授权规则可以是 Roles(角色)、Policy(策略)、AuthenticationSchemes(方案):

csharp 复制代码
[Authorize(Roles = "", Policy = "", AuthenticationSchemes = "")]

2、Roles 角色授权

基于角色授权,用户拥有这个角色,就能通过授权验证。在认证时给用户添加相应的 Claim,即可表示该用户拥有该角色,一个用户可以拥有多个角色。

csharp 复制代码
namespace JwtDemo.Helper
{
    public class JwtHelper
    {
        // ...
        
        public string GetAccessToken(string userId,string[] userRoles)
        {
            var claims = new List<Claim>() {
                new Claim("UserId", userId)
            };

            // 逐个添加角色到 Claim
            foreach (var role in userRoles) {
                claims.Add(new Claim(ClaimTypes.Role, role));
            }

            var jwtSecurityToken = new JwtSecurityToken(
                    issuer: _jwtConfig.Issuer,
                    audience: _jwtConfig.Audience,
                    claims,
                    notBefore: _jwtConfig.NoteBeofre,
                    expires: _jwtConfig.Expiration,
                    signingCredentials: _jwtConfig.SigningCredentials
                );
            var token = new JwtSecurityTokenHandler().WriteToken(jwtSecurityToken);

            return token;
        }

        // ...
}

在 AccountController 中配置角色授权,

csharp 复制代码
using JwtDemo.DTO;
using JwtDemo.Helper;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

namespace JwtDemo.Controllers
{

    [ApiController]
    [Route("api/[controller]")]
    public class AccountController : ControllerBase
    {
        private readonly JwtHelper _jwtHelper;

        public AccountController(JwtHelper jwtHelper)
        {
            _jwtHelper = jwtHelper;
        }

        [HttpPost("login")]
        public IResult Login(AccountDTO dto)
        {
            if (string.IsNullOrEmpty(dto.UserId) || string.IsNullOrEmpty(dto.Password))
            {
                return TypedResults.BadRequest("用户名或密码为空");
            }

            var token = _jwtHelper.GetAccessToken(dto.UserId, new string[] {"user"});
            return TypedResults.Ok(token);
        }

        [HttpGet("test1")]
        [Authorize]
        public IResult Test1()
        {
            return TypedResults.Ok("Test1");
        }

        [HttpGet("test2")]
        [Authorize(Roles = "user")]
        public IResult Test2()
        {
            return TypedResults.Ok("Roles = user");
        }

        [HttpGet("test3")]
        [Authorize(Roles = "admin")]
        public IResult Test3()
        {
            return TypedResults.Ok("Roles = admin");
        }

        [HttpGet("getClaims")]
        [Authorize(Roles = "admin,user")]
        public IResult GetClaims()
        {
            var claims = _jwtHelper.GetClaims(Request);
            // 将 claims 转换为简单对象返回
            var claimData = claims.Select(c => new { Type = c.Type, Value = c.Value });
            return TypedResults.Ok(claimData);
        }

    }
}

运行效果:

Test1 接口没有配置授权方式,因此只需要认证之后就能访问。

Test2 接口配置了角色 user 能够访问。

Test3 接口配置了角色 admin 能够访问,因此用户角色 user 尝试访问该接口的时候会返回 403 Forbidden 禁止访问。

getClaims 接口配置了角色 admin 或 user 都能够访问。

3、Policy 策略授权

一个 Policy 可以包含一个或多个要求,可以是 Roles 匹配,也可以是 Claims 匹配等。

csharp 复制代码
services.AddAuthorization(options => {
    options.AddPolicy("policy1", x => x.RequireClaim("Employee"));
    options.AddPolicy("policy2", x => x.RequireClaim("Employee", "p1", "p2", "p3"));
});

以上代码案例,添加了两个 policy:

  • policy1,要求 Cliams 必须包含一个Employee
  • policy2,要求 Claims 必须包含一个 Employee 且值必须要 p1、p2 或 p3 其中的一个。

在控制器上或者方法上添加属性 Authorize 即可生效:

csharp 复制代码
namespace JwtDemo.Controllers
{

    [ApiController]
    [Route("api/[controller]")]
    public class AccountController : ControllerBase
    {
       
        // ...
        
        [Authorize(Policy = "policy1")]
        [HttpGet("test4")]
        public IResult Test4()
        {
            return TypedResults.Ok("Policy = policy1");
        }
        
        [Authorize(Policy = "policy2")]
        [HttpGet("test5")]
        public IResult Test5()
        {
            return TypedResults.Ok("Policy = policy2");
        }

        // ...

    }
}

运行效果:

当 Token 不携带 Employee 声明时,返回 403 Forbidden 。此时修改 Helper/JwtHelper.cs 中的 GetAccessToken 方法,添加 Employee 声明:

csharp 复制代码
namespace JwtDemo.Helper
{
    public class JwtHelper
    {
        // ...
        
        public string GetAccessToken(string userId, string[] userRoles,string employeeId)
        {
            var claims = new List<Claim>() {
                new Claim("UserId", userId),
                new Claim("Employee", employeeId)
            };

            // 逐个添加角色到 Claim
            foreach (var role in userRoles)
            {
                claims.Add(new Claim(ClaimTypes.Role, role));
            }

            var jwtSecurityToken = new JwtSecurityToken(
                    issuer: _jwtConfig.Issuer,
                    audience: _jwtConfig.Audience,
                    claims,
                    notBefore: _jwtConfig.NoteBeofre,
                    expires: _jwtConfig.Expiration,
                    signingCredentials: _jwtConfig.SigningCredentials
                );
            var token = new JwtSecurityTokenHandler().WriteToken(jwtSecurityToken);

            return token;
        }

        // ...
        
    }

}

Login 方法对应修改:

csharp 复制代码
namespace JwtDemo.Controllers
{

    [ApiController]
    [Route("api/[controller]")]
    public class AccountController : ControllerBase
    {

        // ...
        
        [HttpPost("login")]
        public IResult Login(AccountDTO dto)
        {
            if (string.IsNullOrEmpty(dto.UserId) || string.IsNullOrEmpty(dto.Password))
            {
                return TypedResults.BadRequest("用户名或密码为空");
            }

            var token = _jwtHelper.GetAccessToken(dto.UserId, ["user"],"p1");
            return TypedResults.Ok(token);
        }

        // ...
    }
}

可以看到,当 Token 携带 Employee 声明时,访问接口正常。

当 Token 携带 Employee 声明的值在策略范围内,访问接口正常。

csharp 复制代码
[Authorize(Policy = "policy2", Roles = "user")]

采用角色+策略混合授权方式,也是能够正常访问接口。

4、基于策略 Policy 的自定义授权

在配置中调用 AddPolicy 来注册策略,在某些情况下,可能无法采用此方式注册所有策略,在这些情况下,可以使用自定义 IAuthorizationPolicyProvider 来控制如何提供授权策略,比如在运行时使用外部数据源中的信息策略确定授权要求。授权的主要方式,是通过 Handler 程序判断授权,需要实现 IAuthorizationHandler 接口。

实现步骤如下:

  • 自定义一个 Requirement 实现 IAuthorizationRequirement
  • 自定义 Handler 程序继承 AuthorizationHandler<TRequirement> 并重写 HandleRequirementAsync 方法。
  • 实现策略提供的 PolicyProvider 来动态添加策略。

Step1、实现 AuthorizeAttribute

新增属性,继承 AuthorizeAttribute 为我们添加额外属性来增加自定义授权,属性 Policy 不可为 null。

csharp 复制代码
using Microsoft.AspNetCore.Authorization;

namespace JwtDemo.Attribute
{
    public class MyPermissionAttribute : AuthorizeAttribute
    {
        const string _permission = "_auth_permission:";
        public string? Permission
        {
            get
            {
                return Permission;
            }
            set
            {
                // Policy 为必须值,在为per更改时,与Policy同步。
                // 如果为null,自定义的策略则不会响应。
                Policy = _permission + value?.ToString();
            }
        }
        /// <summary>
        /// 参数构造
        /// </summary>
        /// <param name="permission"></param>
        public MyPermissionAttribute(string permission)
        {
            Permission = permission;
        }
        /// <summary>
        /// 无参构造
        /// </summary>
        public MyPermissionAttribute()
        {
        }

    }
}

Step2、实现 Requirement

csharp 复制代码
using Microsoft.AspNetCore.Authorization;

namespace JwtDemo.Impl
{
    public class PermissionRequirement : IAuthorizationRequirement
    {
        /// <summary>
        /// 权限名
        /// </summary>
        public string? Permission { get; set; }
        /// <summary>
        /// 参数构造
        /// </summary>
        /// <param name="permission"></param>
        public PermissionRequirement(string permission) => this.Permission = permission; 
    }
}

Step3、实现策略提供程序 PolicyProvider

通过配置的方式添加策略不灵活,无法动态添加。通过实现IAuthorizationPolicyProvider 并添加到 DI 中,可实现动态 Policy。

IAuthorizationPolicyProvider 默认实现为DefaultAuthorizationPolicyProvider

实现一个 PolicyProvider 代码如下:

csharp 复制代码
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.Options;

namespace JwtDemo.Impl
{

    public class MyPermissionPolicyProvider : DefaultAuthorizationPolicyProvider, IAuthorizationPolicyProvider
    {

        /// <summary>
        /// 策略名称前缀
        /// </summary>
        private const string POLICY_PREFIX = "_auth_permission:";

        // 只能使用一个授权策略,如果自定义实现不处理所有策略,他应该退回到默认实现(在这里继承DefaultAuthorizationPolicyProvider,使用原有的实现。)
        public MyPermissionPolicyProvider(IOptions<AuthorizationOptions> options) : base(options)
        {
        }

        public async new Task<AuthorizationPolicy> GetDefaultPolicyAsync()
        {
            return await base.GetDefaultPolicyAsync();
        }

        public async new Task<AuthorizationPolicy?> GetFallbackPolicyAsync()
        {
            return await base.GetFallbackPolicyAsync();
        }
        

        // 通过字符串名称进行查找
        public new Task<AuthorizationPolicy?> GetPolicyAsync(string policyName)
        {

            if (policyName.StartsWith(POLICY_PREFIX))
            {
                var policy = new AuthorizationPolicyBuilder();
                var targetName = policyName.Substring(POLICY_PREFIX.Length);
                policy.AddRequirements(new PermissionRequirement(targetName));
                var build = policy.Build();

                if (build == null)
                {

                }
                
                return Task.FromResult(build);
            }

            // 如果名称与期望的格式不匹配,使用默认实现。
            return base.GetPolicyAsync(policyName);
        }
    }

}

注意:只有最后一个添加的 PolicyProvider 会生效,自定义 Provider 必须实现 IAuthorizationPolicyProvider,否则添加到 DI 不生效。

Step4、实现 Handler

运行 HandleRequirementAsync 时, 会将用户的 Claim 中 ClaimType 为 UserID 的项取出,检测是否有执行权限。

csharp 复制代码
using JwtDemo.Impl;
using Microsoft.AspNetCore.Authorization;

namespace JwtDemo.Handler
{
    public class MyPermissionAuthorizationHandler : AuthorizationHandler<PermissionRequirement>
    {
        private ILogger<MyPermissionAuthorizationHandler> _logger;

        public MyPermissionAuthorizationHandler(ILogger<MyPermissionAuthorizationHandler> logger)
        {
            _logger = logger;
        }

        /// <summary>
        /// 为内测人员账号 123456 添加 test 接口的查询和添加权限
        /// </summary>
        Dictionary<string, List<string>> keys = new Dictionary<string, List<string>>() { { "123456", new List<string> { "test.get", "test.add" } } };

        // 检查上下文是否符合给全要求
        protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionRequirement requirement)
        {

            var claim = context.User.Claims.FirstOrDefault(x => x.Type.Equals("UserId"));
            if (claim == null)
            {
                _logger.LogWarning("claims中未找到 Permission 相关权限。");
                return Task.CompletedTask;
            }

            // 权限判断,可以是外部数据,数据库等权限配置。
            if (!keys.ContainsKey(claim.Value))
            {
                _logger.LogWarning("claims中未找到匹配的人员相关权限。");
                context.Fail();
                return Task.CompletedTask;
            }

            var rule = keys[claim.Value].FirstOrDefault(x => x.Equals(requirement.Permission));
            if (rule == null)
            {
                _logger.LogWarning($"claims中未找到匹配的资源相关权限。Permission {requirement.Permission}");
                context.Fail();
                return Task.CompletedTask;
            }

            // 成功
            context.Succeed(requirement);

            // 失败
            //context.Fail();
            return Task.CompletedTask;
        }
    }

}

Step5、在 Action 或 Controller 中配置所需权限

csharp 复制代码
namespace JwtDemo.Controllers
{

    [ApiController]
    [Route("api/[controller]")]
    public class AccountController : ControllerBase
    {
        // ...
        
        [Authorize(Policy = "policy2", Roles = "user")]
        [HttpGet("test6")]
        [MyPermission(Permission = "test.get")]
        public IResult Test6()
        {
            return TypedResults.Ok("Permission = test.get");
        }

        [Authorize(Policy = "policy2", Roles = "user")]
        [HttpGet("test7")]
        [MyPermission(Permission = "test.add")]
        public IResult Test7()
        {
            return TypedResults.Ok("Permission = test.add");
        }

        // ...

    }
}

Step6、启用自定义授权策略

在 Service/JwtService.cs 中,将自定义的 PolicyProvider 和 Handler 添加到 DI 中:

csharp 复制代码
services.AddSingleton<IAuthorizationPolicyProvider, MyPermissionPolicyProvider>();
services.AddSingleton<IAuthorizationHandler, MyPermissionAuthorizationHandler>();

运行效果:

可以看到,使用账号 123456 访问自定义权限的接口 test6 和 test7 都有权限,同时也能正常访问没有自定义权限的接口。

若此时新增接口 test8,使用自定义权限 test.edit,则不在指定的权限范围内,无法访问。

5、自定义 AuthorizationMiddleware

自定义 IAuthorizationMiddlewareResultHandler 处理授权结果,用于:

  • 返回自定义的响应
  • 增强默认质询或禁止响应。
csharp 复制代码
using System.Net;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authorization.Policy;

namespace JwtDemo.Handler
{

    public class MyAuthorizationMiddlewareResultHandler : IAuthorizationMiddlewareResultHandler
    {
        private readonly AuthorizationMiddlewareResultHandler
             DefaultHandler = new AuthorizationMiddlewareResultHandler();


        public async Task HandleAsync(
            RequestDelegate requestDelegate,
            HttpContext httpContext,
            AuthorizationPolicy authorizationPolicy,
            PolicyAuthorizationResult policyAuthorizationResult)
        {
            /* 官方示例
            // if the authorization was forbidden and the resource had specific requirements,
            // provide a custom response.
            if (Show404ForForbiddenResult(policyAuthorizationResult))
            {
                // Return a 404 to make it appear as if the resource does not exist.
                httpContext.Response.StatusCode = (int)HttpStatusCode.NotFound;
                return;
            }

            // Fallback to the default implementation.
            await DefaultHandler.HandleAsync(requestDelegate, httpContext, authorizationPolicy,
                                   policyAuthorizationResult);*/

            if (!httpContext.User.Identity.IsAuthenticated)
            {
                httpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
                // 设置响应内容类型为 JSON
                httpContext.Response.ContentType = "application/json";
                // 响应内容
                await httpContext.Response.WriteAsync("{\"data\":{\"succeeded\":false,\"code\":401,\"message\":\"登录已过期,请重新登录\"}}");
                return;
            }

            if (!policyAuthorizationResult.Succeeded)
            {
                // 设置响应内容类型为 JSON
                httpContext.Response.ContentType = "application/json";
                // 响应内容
                await httpContext.Response.WriteAsync("{\"data\":{\"succeeded\":false,\"code\":403,\"message\":\"您没有权限操作\"}}");
                return;
            }

            await requestDelegate(httpContext);
        }

        // bool Show404ForForbiddenResult(PolicyAuthorizationResult policyAuthorizationResult)
        // {
        //     return policyAuthorizationResult.Forbidden &&
        //         policyAuthorizationResult.AuthorizationFailure.FailedRequirements.OfType<
        //                                                        Show404Requirement>().Any();
        // }
    }

    // public class Show404Requirement : IAuthorizationRequirement { }
}

注册中间件:

csharp 复制代码
services.AddSingleton<IAuthorizationMiddlewareResultHandler, MyAuthorizationMiddlewareResultHandler>();

运行效果:

参考文档

相关推荐
子兮曰4 天前
jev-ultrafast 深度解析:7 秒订机票的浏览器 Agent 是如何炼成的
前端·后端·agent
子兮曰4 天前
Jev 爆发一周:7 秒 Agent 背后的 System One 生态与三场争议
前端·后端·ai编程
爱勇宝4 天前
ZCode 开源 24 小时:一份没有历史的账本,回答不了"有没有偷代码"
前端·后端·chatglm (智谱)
胡写代码4 天前
别再前后端各写一套表单校验了
java·后端
大勇前进4 天前
原生 PHP 还是 Laravel?小项目到底要不要上框架
后端
yuzhi_liu4 天前
我用 LangGraph4j 实现 Multi-Agent Supervisor
后端
alsmile4 天前
Node-RED 之外,国产规则引擎的新方案:基于标准语法,Go 先行实现
后端·开源·go
大白804 天前
PHP 内存溢出排查思路:看懂报错日志,精准定位问题
后端
二月龙4 天前
PHP 接口返回统一响应封装,让前后端对接更省心
后端
龙亘川4 天前
一网统管AI平台民生业务实践:基于城市数字底座赋能公积金业务服务升级
大数据·安全·智慧城市·开源软件·数据可视化·政务