文章摘要
这篇文章介绍了Microsoft.AspNetCore Authorization middleware的实现,展示了如何自定义一个用于控制资源访问权限的middleware。核心内容包括: 1. **类的结构**:该middleware继承了Microsoft.AspNetCore's Authorization middleware,并引入了自定义的`IPolicyProvider`和`IAuthorizeData`接口。 2. **核心功能**: - **获取并合并作者信息**:通过`IPolicyProvider`和`IAuthorizeData`获取和合并用户所需的所有角色权限。 - **认证和授权检查**:使用`IPolicyEvaluator.AuthenticateAsync()`和`AuthorizeAsync()`方法对访问者进行认证,并根据资源需求进行授权。 - **挑战和禁止访问**:在授权失败时,通过`context.ChallengeAsync()`或`context.ForbidAsync()`方法处理未登录或无权限访问的情况。 3. **关键逻辑**: - 如果用户身份认证通过但无权限访问,则返回401未认证状态。 - 如果用户被禁止访问资源,则返回403禁止访问状态。 - 中ware通过整合作者信息和认证逻辑,实现了对资源访问权限的精细控制。 这篇文章展示了如何通过自定义middleware实现对资源访问权限的精细控制,适合需要复杂认证和授权场景的应用。
// Microsoft.AspNetCore.Authorization.AuthorizationMiddleware
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authorization.Policy;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
public class AuthorizationMiddleware
{
private const string AuthorizationMiddlewareInvokedWithEndpointKey=”__AuthorizationMiddlewareWithEndpointInvoked”;
private static readonly object AuthorizationMiddlewareWithEndpointInvokedValue=new object();
private readonly RequestDelegate _next;
private readonly IAuthorizationPolicyProvider _policyProvider;
public AuthorizationMiddleware(RequestDelegate next, IAuthorizationPolicyProvider policyProvider)
{
_next=next throw new ArgumentNullException(“next”);
_policyProvider=policyProvider throw new ArgumentNullException(“policyProvider”);
}
public async Task Invoke(HttpContext context)
{
if (context==null)
{
throw new ArgumentNullException(“context”);
}
Endpoint endpoint=context.GetEndpoint();
if (endpoint !=null)
{
context.Items[“__AuthorizationMiddlewareWithEndpointInvoked”]=AuthorizationMiddlewareWithEndpointInvokedValue;
}
//获取访问当前资源所需要的所有角色权限,及授权策略,以及访问该资源时需要使用的认证方案列表,并统一合并到一个AuthorizationPolicy对象中。
IReadOnlyList<IAuthorizeData> authorizeData=endpoint?.Metadata.GetOrderedMetadata<IAuthorizeData>() Array.Empty<IAuthorizeData>();
AuthorizationPolicy policy=await AuthorizationPolicy.CombineAsync(_policyProvider, authorizeData);
if (policy==null)
{
await _next(context);
return;
}
IPolicyEvaluator policyEvaluator=context.RequestServices.GetRequiredService<IPolicyEvaluator>();
//通过IPolicyEvaluator.AuthenticateAsync()方法,对当前访问者进行认证,至于使用哪种方案认证,根据该资源要求使用的认证方案来,如果没有指定,
//则使用默认认证方案进行认证。
AuthenticateResult authenticationResult=await policyEvaluator.AuthenticateAsync(policy, context);
//如果包含实现了IAllowAnonymous接口的特性,则不进行授权检查。
if (endpoint?.Metadata.GetMetadata<IAllowAnonymous>() !=null)
{
await _next(context);
return;
}
//这里调用AuthorizeAsync进行授权检查,注意,这里将上一步认证结果authenticationResult也传到了授权检查方法内部。
PolicyAuthorizationResult policyAuthorizationResult=await policyEvaluator.AuthorizeAsync(policy, authenticationResult, context, endpoint);
//检查授权结果,如果是未登录,则返回401未认证,让用户进行登录,如果该资源指定了特定的认证方案,则调用特定认证方案的Challenge方法,
//否则调用默认认证方案的Challenge方法,通常Challenge做的事情就是重定向用户的浏览器到登录页面或者对于ajax异步请求返回401.
if (policyAuthorizationResult.Challenged)
{
if (policy.AuthenticationSchemes.Any())
{
foreach (string authenticationScheme in policy.AuthenticationSchemes)
{
await context.ChallengeAsync(authenticationScheme);
}
}
else
{
await context.ChallengeAsync();
}
}
//如果当前访问者用户身份认证通过,但是不被允许访问该资源的权限,那么默认返回401(禁止访问)给浏览器端,通常对于未授权的访问请求,应用常常的做法是将用户的浏览器重定向到禁止访问的提示页面,或者对于ajax异步请求来说,通常返回403状态码,和上面未认证情况一样,如果该资源指定了特定的认证方案,那么会调用特定认证方案的Forbid方法,否则调用默认认证方案的Forbid方法。
else if (policyAuthorizationResult.Forbidden)
{
if (policy.AuthenticationSchemes.Any())
{
foreach (string authenticationScheme2 in policy.AuthenticationSchemes)
{
await context.ForbidAsync(authenticationScheme2);
}
}
else
{
await context.ForbidAsync();
}
}
else
{
await _next(context);
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authorization.Policy;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
public class AuthorizationMiddleware
{
private const string AuthorizationMiddlewareInvokedWithEndpointKey=”__AuthorizationMiddlewareWithEndpointInvoked”;
private static readonly object AuthorizationMiddlewareWithEndpointInvokedValue=new object();
private readonly RequestDelegate _next;
private readonly IAuthorizationPolicyProvider _policyProvider;
public AuthorizationMiddleware(RequestDelegate next, IAuthorizationPolicyProvider policyProvider)
{
_next=next throw new ArgumentNullException(“next”);
_policyProvider=policyProvider throw new ArgumentNullException(“policyProvider”);
}
public async Task Invoke(HttpContext context)
{
if (context==null)
{
throw new ArgumentNullException(“context”);
}
Endpoint endpoint=context.GetEndpoint();
if (endpoint !=null)
{
context.Items[“__AuthorizationMiddlewareWithEndpointInvoked”]=AuthorizationMiddlewareWithEndpointInvokedValue;
}
//获取访问当前资源所需要的所有角色权限,及授权策略,以及访问该资源时需要使用的认证方案列表,并统一合并到一个AuthorizationPolicy对象中。
IReadOnlyList<IAuthorizeData> authorizeData=endpoint?.Metadata.GetOrderedMetadata<IAuthorizeData>() Array.Empty<IAuthorizeData>();
AuthorizationPolicy policy=await AuthorizationPolicy.CombineAsync(_policyProvider, authorizeData);
if (policy==null)
{
await _next(context);
return;
}
IPolicyEvaluator policyEvaluator=context.RequestServices.GetRequiredService<IPolicyEvaluator>();
//通过IPolicyEvaluator.AuthenticateAsync()方法,对当前访问者进行认证,至于使用哪种方案认证,根据该资源要求使用的认证方案来,如果没有指定,
//则使用默认认证方案进行认证。
AuthenticateResult authenticationResult=await policyEvaluator.AuthenticateAsync(policy, context);
//如果包含实现了IAllowAnonymous接口的特性,则不进行授权检查。
if (endpoint?.Metadata.GetMetadata<IAllowAnonymous>() !=null)
{
await _next(context);
return;
}
//这里调用AuthorizeAsync进行授权检查,注意,这里将上一步认证结果authenticationResult也传到了授权检查方法内部。
PolicyAuthorizationResult policyAuthorizationResult=await policyEvaluator.AuthorizeAsync(policy, authenticationResult, context, endpoint);
//检查授权结果,如果是未登录,则返回401未认证,让用户进行登录,如果该资源指定了特定的认证方案,则调用特定认证方案的Challenge方法,
//否则调用默认认证方案的Challenge方法,通常Challenge做的事情就是重定向用户的浏览器到登录页面或者对于ajax异步请求返回401.
if (policyAuthorizationResult.Challenged)
{
if (policy.AuthenticationSchemes.Any())
{
foreach (string authenticationScheme in policy.AuthenticationSchemes)
{
await context.ChallengeAsync(authenticationScheme);
}
}
else
{
await context.ChallengeAsync();
}
}
//如果当前访问者用户身份认证通过,但是不被允许访问该资源的权限,那么默认返回401(禁止访问)给浏览器端,通常对于未授权的访问请求,应用常常的做法是将用户的浏览器重定向到禁止访问的提示页面,或者对于ajax异步请求来说,通常返回403状态码,和上面未认证情况一样,如果该资源指定了特定的认证方案,那么会调用特定认证方案的Forbid方法,否则调用默认认证方案的Forbid方法。
else if (policyAuthorizationResult.Forbidden)
{
if (policy.AuthenticationSchemes.Any())
{
foreach (string authenticationScheme2 in policy.AuthenticationSchemes)
{
await context.ForbidAsync(authenticationScheme2);
}
}
else
{
await context.ForbidAsync();
}
}
else
{
await _next(context);
}
}
}
© 版权声明
文章版权归作者所有,未经允许请勿转载。