gpt4 book ai didi

【ASP.NETCore】用配置文件来设置授权角色

转载 作者:我是一只小鸟 更新时间:2023-01-23 22:31:19 26 4
gpt4 key购买 nike

在开始之前,老周先祝各个次元的伙伴们新春快乐、生活愉快、万事如意.

在上一篇水文中,老周介绍了角色授权的一些内容。本篇咱们来聊一个比较实际的问题——把用于授权的角色名称放到外部配置,不要硬编码,以方便后期修改.

由于要配置的东西比较简单,咱们并不需要存在数据库,而是用 JSON 文件配置就可以了。将授权策略和角色列表关联起来。比如,老周这里有个 authorRoles.json 文件,它的内容如下:

                          
                            {
  
                          
                          "cust1"
                          
                            : {
    
                          
                          "roles": ["admin", "supperuser"
                          
                            ]
  },
  
                          
                          "cust2"
                          
                            : {
    
                          
                          "roles": ["user", "web", "logger"
                          
                            ]
  }
}
                          
                        

其中,cust1、cust2 是策略名称,所以上面就配置了两个授权策略。每个策略下有个 roles 属性,它的值是数组,这个数组用来指定此策略下允许的角色列表。故:cust1 策略下允许admin、supperuser两种角色的用户访问;cust2 策略下允许 user、web、logger 角色的用户访问.

在 WebApplicationBuilder 的配置中,咱们可以单独加载 authorRoles.json 文件中的内容,然后根据配置文件内容动态添加授权策略.

1、先把配置文件中的内容读出来.

                          
                            //
                          
                          
                             配置文件名
                          
                          
                            const
                          
                          
                            string
                          
                           roleConfigFile = 
                          
                            "
                          
                          
                            authorRoles.json
                          
                          
                            "
                          
                          
                            ;

                          
                          
                            //
                          
                          
                             单独加载配置
                          
                          
IConfigurationBuilder configBuilder = 
                          
                            new
                          
                          
                             ConfigurationBuilder();

                          
                          
                            //
                          
                          
                             添加配置源,此处是JSON文件
                          
                          
                            configBuilder.AddJsonFile(roleConfigFile);

                          
                          
                            //
                          
                          
                             生成配置树对象
                          
                          
IConfiguration myconfig = configBuilder.Build();
                        

此时,myconfig 变量中就包含了 authorRoles.json 文件的内容了.

2、动态添加授权策略.

                          
                            var
                          
                           builder =
                          
                             WebApplication.CreateBuilder(args);


                          
                          
                            //
                          
                          
                             根据配置文件的内容来设置授权策略
                          
                          
builder.Services.AddAuthorization(opt =>
                          
                            
{
    
                          
                          
                            foreach
                          
                           (IConfigurationSection cc 
                          
                            in
                          
                          
                             myconfig.GetChildren())
    {
        
                          
                          
                            var
                          
                           policyName =
                          
                             cc.Key;
        opt.AddPolicy(policyName, pbd 
                          
                          =>
                          
                            
        {
            
                          
                          
                            //
                          
                          
                             获取子节点
                          
                          
                            var
                          
                           roles = cc.GetSection(
                          
                            "
                          
                          
                            roles
                          
                          
                            "
                          
                          
                            );
            
                          
                          
                            //
                          
                          
                             取出角色名称列表
                          
                          
                            string
                          
                          []? roleslist = roles.Get<
                          
                            string
                          
                          []>
                          
                            ();
            
                          
                          
                            if
                          
                           (roleslist 
                          
                            is
                          
                           not 
                          
                            null
                          
                          
                            )
            {
                
                          
                          
                            //
                          
                          
                             添加角色
                          
                          
                                            pbd.RequireRole(roleslist);
                
                          
                          
                            //
                          
                          
                             关联验证架构
                          
                          
                                            pbd.AddAuthenticationSchemes(CustAuthenticationSchemeDefault.SchemeName);
            }
        });
    }
});
                          
                        

在读配置的时候,GetChildren 方法会返回两个节点:cust1 和 cust2。然后用 GetSection 再读下一层,即 roles。接着用 Get 方法就能把字符串数组类型的角色列表读出来了.

这里关联了一个验证架构(或叫验证方案),这个验证架构是老周自己写的,主要是为了简单。老周这个示例是用 Web API 的形式呈现的,所以,不用 Cookie,而是用一个简单的 Token,调用时附加在 URL 的查询字符串中传递给服务器.

如果你的项目的 Token 只是在自己项目中用,不用遵守通用标准,你完全可以自己生成。生成方式你看着办,比如用随机字节什么的都行。在 Token 中不要带密码等安全信息。毕竟,Token 这种东西你说安全,也不见得多安全,别人只要拿到你的 Token 就可以代替你访问服务器。当然你会说,我把 Token 加密再传输。其实别人盗你的 Token 根本不需要知道明文,人家只要按照正确的传递方式(如 Query String、Cookies 等),把你加密后的 Token 放上去,也可以冒用你身份的。所以,很多开放平台都会分配给你 App Key 和密钥,并且强调你的密钥必须保管好,不能让别人知道.

下面看看老周自己写的验证.

                          
                            using
                          
                          
                             Microsoft.AspNetCore.Authentication;
    
                          
                          
                            using
                          
                          
                             Microsoft.AspNetCore.Http;
    
                          
                          
                            using
                          
                          
                             System.Threading.Tasks;

    
                          
                          
                            public
                          
                          
                            class
                          
                          
                             CustAuthenticationHandler : 
                             IAuthenticationHandler 
                            
    {

                          
                          
                            #pragma
                          
                           warning disable CS8618
        
                          
                            private
                          
                           HttpContext HttpContext { 
                          
                            set
                          
                          ; 
                          
                            get
                          
                          
                            ; }
        
                          
                          
                            private
                          
                           AuthenticationScheme Scheme { 
                          
                            get
                          
                          ; 
                          
                            set
                          
                          
                            ; }

                          
                          
                            #pragma
                          
                           warning restore CS8618

        
                          
                            public
                          
                           Task<AuthenticateResult>
                          
                             AuthenticateAsync()
        {
            
                          
                          
                            //
                          
                          
                             获取配置的Token
                          
                          
            IConfiguration appconfig = HttpContext.RequestServices.GetRequiredService<IConfiguration>
                          
                            ();
            
                          
                          
                             string 
                            []? tks = appconfig.GetSection(
                             " 
                             custAuthen:tokens 
                             " 
                            ).Get<
                             string 
                            []>
                             (); 
                          
                          
                            if
                          
                           (tks != 
                          
                            null
                          
                           && tks.Length > 
                          
                            0
                          
                           && HttpContext.Request.Query.TryGetValue(
                          
                            "
                          
                          
                            token
                          
                          
                            "
                          
                          , 
                          
                            out
                          
                          
                            var
                          
                          
                             reqToken))
            {
                
                          
                          
                            //
                          
                          
                             看看有没有效
                          
                          
                            if
                          
                           (!tks.Any(t => t ==
                          
                             reqToken))
                {
                    
                          
                          
                            return
                          
                           Task.FromResult(AuthenticateResult.Fail(
                          
                            "
                          
                          
                            未提供有效的Token
                          
                          
                            "
                          
                          
                            ));
                }
                
                          
                          
                            //
                          
                          
                             成功
                          
                          
                            var
                          
                           tickit = 
                          
                            new
                          
                          
                             AuthenticationTicket(HttpContext.User, Scheme.Name);
                
                          
                          
                            return
                          
                          
                             Task.FromResult(AuthenticateResult.Success(tickit));
            }
            
                          
                          
                            return
                          
                          
                             Task.FromResult(AuthenticateResult.NoResult());
        }

        
                          
                          
                            public
                          
                           Task ChallengeAsync(AuthenticationProperties?
                          
                             properties)
        {
            HttpContext.Response.StatusCode 
                          
                          =
                          
                             StatusCodes.Status401Unauthorized;
            
                          
                          
                            return
                          
                          
                             Task.CompletedTask;
        }

        
                          
                          
                            public
                          
                           Task ForbidAsync(AuthenticationProperties?
                          
                             properties)
        {
            HttpContext.Response.StatusCode 
                          
                          =
                          
                             StatusCodes.Status403Forbidden;
            
                          
                          
                            return
                          
                          
                             Task.CompletedTask;
        }

        
                          
                          
                            public
                          
                          
                             Task InitializeAsync(AuthenticationScheme scheme, HttpContext context)
        {
            
                          
                          
                            if
                          
                           (context == 
                          
                            null
                          
                          ) 
                          
                            throw
                          
                          
                            new
                          
                           ArgumentNullException(
                          
                            "
                          
                          
                            context
                          
                          
                            "
                          
                          
                            );
            HttpContext 
                          
                          =
                          
                             context;
            Scheme 
                          
                          =
                          
                             scheme;
            
                          
                          
                            //
                          
                          
                             看看验证架构是否一致
                          
                          
                            if
                          
                           (!
                          
                            scheme.Name.Equals(CustAuthenticationSchemeDefault.SchemeName, StringComparison.OrdinalIgnoreCase))
            {
                
                          
                          
                            throw
                          
                          
                            new
                          
                           Exception(
                          
                            "
                          
                          
                            验证架构不一致
                          
                          
                            "
                          
                          
                            );
            }
            
                          
                          
                            return
                          
                          
                             Task.CompletedTask;
        }
    }

    
                          
                          
                            public
                          
                          
                            static
                          
                          
                            class
                          
                          
                             CustAuthenticationSchemeDefault
    {
        
                          
                          
                            public
                          
                          
                            readonly
                          
                          
                            static
                          
                          
                            string
                          
                           SchemeName = 
                          
                            "
                          
                          
                            CustToken
                          
                          
                            "
                          
                          
                            ;
    }
                          
                        

这里老周没有用什么高级算法生成 Token,而是四个字符串(字符串也是随便输入的),表示四个 Token,只要有一个匹配就算是验证成功了。这些 Token 全写在 appsettings.json 里面.

                          
                            {
  
                          
                          "Logging"
                          
                            : {
    ……
    }
  },
  
                          
                          "AllowedHosts": "*"
                          
                            ,
  
                          
                          
                            "custAuthen"
                             : { 
                            "tokens"
                             : [ 
                            "662CV08Y4GHXOP3"
                             , 
                            "BI4C68DLO2HOS0D"
                             , 
                            "7GSEJ0J8F0246K5"
                             , 
                            "O9FG6V974KWO9G8"
                          
                          
                             ] } 
                            
}
                          
                        

所以,访问这四个 Token 的配置路径就是 custAuthen:tokens.

在实现 ForbidAsync 和 ChallengeAsync 方法时,不要调用 HttpContext 的扩展方法 ForbidAsync、ChallengeAsync,因为这些扩展方法内部是通过调用 AuthenticationService 类的 ForbidAsync、ChallengeAsync 方法实现的。最终又会回过头来调用 CustAuthenticationHandler 类的  ChallengeAsync、ForbidAsync 方法。这等于转了一圈,到头来自己调用自己,易造成无限递归。所以这里我只设置一个 Status Code 就好了.

在服务容器上注册一下自定义的验证处理方案.

                          
                            var
                          
                           builder =
                          
                             WebApplication.CreateBuilder(args);

                          
                          
                            //
                          
                          
                             添加验证功能
                          
                          
builder.Services.AddAuthentication(opt =>
                          
                            
{
    
                          
                          
                            //
                          
                          
                             注册验证架构(方案)
                          
                          
                            opt.AddScheme<CustAuthenticationHandler>(CustAuthenticationSchemeDefault.SchemeName, displayName: 
                             null 
                          
                          
                             ) 
                            ;
});
                          
                        

所以,整个应用程序的初始化代码就是这样.

                          
                            //
                          
                          
                             配置文件名
                          
                          
                            const
                          
                          
                            string
                          
                           roleConfigFile = 
                          
                            "
                          
                          
                            authorRoles.json
                          
                          
                            "
                          
                          
                            ;

                          
                          
                            //
                          
                          
                             单独加载配置
                          
                          
IConfigurationBuilder configBuilder = 
                          
                            new
                          
                          
                             ConfigurationBuilder();

                          
                          
                            //
                          
                          
                             添加配置源,此处是JSON文件
                          
                          
                            configBuilder.AddJsonFile(roleConfigFile);

                          
                          
                            //
                          
                          
                             生成配置树对象
                          
                          
IConfiguration myconfig =
                          
                             configBuilder.Build();


                          
                          
                            var
                          
                           builder =
                          
                             WebApplication.CreateBuilder(args);

                          
                          
                            //
                          
                          
                             添加验证功能
                          
                          
builder.Services.AddAuthentication(opt =>
                          
                            
{
    
                          
                          
                            //
                          
                          
                             注册验证架构(方案)
                          
                          
    opt.AddScheme<CustAuthenticationHandler>(CustAuthenticationSchemeDefault.SchemeName, displayName: 
                          
                            null
                          
                          
                            );
});

                          
                          
                            //
                          
                          
                             根据配置文件的内容来设置授权策略
                          
                          
builder.Services.AddAuthorization(opt =>
                          
                            
{
    
                          
                          
                            foreach
                          
                           (IConfigurationSection cc 
                          
                            in
                          
                          
                             myconfig.GetChildren())
    {
        
                          
                          
                            var
                          
                           policyName =
                          
                             cc.Key;
        opt.AddPolicy(policyName, pbd 
                          
                          =>
                          
                            
        {
            
                          
                          
                            //
                          
                          
                             获取子节点
                          
                          
                            var
                          
                           roles = cc.GetSection(
                          
                            "
                          
                          
                            roles
                          
                          
                            "
                          
                          
                            );
            
                          
                          
                            //
                          
                          
                             取出角色名称列表
                          
                          
                            string
                          
                          []? roleslist = roles.Get<
                          
                            string
                          
                          []>
                          
                            ();
            
                          
                          
                            if
                          
                           (roleslist 
                          
                            is
                          
                           not 
                          
                            null
                          
                          
                            )
            {
                
                          
                          
                            //
                          
                          
                             添加角色
                          
                          
                                            pbd.RequireRole(roleslist);
                
                          
                          
                            //
                          
                          
                             关联验证架构
                          
                          
                                            pbd.AddAuthenticationSchemes(CustAuthenticationSchemeDefault.SchemeName);
            }
        });
    }
});
builder.Services.AddControllers();

                          
                          
                            var
                          
                           app = builder.Build();
                        

  。

之后,是配置中间件管道。为了简单演示,老周没有写用于身份验证的 Web API,而是直接通过 URL 参数来提供当前访问者的角色。实际开发中不能这样做,而应该从数据库中根据用户查询出用户的角色。但此处是为了演示的简单,也是为了延长键盘寿命,就不建数据库了,不然完成这个示例需要一坤年的时间.

不过,咱们知道,授权是用 Claim 来收集信息的,所以,要在授权执行之前收集好信息。我这里用一个中间件,在授权和调用 API 之前执行.

                          app.Use((context, next) =>
                          
                            
{
    
                          
                          
                             var 
                             val = context.Request.Query[
                             " 
                             role 
                             " 
                             ]; 
                          
                          
                            string
                          
                          ? role =
                          
                             val.FirstOrDefault();
    
                          
                          
                            if
                          
                          (role != 
                          
                            null
                          
                          
                            )
    {
        ClaimsIdentity id 
                          
                          = 
                          
                            new
                          
                          (
                          
                            new
                          
                          
                            []
        {
            
                          
                          
                            new
                          
                          
                             Claim(ClaimTypes.Role, role) 
                            
        }
                          
                          
                            /*
                          
                          
                            , CustAuthenticationSchemeDefault.SchemeName
                          
                          
                            */
                          
                          
                            );
        ClaimsPrincipal p 
                          
                          = 
                          
                            new
                          
                          
                            (id);
        context.User 
                          
                          =
                          
                             p;
    }
    
                          
                          
                            return
                          
                          
                             next();
});
                          
                        

由于 WebApplication 对象默认帮我们调用了 UseRouting 和 UseEndpoints 方法。Web API 在访问时路由的是 MVC 控制器,直接走 End point 路线,会导致咱们上面的 Use 方法设置用户角色的中间件不执行。所以要重新调用 UseRouting 和 UseAuthorization 方法.

                          
                            app.UseRouting();
app.UseAuthorization();
app.MapControllers();
                          
                        

  。

用一个名为 Demo 的控制器来做验证.

                          [Route(
                          
                            "
                          
                          
                            api/[controller]
                          
                          
                            "
                          
                          
                            )]
[ApiController]

                          
                          
                            public
                          
                          
                            class
                          
                          
                             DemoController : ControllerBase
{
    [HttpGet(
                          
                          
                            "
                          
                          
                            backup
                          
                          
                            "
                          
                          
                            )]
    [
                             Authorize( 
                          
                          
                             " 
                             cust1 
                             " 
                          
                          
                             ) 
                            ]
    
                          
                          
                            public
                          
                          
                            string
                          
                           Backup() => 
                          
                            "
                          
                          
                            备份完成
                          
                          
                            "
                          
                          
                            ;

    [HttpGet(
                          
                          
                            "
                          
                          
                            hello/{name}
                          
                          
                            "
                          
                          
                            )]
    [
                             Authorize( 
                          
                          
                             " 
                             cust2 
                             " 
                          
                          
                             ) 
                            ]
    
                          
                          
                            public
                          
                          
                            string
                          
                           Hello(
                          
                            string
                          
                          
                             name)
    {
        
                          
                          
                            return
                          
                           $
                          
                            "
                          
                          
                            你好,{name}
                          
                          
                            "
                          
                          
                            ;
    }
}
                          
                        

cust1、cust2 正是咱们前面配置里的节点名称,即策略名称。例如,调用 Hello 方法使用 cust2 授权策略,它配置的角色为 user、web、loggor.

  。

在调用这些 API 时,URL需要携带两个参数:

1、role:用户角色; 。

2、token:用于验证.

用 http-repl 工具先测试 demo/backup 方法的调用.

                           get /api/demo/backup?
                          
                            role=web
                          
                          &token=O9FG6V974KWO9G8
                        

上述调用提供的用户角色为 web,根据前面的配置,web 角色应使用 cust2 策略。但 Backup 方法应用的授权策略是 cust1,因此无权访问,返回 403.

咱们改一下,使用角色为 admin 的用户.

                          get /api/demo/backup?
                          
                            role=admin
                          
                          &token=O9FG6V974KWO9G8
                        

此时,授权通过,返回 200.

  。

  。

访问 Hello 方法也一样,授权策略是 cust2,允许的角色是 user、web、logger.

                          get /api/demo/hello/小红?
                          
                            role=web
                          
                          &token=BI4C68DLO2HOS0D
                        

授权通过,返回 200 状态码.

  。

  。

用配置文件来设置角色,算是一种简单方案。如果授权需要的角色有变化,只要修改配置文件中的角列表就行。当然,像 cust1、cust2 等策略名称要事先规划好,策略名称不随便改.

有大伙伴会说,干脆连MVC控制器或其方法上应用哪个授权策略也转到配置文件中,岂不美哉!好是好,但不好弄。可以要自己写个授权的 Filter,主要问题是自己写有时候没有官方内置的代码严谨,容易出“八阿哥”.

所以,综合复杂性与灵活性的平衡,在不扩展现有接口的前提下,咱们这个示例是比较好的,至少,咱们可以在配置文件中修改角色列表.

最后此篇关于【ASP.NETCore】用配置文件来设置授权角色的文章就讲到这里了,如果你想了解更多关于【ASP.NETCore】用配置文件来设置授权角色的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com