一、认证与授权的区别
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、创建项目
dotnet new webapi -controllers -f net8.0
2、安装 Nuget 包
# Microsoft.AspNetCore.Authentication.JwtBearer
dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer -v 8.0.7
3、生成秘钥
我们需要一个密钥用于加密和验证 JWT,可以使用使用 RandomNumberGenerator
类生成随机字节。
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);
}
}
Generated SecretKey: pEZADfw6uJe8MOAozDoaAuXC+/ATW4XysMtQwH7wFmw=
4、配置信息
在 appsettings.json
中新增节点信息:
"JwtConfig": {
"SecretKey": "pEZADfw6uJe8MOAozDoaAuXC+/ATW4XysMtQwH7wFmw=",
"Audience": "test",
"Issuer": "test",
"Expired": 5
}
5、JwtConfig
新增 JwtConfig
对象类,用于读取配置文件信息:
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:
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 服务:
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、调用中间件
// Program.cs
// ...
// JWT 认证服务
builder.Services.AddJwtService(builder.Configuration);
// ...
// 先认证
app.UseAuthentication();
// 再授权
app.UseAuthorization();
9、AccountController
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
属性调用:
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
(方案):
[Authorize(Roles = "", Policy = "", AuthenticationSchemes = "")]
2、Roles 角色授权
基于角色授权,用户拥有这个角色,就能通过授权验证。在认证时给用户添加相应的 Claim
,即可表示该用户拥有该角色,一个用户可以拥有多个角色。
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
中配置角色授权,
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
匹配等。
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
即可生效:
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
声明:
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
方法对应修改:
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
声明的值在策略范围内,访问接口正常。
[Authorize(Policy = "policy2", Roles = "user")]
采用角色+策略混合授权方式,也是能够正常访问接口。
4、基于策略 Policy 的自定义授权
在配置中调用 AddPolicy
来注册策略,在某些情况下,可能无法采用此方式注册所有策略,在这些情况下,可以使用自定义 IAuthorizationPolicyProvider
来控制如何提供授权策略,比如在运行时使用外部数据源中的信息策略确定授权要求。授权的主要方式,是通过 Handler
程序判断授权,需要实现 IAuthorizationHandler
接口。
实现步骤如下:
- 自定义一个
Requirement
实现IAuthorizationRequirement
- 自定义
Handler
程序继承AuthorizationHandler<TRequirement>
并重写HandleRequirementAsync
方法。 - 实现策略提供的
PolicyProvider
来动态添加策略。
Step1、实现 AuthorizeAttribute
新增属性,继承 AuthorizeAttribute
为我们添加额外属性来增加自定义授权,属性 Policy
不可为 null
。
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
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
代码如下:
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
的项取出,检测是否有执行权限。
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 中配置所需权限
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
中:
services.AddSingleton<IAuthorizationPolicyProvider, MyPermissionPolicyProvider>();
services.AddSingleton<IAuthorizationHandler, MyPermissionAuthorizationHandler>();
运行效果:
可以看到,使用账号 123456
访问自定义权限的接口 test6
和 test7
都有权限,同时也能正常访问没有自定义权限的接口。
若此时新增接口 test8
,使用自定义权限 test.edit
,则不在指定的权限范围内,无法访问。
5、自定义 AuthorizationMiddleware
自定义 IAuthorizationMiddlewareResultHandler
处理授权结果,用于:
- 返回自定义的响应
- 增强默认质询或禁止响应。
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 { }
}
注册中间件:
services.AddSingleton<IAuthorizationMiddlewareResultHandler, MyAuthorizationMiddlewareResultHandler>();
运行效果: