栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

Asp.Net Core 授权源码分析(上篇)

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Asp.Net Core 授权源码分析(上篇)

Asp.Net Core 授权源码分析
  • 说明
  • 源码分析
    • 1.1 AddAuthorization
    • 1.2 AuthorizationOptions
    • 1.3 IAuthorizationRequirement
    • 1.4 AuthorizationPolicy
    • 1.5 AuthorizationPolicyBuilder
    • 1.6 常见的Requirement
      • 1.6.1 DenyAnonymousAuthorizationRequirement
      • 1.6.2 NameAuthorizationRequirement
      • 1.6.3 ClaimsAuthorizationRequirement
      • 1.6.4 RolesAuthorizationRequirement
    • 1.7 IAuthorizeData
    • 小结

说明

我们通过定义授权策略,查看源码发现,在对授权配置AuthorizationOptions之后,授权系统通过DI的方式注册了几个核心的默认实现。

源码分析 1.1 AddAuthorization

添加授权策略服务使用AddAuthorization方法,以便调用。

从源码可以发现,从core3.0后,由之前在core2.0中的AuthorizationServiceCollectionExtensions.cs文件中,原来的AddAuthorization的方法变为了AddAuthorizationCore方法,微软在这一块进行了封装在PolicyServiceCollectionExtensions.cs文件中,沿用了之前AddAuthorization拓展名称,不影响之前版本的使用。

   public static class PolicyServiceCollectionExtensions
    {
        public static IServiceCollection AddAuthorizationPolicyEvaluator(this IServiceCollection services)
        {
            if (services == null)
            {
                throw new ArgumentNullException(nameof(services));
            }
            services.TryAddSingleton();
            services.TryAddTransient();
            services.TryAddTransient();
            return services;
        }
        public static IServiceCollection AddAuthorization(this IServiceCollection services)
        {
            if (services == null)
            {
                throw new ArgumentNullException(nameof(services));
            }

            services.AddAuthorizationCore();
            services.AddAuthorizationPolicyEvaluator();
            return services;
        }      
        public static IServiceCollection AddAuthorization(this IServiceCollection services, Action configure)
        {
            if (services == null)
            {
                throw new ArgumentNullException(nameof(services));
            }
            services.AddAuthorizationCore(configure);
            services.AddAuthorizationPolicyEvaluator();
            return services;
        }
    }
 public static class AuthorizationServiceCollectionExtensions
    {
        public static IServiceCollection AddAuthorizationCore(this IServiceCollection services)
        {
            if (services == null)
            {
                throw new ArgumentNullException(nameof(services));
            }
            services.TryAdd(ServiceDescriptor.Transient());
            services.TryAdd(ServiceDescriptor.Transient());
            services.TryAdd(ServiceDescriptor.Transient());
            services.TryAdd(ServiceDescriptor.Transient());           		     services.TryAdd(ServiceDescriptor.Transient());
            services.TryAddEnumerable(ServiceDescriptor.Transient());
            return services;
        }
        public static IServiceCollection AddAuthorizationCore(this IServiceCollection services, Action configure)
        {
            if (services == null)
            {
                throw new ArgumentNullException(nameof(services));
            }
            services.Configure(configure);
            return services.AddAuthorizationCore();
        }
    }

由上可知,在调用AddAuthorization方法进行授权配置的时候,需要使用到AuthorizationOptions委托方式传参。

所以我们再来看看下面这一行代码,通过AddPolicy实现添加策略方式。

 options.AddPolicy("customizePermisson",policy => policy.Requirements.Add(new PermissionRequirement("user")));

通过上图可以把Policy(策略)加入到了AuthorizationOptions中的PolicyMap,Policy具体啥时候使用呢,后面会介绍到。

1.2 AuthorizationOptions

授权选项实现添加和授权配置,提供授权服务的配置。

 public class AuthorizationOptions
    {
        private Dictionary PolicyMap { get; } = new Dictionary(StringComparer.OrdinalIgnoreCase);
      
        public bool InvokeHandlersAfterFailure { get; set; } = true;
      
        public AuthorizationPolicy DefaultPolicy { get; set; } = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build();
       
        public AuthorizationPolicy? FallbackPolicy { get; set; }

        public void AddPolicy(string name, AuthorizationPolicy policy)
        {
            if (name == null)
            {
                throw new ArgumentNullException(nameof(name));
            }
            if (policy == null)
            {
                throw new ArgumentNullException(nameof(policy));
            }
            PolicyMap[name] = policy;
        }

        public void AddPolicy(string name, Action configurePolicy)
        {
            if (name == null)
            {
                throw new ArgumentNullException(nameof(name));
            }
            if (configurePolicy == null)
            {
                throw new ArgumentNullException(nameof(configurePolicy));
            }
            var policyBuilder = new AuthorizationPolicyBuilder();
            configurePolicy(policyBuilder);
            PolicyMap[name] = policyBuilder.Build();
        }
      
        public AuthorizationPolicy GetPolicy(string name)
        {
            if (name == null)
            {
                throw new ArgumentNullException(nameof(name));
            }
            if (PolicyMap.TryGetValue(name, out var value))
            {
                return value;
            }
            return null;
        }
    }

