欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

asp.net core系列 51 Identity 授权(下)

程序员文章站 2022-07-04 23:28:16
1.6 基于资源的授权 前面二篇中,熟悉了五种授权方式(对于上篇讲的策略授权,还有IAuthorizationPolicyProvider的自定义授权策略提供程序没有讲,后面再补充)。本篇讲的授权方式不是一种全新的授权方式,而是授权应用场景的灵活控制。 基于资源的授权是控制在 razor pages ......

1.6 基于资源的授权

  前面二篇中,熟悉了五种授权方式(对于上篇讲的策略授权,还有iauthorizationpolicyprovider的自定义授权策略提供程序没有讲,后面再补充)。本篇讲的授权方式不是一种全新的授权方式,而是授权应用场景的灵活控制。

  基于资源的授权是控制在 razor pages处理程序或mvc的action之中。资源:比如作者发表的文章,只有该作者才能更新文章,文章在进行授权评估之前,必须从数据存储中检索文章。

 

  (1) 引用 iauthorizationservice 授权服务

    授权作为实现iauthorizationservice服务并注册到服务集合的startup类。 下面在mvc action中引用该接口,准备进行授权控制。

    public class documentcontroller : controller
    {

        private readonly iauthorizationservice _authorizationservice;
        private readonly idocumentrepository _documentrepository;

        public documentcontroller(iauthorizationservice authorizationservice,
                                  idocumentrepository documentrepository)
        {
            _authorizationservice = authorizationservice;
            _documentrepository = documentrepository;
        }
    }

     iauthorizationservice接口有二个authorizeasync方法重载:

        //重载1:指定资源resource和策略需求列表
         task<authorizationresult> authorizeasync(claimsprincipal user, object resource, ienumerable<iauthorizationrequirement> requirements);
        //重载2:指定资源resource和策略名称
        task<authorizationresult> authorizeasync(claimsprincipal user, object resource, string policyname);

 

  (2) 授权需求定义

    基于 crud (创建、 读取、 更新、 删除) 的授权操作,使用operationauthorizationrequirement帮助器类,来提供一些授权名称。

    /// <summary>
    ///授权四种需求crud
    /// </summary>
    public static class operations
    {
        public static operationauthorizationrequirement create =
            new operationauthorizationrequirement { name = nameof(create) };
        public static operationauthorizationrequirement read =
            new operationauthorizationrequirement { name = nameof(read) };
        public static operationauthorizationrequirement update =
            new operationauthorizationrequirement { name = nameof(update) };
        public static operationauthorizationrequirement delete =
            new operationauthorizationrequirement { name = nameof(delete) };
    }    

 

  (3) 定义处理程序

    /// <summary>
    /// 接口authorizationhandler<trequirement, tresource>  
    /// 使用operationauthorizationrequirement需求和document资源
    /// </summary>
    public class documentauthorizationcrudhandler: authorizationhandler<operationauthorizationrequirement, document>
    {

        protected override task handlerequirementasync(authorizationhandlercontext context,
                                                       operationauthorizationrequirement requirement,
                                                       document resource)
        {
            //登录的当前用户是该文章作者,并且有读取权限。实际开发中从数据库读取tresource资源和requirement需求(需求这里是crud权限)
            //动态获取时,可以基于用户声明表userclaim,也可以基于角色声明表roleclaim,使用context.user.hasclaim 来判断
            if (context.user.identity?.name == resource.author &&
                requirement.name == operations.read.name)
            {
                context.succeed(requirement);
            }

            return task.completedtask;
        }
    }    

 

  (4) action中使用authorizeasync验证授权

    当用户登录后,要访问该文章页面时(/document/index/1),使用authorizeasync方法进行调用,确定当前用户是否允许查看提供的文章.

        /// <summary>
        /// /document/index/1
        /// </summary>
        /// <param name="documentid"></param>
        /// <returns></returns>
        public async task<iactionresult> index(int documentid)
        {
            document document = _documentrepository.find(documentid);

            if (document == null)
            {
                return new notfoundresult();
            }

            //使用authorizeasync重载方法(1), 来验证用户访问资源权限,条件是当前用户必需是924964690@qq.com,因为是该用户的文章
            var authorizationresult = await _authorizationservice.authorizeasync(user, document, operations.read);

            //如果授权成功,则返回查看文档的页面
            if (authorizationresult.succeeded)
            {
                return view();
            }
            //用户已通过身份验证,但授权失败
            else if (user.identity.isauthenticated)
            {
                return new forbidresult();
            }
            else
            {
                //challenge:怀疑,返回重新执行身份认证,重定向到登录页
                return new challengeresult();
            }
        }

 

  (5) document实体的定义和该实体仓储

    public class document
    {
        public string author { get; set; }

        public byte[] content { get; set; }

        public int id { get; set; }

        public string title { get; set; }
    }
public class documentrepository : idocumentrepository { public document find(int documentid) { return new document { author = "924964690@qq.com", content = null, id = documentid, title = "test document" }; } } public interface idocumentrepository { document find(int documentid); }

 

  (6) 添加路由规则,和注入iauthorizationservice服务

    services.addsingleton<iauthorizationhandler, documentauthorizationcrudhandler>();
   routes.maproute(
                   name: "document",
                   template: "{controller=document}/{action=index}/{documentid?}");

    最后当924964690@qq.com用户登录成功后,访问document/index/1,查看该文章成功。

 

  总结:基于资源的授权,是应用在mvc的action 中或razor pages处理程序中,是区别之前的几种授权方式, 因为之前讲的授权是:启动程序时授权文件或文件夹,在控制器 action 和pagemodel之上应用[authorize]特性。

       对于authorizeasync重载方法(2)的使用案例查看官网文档,这里不在介绍。

  思考:在实际开发项目中,处理资源如(增、删、改、查)权限,可以考虑本篇的基于资源的授权,但上面的示例需要改进,因为示例中定义的处理程序只针对document资源,以及需求(指权限)是写死在处理程序中。如果要实现通用的资源授权,资源和需求权限需要从数据库中获取。例如考虑如下修改:

         //定义通用的tresource
         public class  authorizationresource
           {
              public string urlresource{get;set;}
           }
       //在index的action中修改
         .authorizeasync(user, new authorizationresource (){urlresource="/document/index/1" }, operations.read);
      //处理程序修改,省略了授权逻辑处理(数据库获取需求和资源)
         public class documentauthorizationcrudhandler: authorizationhandler<operationauthorizationrequirement, authorizationresource >

    

1.7 基于视图的授权    

   在项目开发中,授权权限还需要控制页面,对页面的html进行显示或隐藏。需要在页面上使用授权服务依赖关系注入,若要将授权服务注入到 razor 视图中,使用@inject指令。如果希望每个视图都能使用授权服务,需要将@inject指令插入 _viewimports.cshtml的文件视图中。下面的视图授权控制是基于资源的授权

    @using microsoft.aspnetcore.authorization
    @inject iauthorizationservice authorizationservice
    <!--  指定策略名称  !-->
  @if ((await authorizationservice.authorizeasync(user, "policyname")).succeeded)
  {
      <p>this paragraph is displayed because you fulfilled policyname.</p>
  }
    <!--  model是指tresource !-->    
   @if ((await authorizationservice.authorizeasync(user, model, operations.edit)).succeeded)
  {
      <p><a class="btn btn-default" role="button"
          href="@url.action("edit", "document", new { id = model.id })">edit</a></p>
  }

  总结:视图中授权控制不能保证权限安全,还需要在action中实现授权服务。开源github

 

  参考文献