它主要是用来保存Policy和设置默认的DefaultPolicy,

常用的2个方法

public void AddPolicy(string name, AuthorizationPolicy policy);
public void AddPolicy(string name, Action configurePolicy);

重点讲解下下面的这个方法

这个方法可以通过一个有参构造来进行构建 AuthorizationPolicyBuilder主要作用就是用来创建AuthorizationPolicy,牵涉到的设计模式包含(建造者设计、单例);

1.3 IAuthorizationRequirement

授权要求

每一个AuthorizationPolicy(策略)可以有多个IAuthorizationRequirement授权要求。

每一个IAuthorizationRequirement对应一个AuthorizationHandler。

可以看到他是一个标记接口,主要是用来定位对应的处理Handler。

   public interface IAuthorizationRequirement
   {
   }
1.4 AuthorizationPolicy

表示授权要求和方案的集合。具体源码如下:

public class AuthorizationPolicy
{
    public AuthorizationPolicy(IEnumerable requirements, IEnumerable authenticationSchemes)
    {
        if (requirements == null)
        {
            throw new ArgumentNullException(nameof(requirements));
        }

        if (authenticationSchemes == null)
        {
            throw new ArgumentNullException(nameof(authenticationSchemes));
        }

        if (requirements.Count() == 0)
        {
            throw new InvalidOperationException(Resources.Exception_AuthorizationPolicyEmpty);
        }
        Requirements = new List(requirements).AsReadOnly();
        AuthenticationSchemes = new List(authenticationSchemes).AsReadOnly();
    }
  
    public IReadOnlyList Requirements { get; }
    
    public IReadOnlyList AuthenticationSchemes { get; }
   
    public static AuthorizationPolicy Combine(params AuthorizationPolicy[] policies)
    {
        if (policies == null)
        {
            throw new ArgumentNullException(nameof(policies));
        }

        return Combine((IEnumerable)policies);
    }
   
    public static AuthorizationPolicy Combine(IEnumerable policies)
    {
        if (policies == null)
        {
            throw new ArgumentNullException(nameof(policies));
        }

        var builder = new AuthorizationPolicyBuilder();
        foreach (var policy in policies)
        {
            builder.Combine(policy);
        }
        return builder.Build();
    }
   
    public static async Task CombineAsync(IAuthorizationPolicyProvider policyProvider, IEnumerable authorizeData)
    {
        if (policyProvider == null)
        {
            throw new ArgumentNullException(nameof(policyProvider));
        }

        if (authorizeData == null)
        {
            throw new ArgumentNullException(nameof(authorizeData));
        }

        // Avoid allocating enumerator if the data is known to be empty
        var skipEnumeratingData = false;
        if (authorizeData is IList dataList)
        {
            skipEnumeratingData = dataList.Count == 0;
        }

        AuthorizationPolicyBuilder policyBuilder = null;
        if (!skipEnumeratingData)
        {
            foreach (var authorizeDatum in authorizeData)
            {
                if (policyBuilder == null)
                {
                    policyBuilder = new AuthorizationPolicyBuilder();
                }

                var useDefaultPolicy = true;
                if (!string.IsNullOrWhiteSpace(authorizeDatum.Policy))
                {
                    var policy = await policyProvider.GetPolicyAsync(authorizeDatum.Policy);
                    if (policy == null)
                    {
                        throw new InvalidOperationException(Resources.FormatException_AuthorizationPolicyNotFound(authorizeDatum.Policy));
                    }
                    policyBuilder.Combine(policy);
                    useDefaultPolicy = false;
                }

                var rolesSplit = authorizeDatum.Roles?.Split(',');
                if (rolesSplit != null && rolesSplit.Any())
                {
                    var trimmedRolesSplit = rolesSplit.Where(r => !string.IsNullOrWhiteSpace(r)).Select(r => r.Trim());
                    policyBuilder.RequireRole(trimmedRolesSplit);
                    useDefaultPolicy = false;
                }

                var authTypesSplit = authorizeDatum.AuthenticationSchemes?.Split(',');
                if (authTypesSplit != null && authTypesSplit.Any())
                {
                    foreach (var authType in authTypesSplit)
                    {
                        if (!string.IsNullOrWhiteSpace(authType))
                        {
                            policyBuilder.AuthenticationSchemes.Add(authType.Trim());
                        }
                    }
                }

                if (useDefaultPolicy)
                {
                    policyBuilder.Combine(await policyProvider.GetDefaultPolicyAsync());
                }
            }
        }

        // If we have no policy by now, use the fallback policy if we have one
        if (policyBuilder == null)
        {
            var fallbackPolicy = await policyProvider.GetFallbackPolicyAsync();
            if (fallbackPolicy != null)
            {
                return fallbackPolicy;
            }
        }

        return policyBuilder?.Build();
    }
}

我们从源码中可以发现

AuthorizationPolicy对象的CombineAsync方法(该方法会在AuthorizationMiddleware中间件中去调用)

  • 它主要完成对控制器或则方法进行IAuthorizeData标记特性中PolicyName属性以及Roles属性值的获取
  • 通过PolicyName从AuthorizationPolicyProvider中获取AuthorizationPolicy对象
  • 通过AuthorizationPolicyBuilder的Combine方法进行合并授权
1.5 AuthorizationPolicyBuilder

策略构建

主要完成对Authorizationpolicy对象的构建。

整个对象就是对IAuthorizationRequirement以及AuthenticationSchemes的集合进行合并成一个。

public class AuthorizationPolicyBuilder
{ 
    public AuthorizationPolicyBuilder(params string[] authenticationSchemes)
    {
        AddAuthenticationSchemes(authenticationSchemes);
    }
    public AuthorizationPolicyBuilder(AuthorizationPolicy policy)
    {
        Combine(policy);
    }
    public IList Requirements { get; set; } = new List();    
    public IList AuthenticationSchemes { get; set; } = new List();
    public AuthorizationPolicyBuilder AddAuthenticationSchemes(params string[] schemes)
    {
        foreach (var authType in schemes)
        {
            AuthenticationSchemes.Add(authType);
        }
        return this;
    }    
    public AuthorizationPolicyBuilder AddRequirements(params IAuthorizationRequirement[] requirements)
    {
        foreach (var req in requirements)
        {
            Requirements.Add(req);
        }
        return this;
    }
    public AuthorizationPolicyBuilder Combine(AuthorizationPolicy policy)
    {
        if (policy == null)
        {
            throw new ArgumentNullException(nameof(policy));
        }

        AddAuthenticationSchemes(policy.AuthenticationSchemes.ToArray());
        AddRequirements(policy.Requirements.ToArray());
        return this;
    }    
    public AuthorizationPolicyBuilder RequireClaim(string claimType, params string[] allowedValues)
    {
        if (claimType == null)
        {
            throw new ArgumentNullException(nameof(claimType));
        }

        return RequireClaim(claimType, (IEnumerable)allowedValues);
    } 
    public AuthorizationPolicyBuilder RequireClaim(string claimType, IEnumerable allowedValues)
    {
        if (claimType == null)
        {
            throw new ArgumentNullException(nameof(claimType));
        }

        Requirements.Add(new ClaimsAuthorizationRequirement(claimType, allowedValues));
        return this;
    }    
    public AuthorizationPolicyBuilder RequireClaim(string claimType)
    {
        if (claimType == null)
        {
            throw new ArgumentNullException(nameof(claimType));
        }

        Requirements.Add(new ClaimsAuthorizationRequirement(claimType, allowedValues: null));
        return this;
    }     
    public AuthorizationPolicyBuilder RequireRole(params string[] roles)
    {
        if (roles == null)
        {
            throw new ArgumentNullException(nameof(roles));
        }

        return RequireRole((IEnumerable)roles);
    }  
    public AuthorizationPolicyBuilder RequireRole(IEnumerable roles)
    {
        if (roles == null)
        {
            throw new ArgumentNullException(nameof(roles));
        }

        Requirements.Add(new RolesAuthorizationRequirement(roles));
        return this;
    }    
    public AuthorizationPolicyBuilder RequireUserName(string userName)
    {
        if (userName == null)
        {
            throw new ArgumentNullException(nameof(userName));
        }

        Requirements.Add(new NameAuthorizationRequirement(userName));
        return this;
    }
    public AuthorizationPolicyBuilder RequireAuthenticatedUser()
    {
        Requirements.Add(new DenyAnonymousAuthorizationRequirement());
        return this;
    }
    public AuthorizationPolicyBuilder RequireAssertion(Func handler)
    {
        if (handler == null)
        {
            throw new ArgumentNullException(nameof(handler));
        }

        Requirements.Add(new AssertionRequirement(handler));
        return this;
    }
    public AuthorizationPolicyBuilder RequireAssertion(Func> handler)
    {
        if (handler == null)
        {
            throw new ArgumentNullException(nameof(handler));
        }

        Requirements.Add(new AssertionRequirement(handler));
        return this;
    } 
    public AuthorizationPolicy Build()
    {
        return new AuthorizationPolicy(Requirements, AuthenticationSchemes.Distinct());
    }
}
1.6 常见的Requirement 1.6.1 DenyAnonymousAuthorizationRequirement

阻止匿名用户操作,没有通过认证的用户是不允许访问资源的

public class DenyAnonymousAuthorizationRequirement : AuthorizationHandler, IAuthorizationRequirement
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, DenyAnonymousAuthorizationRequirement requirement)
    {
        var user = context.User;
        var userIsAnonymous =
            user?.Identity == null ||
            !user.Identities.Any(i => i.IsAuthenticated);
        if (!userIsAnonymous)
        {
            context.Succeed(requirement);
        }
        return Task.CompletedTask;
    }
}

就是检查用户的Identities是否存在一个通过认证的。

1.6.2 NameAuthorizationRequirement

顾名思义指定名称来判断是否匹配授权访问资源。

public class NameAuthorizationRequirement : AuthorizationHandler, IAuthorizationRequirement
{
    public NameAuthorizationRequirement(string requiredName)
    {
        if (requiredName == null)
        {
            throw new ArgumentNullException(nameof(requiredName));
        }

        RequiredName = requiredName;
    }
    public string RequiredName { get; }
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, NameAuthorizationRequirement requirement)
    {
        if (context.User != null)
        {
            if (context.User.Identities.Any(i => string.Equals(i.Name, requirement.RequiredName)))
            {
                context.Succeed(requirement);
            }
        }
        return Task.CompletedTask;
    }
}
1.6.3 ClaimsAuthorizationRequirement

基于Claim声明类型的授权策略,检查当前用户是否声明ClaimType以及ClaimValue。

public class ClaimsAuthorizationRequirement : AuthorizationHandler, IAuthorizationRequirement
    {
        public ClaimsAuthorizationRequirement(string claimType, IEnumerable allowedValues)
        {
            if (claimType == null)
            {
                throw new ArgumentNullException(nameof(claimType));
            }
            ClaimType = claimType;
            AllowedValues = allowedValues;
        }
        public string ClaimType { get; }
        public IEnumerable AllowedValues { get; }
        protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, ClaimsAuthorizationRequirement requirement)
        {
            if (context.User != null)
            {
                var found = false;
                if (requirement.AllowedValues == null || !requirement.AllowedValues.Any())
                {
                    found = context.User.Claims.Any(c => string.Equals(c.Type, requirement.ClaimType, StringComparison.OrdinalIgnoreCase));
                }
                else
                {
                    found = context.User.Claims.Any(c => string.Equals(c.Type, requirement.ClaimType, StringComparison.OrdinalIgnoreCase)
                                                        && requirement.AllowedValues.Contains(c.Value, StringComparer.Ordinal));
                }
                if (found)
                {
                    context.Succeed(requirement);
                }
            }
            return Task.CompletedTask;
        }
    }
1.6.4 RolesAuthorizationRequirement

基于角色的授权策略,不建议使用,角色不能动态添加

public class RolesAuthorizationRequirement : AuthorizationHandler, IAuthorizationRequirement
{
    public RolesAuthorizationRequirement(IEnumerable allowedRoles)
    {
        if (allowedRoles == null)
        {
            throw new ArgumentNullException(nameof(allowedRoles));
        }

        if (allowedRoles.Count() == 0)
        {
            throw new InvalidOperationException(Resources.Exception_RoleRequirementEmpty);
        }
        AllowedRoles = allowedRoles;
    }
    public IEnumerable AllowedRoles { get; }
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, RolesAuthorizationRequirement requirement)
    {
        if (context.User != null)
        {
            bool found = false;
            if (requirement.AllowedRoles == null || !requirement.AllowedRoles.Any())
            {
                // Review: What do we want to do here?  No roles requested is auto success?
            }
            else
            {
                found = requirement.AllowedRoles.Any(r => context.User.IsInRole(r));
            }
            if (found)
            {
                context.Succeed(requirement);
            }
        }
        return Task.CompletedTask;
    }
}
1.7 IAuthorizeData

授权特性标记

从上述可以看到上面有3种方式

当时这几种不满足我们企业开发的需求,后续会提供另外的一种方式。

  • AuthenticationSchemes 获取或设置一个逗号分隔的方案列表,从中构造用户信息。

  • Policy 获取或设置确定对资源的访问的策略名称。

  • Roles 获取或设置允许访问资源的角色的逗号分隔列表。

小结

当你想用某个或则自定义的IRequirement的时候。

需要注入 services.AddSingleton()。

由于内容篇幅太长,下一篇会讲解主要授权的代码以及流程。

最后也会放上企业级授权的方案。

另外官方地址给你们放上去 参考文档。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1012389.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号