浅聊权限模型

前言

权限模型大家应该都不陌生,在提到权限模型时我们应该先知道访问控制,对于访问控制,NIST 定义:“访问是一种利用计算机资源去做某件事情的能力,访问控制是一种手段,通过它这种能力在某些情况下被允许或者受限制(通常是通过物理上和基于系统的控制)”。
访问控制的目的就是确保特定的数据和资源能够在合适的时间和地点被合适的主体正确的访问和利用。访问控制或权限控制可以简单理解为权力限制,即不同的人由于拥有不同权力,他所看到的、能使用的可能不一样。对应到一个应用系统,其实就是一个用户可能拥有不同的数据权限(看到的)和操作权限(使用的)。
为了达到访问控制的目的我们引入了权限模型,通过不同的权限模型对不同的使用场景下进行访问控制,下面就对一些权限模型进行简单的介绍。

概述

   权限相关术语
英文名称
中文名称
描述
Subject
主体
通常是用户或用户组
Object
对象
权限所作用的对象,通常指各类资源
Action
操作
对Object的操作,如:创建、删除、查询、修改等
Effect
结果/效力
规则匹配后的限制操作,如:Allow/ Deny
Condition
限制条件
权限生效的条件
Permission
权限
用来指代是否允许某人在某种条件下对某种资源做某种操作
Role
角色
权限集合,包含一个或多个权限(Permission)
Policy
策略
一组规则/声明,在特定用户尝试执行特定的操作时进行评估,然后将测咯应用于用户、组和角色
   权限模型
  • ACL —— 权限控制列表(Access Control List)
  • DAC —— 自主访问控制(Discretionary Access Control)
  • MAC —— 强制访问控制(Mandatory Access Control)
  • RBAC —— 基于角色的权限访问控制(Role Based Access Control)
  • ABAC —— 基于属性的权限访问控制(Attribute Based Access Control)
  • PBAC —— 基于策略的权限访问控制(Policy Based Access Control)
  • NGAC —— 下一代权限访问控制(Next Generation Access Control)

模型

  传统权限模型

早期授权模型是一个十分简单的东西,简单到用户和权限之间就是之间关联的,这些对应关系可以存在于数据库,也可以存在于配置文件,这样当用户登录进来之后,只需要去查询当前用户对应的权限即可,没有其它错综复杂的关系在里面。

    ACL

ACL(Access Control List,权限控制列表),是最早的、最基本的一种访问控制机制,是基于客体进行控制的模型,在其他模型中也有 ACL 的身影。
     定义:规定 资源可以被哪些 主体进行哪些 操作。
     Subject can Action to Object
     解决问题:主要为了解决相同权限的用户挨个配置的问题,后来也采用了用户组的方式。
     原理:每一个客体都有一个列表,列表中记录的是哪些主体可以对这个客体做哪些行为。
     示例:一个文件对象具有的如下的ACL,Alice : read,write;Bob: read;
// Alice : read,write; Subject:Alice Action: read,write Object: File // Bob: read; Subject:Bob Action: read,write Object: File
     应用场景:ACL 的第一个实现是在1965 年的 Multics文件系统中,后面大量应用在操作系统、文件系统、网络系统、AD目录等。
     优点:使用简单、易于理解。
     缺点:当主体的数量较多时,配置和维护工作就会成本大、易出错。

    DAC

DAC (Discretionary Access Control,自主访问控制),是 ACL 的扩展模型,灵活性更强。使用这种模型,不仅可以判断 Subject 是否可以对 Object 做 Action 操作,同时也能让 Subject 将 Object、Action 的相同权限授权给其他的 Subject。DAC和ACL的最主要的区别就是 自主这两个字,当一个用户拥有该资源的权限的时候,就可以将该资源的权限再分配给其它用户,这一过程称之为 自主
     定义:规定资源可以被哪些主体进行哪些操作,同时主体可以将资源、操作的权限,授予其他主体 即Subject可以直接或间接地将自己可以访问的某种权限传递给另外的Subject。
  • Subject can Action to Object
  • Subject can grant other Subject
     解决问题:经典的 ACL 模型权限集中在同一个 Subject 上,缺乏灵活性,为了加强灵活性,在 ACL 的基础上,DAC 模型将权限下放,允许拥有权限的 Subject 自主地将权限授予其他 Subject。
     原理:在 ACL 模型的基础上,允许主体可以将自己拥有的权限自主地授予其他主体,所以权限可以任意传递。
     示例:Alice 具有创建文章的权限,同时Alice将创建文章的权限授予Bob, 则Bob也可以具有创建文章的权限。
// Granting Alice article created permission. Subject:Alice Action: Create Object: Article // Alice grants Bob to create articles. Subject:Bob Action: Create Object: Article
     应用场景:常见于文件系统,LINUX,UNIX、WindowsNT 版本的操作系统都提供 DAC 的支持。 如下面是Windows文件系统访问控制模型,在实现上,先对用户鉴权,然后根据控制列表决定用户能否访问资源。用户控制权限的修改通常由特权用户或者管理员组实现。
 
     优点:权限分享成本低、授权机制灵活。
     缺点
  1. 对权限控制比较分散,例如无法简单地将一组文件设置统一的权限开放给指定的一群用户。
  2. 资源所有者(主体)的权限太大,无意间就可能泄露信息。

    MAC

MAC(Mandatory Access Control 强制访问控制),MAC是为了弥补DAC模型中权限控制过于分散的问题而诞生的。在MAC的设计中,每一个对象都都有一些权限标识,每个用户同样也会有一些权限标识,而用户能否对该对象进行操作取决于双方的权限标识的关系,这个限制判断通常是由系统硬性限制的。
     定义
      a、规定资源可以被哪些类别的主体进行哪些操作 ;
      b、规定主体可以对哪些等级的资源进行哪些操作;
      c、当一个操作,同时满足a与b时允许操作。
  • Subject can Action to Object
  • Object can be Action by Subject
     解决问题:MAC是为了弥补DAC模型中权限控制过于分散的问题。
     原理:主体有一个权限标识,客体也有一个权限标识,而主体能否对该客体进行操作取决于双方的权限标识的关系。
     示例:我们设定了“Alice 和 Bob 可以创建文章”这个 MAC 策略:
Subject: Alice Action: Create Object: Article Subject: Bob Action: Create Object: Article
      我们还有另外一个 MAC 策略“文章可以被 Alice 创建”:
Subject: Article Action: Create Object: Alice
      在上述策略中,Alice 可以创建文章,但是 Bob 不能创建文章,因为第二条要求没有满足。
Subject: Bob Action: Create Object: Article
     应用场景:常见于机密机构或者其他等级观念强烈的行业,如军用和市政安全领域的软件。比如在影视作品中我们经常能看到特工在查询机密文件时,屏幕提示需要“无法访问,需要一级安全许可”,这个例子中,文件上就有“一级安全许可”的权限标识,而用户并不具有。
     优点:实现资源与主体的双重验证,确保资源的交叉隔离,提高安全性。
     缺点
  1. 实现工作量较大,授权管理不方便,不够灵活。
  2. 过于强调保密性,对系统连续工作能力(可用性)方面考虑不足。

  常用权限模型

ACL、DAC 和 MAC 是旧时代的权限控制模型,无法满足现代应用对权限控制的需求,于是诞生了新时代的权限模型。

    RBAC

RBAC (Role-Based Access Control,基于角色的访问控制),引入了 Role(角色)的概念,并且将权限与角色进行关联。用户通过扮演某种角色,具有该角色的所有权限。RBAC模型是20世纪90年代研究出来的一种模型,早在20世纪70年代的多用户计算时期,这种思想就已经被提出来,直到20世纪90年代中后期,RBAC才在研究团体中得到一些重视,并先后提出了许多类型的RBAC模型。其中以美国George Mason大学信息安全技术实验室(LIST)提出的RBAC96模型最具有代表性,并得到了普遍的公认。
    定义与概念
    a. 规定角色可以对哪些资源进行哪些操作 ;
    b. 规定主体拥有哪些角色;
       c. 当一个操作同时满足a与b时,允许操作。
      RBAC认为权限授权实际上是Who、What、How的问题。在RBAC模型中,who、what、how构成了访问权限三元组,也就是“Who对What(Which)进行How的操作,也就是“主体”对“客体”的操作,其中who—是权限的拥有者或主体(如:User、Role),what—是资源或对象(Resource、Class),How—是对资源对对象的行为(Action)。
      即RBAC三要素:
  • 用户:系统中所有的账户
  • 角色:一系列权限的集合(如:管理员,开发者,审计管理员等)
  • 权限:菜单,按钮,数据的增删改查等详细权限。
暂时无法在飞书文档外展示此内容
    基本思想:对系统操作的各种权限不是直接授予具体的用户,而是在用户集合与权限集合之间建立一个 角色集合。每一种角色对应一组相应的权限。一旦用户被分配了适当的角色后,该用户就拥有此角色的所有操作权限。将用户和权限进行分离,彼此相互独立,使权限的授予更加灵活。管理员不必在每次创建用户时都进行分配权限的操作,只要分配用户相应的角色即可,而且角色的权限变更比用户的权限变更要少得多,这样将简化用户的权限管理,减少系统的开销。
    RBAC模型
      RBAC其实是一种分析模型,主要分为:基本模型RBAC0(Core RBAC)、角色分层模型RBAC1(Hierarchal RBAC)、角色限制模型RBAC2(Constraint RBAC)和统一模型RBAC3(Combines RBAC)。其中RBAC0是核心,RBAC1、RBAC2、RBAC3都是先后在RBAC0上的扩展。
      RBAC0
        RBAC0定义了能构成RBAC控制系统的最小的元素集合,RBAC0由四部分构成:
         a、用户(User)
         b、角色(Role)
         c、会话(Session)
         d、许可(Pemission)
        许可又包括“操作”和“控制对象”,其中许可被赋予角色,而不是用户,当一个角色被指定给一个用户时,此用户就拥有了该角色所包含的许可。会话是动态的概念,用户必须通过会话才可以设置角色,是用户与激活的角色之间的映射关系。
          用户与角色是多对多的关系;角色和许可也是多对多的关系;用户与会话是一对一关系;会话与角色是一对多关系;
暂时无法在飞书文档外展示此内容
        如上图所示你可以授予给用户一个或多个角色,每个角色具有一个或多个权限,这种 用户-角色、角色-权限间的关系,让我们可以不用再单独管理单个用户,用户从授予的角色里面继承所需的权限。会话在RBAC中是比较隐晦,其表示一个用户多个角色的映射关系。
        每个角色至少具备一个权限,每个用户至少扮演一个角色;可以对两个完全不同的角色分配完全相同的访问权限;会话由用户控制,一个用户可以创建会话并激活多个用户角色,从而获取相应的访问权限,用户可以在会话中更改激活角色,并且用户可以主动结束一个会话。
        用户和角色可以是多对多的关系,权限和角色也是多对多的关系。一个用户在不同的场景下可以拥有不同的角色,例如项目经理也可以是项目架构师等;当然一个角色可以给多个用户,例如一个项目中有多个组长,多个组员等。一个角色可以拥有多份权限, 同一个权限也可以授给多个角色 。
      RBAC1
       RBAC1,它是RBAC角色的分层模型,RBAC1建立在RBAC0基础之上,在角色中引入了 继承的概念,有了继承那么角色就有了上下级或者等级关系。一个角色可以从另一个角色继承权限,并且在拥有其他角色权限的同时,自己还可以关联额外的权限。这种设计可以给角色分组和分层,一 定程度简化了权限管理工作。
暂时无法在飞书文档外展示此内容
      角色间的继承关系可分为 一般继承关系和 受限继承关系。
      一般继承关系仅要求角色继承关系是一个绝对偏序关系,允许角色间的多继承。适用于角色权限有继承需求但又不是严格的上下层级关系的权限场景。
      受限继承关系则进一步要求角色继承关系是一个树结构,实现角色间的单继承。 即下级角色只能拥有一个上级角色,但是上级角色可以拥有多个下级角色。
      RBAC2
      RBAC2,它是RBAC的约束模型,RBAC2也是建立的RBAC0的基础之上的,在RBAC0基础上加入了约束的概念,主要引入了静态职责分离SSD(Static Separation of Duty)和动态职责分离DSD(Dynamic Separation of Duty)。
      SSD是用户和角色的指派阶段加入的,主要是对用户和角色有如下约束:
        a、互斥角色:同一个用户在两个互斥角色中只能选择一个;
        b、基数约束:一个用户拥有的角色是有限的,一个角色拥有的许可也是有限的;
        c、先决条件约束:用户想要获得高级角色,首先必须拥有低级角色;
      DSD是会话和角色之间的约束,可以动态的约束用户拥有的角色,如一个用户可以拥有两个角色,但是运行时只能激活一个角色,比如使用招聘软件时一个人既可以是招聘者也可以是应聘者,但同时只能选择一种身份操作。
暂时无法在飞书文档外展示此内容
      RBAC3
       RBAC3,它是RBAC1与RBAC2合集,所以RBAC3是既有角色分层又有约束的一种模型。
暂时无法在飞书文档外展示此内容
     以上就是RBAC模型的四种设计思想,现在我们用的权限模型都是在RBAC模型的基础上根据自己的业务进行组合和改进。
     优点:相对于 ACL 最大的优势就是它简化了用户与权限的管理,通过对用户进行分类,使得角色与权限关联起来,而用户与权限变成了间接关联 ,RBAC 模型使得访问控制,特别是对用户的授权管理变得非常简单和易于维护,因此有广泛的应用 。
     缺点:
    由于权限是以角色为载体分配的,如果某一角色下的个别用户需要进行特别的权限定制,如:加入一些其他角色的小部分权限或去除当前角色的一些权限时,RBAC 就无能为力了 。
    由于 RBAC 模型没有提供操作顺序控制机制,使得 RBAC 模型很难应用于那些要求有严格操作次序的实体系统 。
开源实现-Casbin
  关于权限访问控制模型相关开源实现也有一些,其中Casbin是比较出名和使用比较广泛的。
  Casbin是一个非常流行的开源访问控制框架,它支持多种访问控制模型(如RBAC、ABAC、PBAC等),并提供了多种语言的实现(如Go、Java、Python等)。
工作原理
  在 Casbin 中, 访问控制模型被抽象为基于 PERM (Policy, Effect, Request, Matcher) 的一个配置文件。至少应包含四个部分: [request_definition], [policy_definition], [policy_effect], [matchers],如果 model 使用 RBAC , 还需要添加[role_definition]部分。
  1. 请求
        定义请求参数,[request_definition] 定义部分,它明确了 e.Enforce(...) 函数中参数的含义。基本请求是一个元组对象,至少需要主题(访问实体)、目标(访问资源) 和动作(访问方式)。
        例如,一个简单请求: r={sub,obj,act}
        它实际上定义了我们应该提供访问控制匹配功能的参数名称和顺序。也可以自定义你自己的请求表单, 如果不需要指定特定资源,则可以这样定义 sub、act ,或者如果有两个访问实体, 则为 sub、sub2、obj、act
  2. 策略
        定义访问策略的模式。[policy_definition]定义部分, 事实上,它在策略规则文件中界定了字段的名称和顺序。policy部分的每一行称之为一个策略规则, 每条策略规则通常以形如p, p2的policy type开头。 如果存在多个policy定义,那么我们会根据前文提到的policy type与具体的某条定义匹配。 上面的policy的绑定关系将会在matcher中使用。
        例如: p={sub, obj, act}p={sub, obj, act, eft}
        注:如果未定义eft (policy result),则策略文件中的结果字段将不会被读取, 和匹配的策略结果将默认被允许
  3. 匹配器
        匹配请求和政策的规则。[matchers] 定义部分,匹配程序是表达式。它定义了如何根据请求评估策略规则
        例如: m = r.sub == p.sub && r.act == p.act && r.obj == p.obj 这个简单和常见的匹配规则意味着如果请求的参数(访问实体,访问资源和访问方式)匹配, 如果可以在策略中找到资源和方法,那么策略结果( p.eft)便会返回。 策略的结果将保存在 p.eft 中。
  4. 效果
        它可以被理解为一种模型,在这种模型中,对匹配结果再次作出逻辑组合判断。[policy_effect] 定义部分,它确定如果多项政策规则与请求相符,是否应批准访问请求。
        例如: e = some (where (p.eft == allow))
        这句话意味,如果匹配的策略结果有一些是允许的,那么最终结果为真。
         e = some (where (p.eft == allow)) && !some(where (p.eft == deny) 此示例组合的逻辑含义是:如果有符合允许结果的策略且没有符合拒绝结果的策略, 结果是为真。 换言之,当匹配策略均为允许(没有任何否认)为真(更简单的是,既允许又同时否认,拒绝就具有优先地位)。
此外RBAC中模型中需要使用[role_definition] 角色域,原语定义了RBAC中的角色继承关系。 Casbin支持RBAC系统的多个实例,例如,用户可以有角色和继承关系,而资源也可以有角色和继承关系。 这两个RBAC系统不会干扰。
   g = _,_ 表示以角色为基础
   g2 =_,_,_ 表示以域为基础(多租户模式)
上述角色定义表明, g 是一个 RBAC系统, g2 是另一个 RBAC 系统。 _, _表示角色继承关系的前项和后项,即前项继承后项角色的权限。 一般来讲,如果您需要进行角色和用户的绑定,直接使用 g 即可。 当您需要表示角色(或者组)与用户和资源的绑定关系时,可以使用 gg2 这样的表现形式。
使用示例
  1. 创建模型文件 model.conf
[request_definition] r = sub, obj, act [policy_definition] p = sub, obj, act [role_definition] g = _, _ [matchers] m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act [policy_effect] e = some(where (p.eft == allow)) #注意:where 跟后面的条件中间会有一个空格,不然的话语法错误
  1. 创建策略文件 policy.csv
[request_definition]
r = sub, obj, act

[policy_definition]
p = sub, obj, act

[role_definition]
g = _, _

[matchers]
m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act

[policy_effect]
e = some(where (p.eft == allow))    #注意:where 跟后面的条件中间会有一个空格,不然的话语法错误
  1. 程序代码实现
// 初始化enforcer func init() { e, err = casbin.NewEnforcer("model.conf", "policy.csv") if err != nil { log.Fatalf("load file failed, %v", err.Error()) } } // 验证权限 func checkPermission(ctx *gin.Context, sub, obj, act string) { log.Printf("sub = %s obj = %s act = %s", sub, obj, act) ok, err := e.Enforce(sub, obj, act) if err != nil { log.Printf("enforce failed %s", err.Error()) ctx.String(http.StatusInternalServerError, "内部服务器错误") return } if !ok { log.Println("权限验证不通过") ctx.String(http.StatusOK, "权限验证不通过") return } log.Println("权限验证通过") ctx.String(http.StatusOK, "权限验证通过") } // 定义接口访问控制 func main() { r := gin.Default() r.GET("/users", func(ctx *gin.Context) { sub := ctx.Query("username") obj := ctx.Request.URL.Path act := ctx.Request.Method checkPermission(ctx, sub, obj, act) }) r.POST("/users", func(ctx *gin.Context) { sub := ctx.Query("username") obj := ctx.Request.URL.Path act := ctx.Request.Method checkPermission(ctx, sub, obj, act) }) r.Run() }
实现 ABAC 的核心机制是在请求发起后,subject attributes、object attributes 与 environment conditions 作为输入,PDP 根据 PEP 的数据去获取 policy 与环境条件,再进行计算,最后确定是否有权进行请求。
ABAC实现
可扩展访问控制标记语言(Extensible Access Control Markup Language,XACML)是目前 ABAC 唯一的国际公开标准,已经在全球范围内被广泛采用,XACML 灵活的扩展性及丰富的规则及函数是实现复杂访问控制规则的科学选择。 XACML 支持多种逻辑表达式和属性类型,具有很强的策略表达能力,非常适合对这种细粒度的大数据存储访问控制模型进行授权描述。同时具有可扩展性,能够支持大 数据环境下的灵活、动态访问控制的需求。 但是这个实现过于复杂以及并不成熟,采用的并不多。
  在ABAC上实现比较好的就是AWS IAM,AWS 作为云计算的领导者,很早就实现了类似的功能,而使用 IAM 则是 operations 的必修课。参考这个视频,AWS ABAC
ABAC解决复杂场景问题
  1. Attribute 易于管理
    容易管理用户或资源的 attribute。如对用户而言,租户、业务线、部门、用户ID、所属团队空间等就是天然的 attribute;而对于需要管理的对象而言,年级、学科、学段、对象类型等都可以作为 attribute。除此以外,用户或对象关联的标签也也都可以作为 attribute 使用。
  2. 细粒度授权支持
    细粒度的授权管理,在 policy 中允许判断很灵活,请求中属性可以基于正则表达式判断,也可以使用逻辑与、逻辑或的关系自由组合很多不同的访问规则。可以实现灵活的 policy,解析 JSON 或者 XML 去动态的创建规则,而这些含有规则的 JSON 或 XML,则是可以被编程实现的在 RBAC 的时代,这是很难的)。
  3. 访问控制管理成本很低
    系统管理员很友好,在 RBAC 的时代,如果我需要实现细粒度的资源管理或者经常 subject 与 object 的对应关系经常变动,那么管理员难以操作的,也很容易出现问题,其中常常被采用的解决方案就是创建那些本不应该存在的 role。但是在 ABAC 时代,管理员的管理对象会缩减到 policy,也就是只处理访问控制。如:医疗机构中,如果某个护士负责照顾老张,系统管理员只需要新建一个 policy 并写上允许访问即可,当老张出院后,只需要删除或者失效这个 policy 就可以了。在 RBAC 的环境中,你可能需要为某个虚拟的 role 动态的添加 permission,而 permission 如果到了针对单个病人的情况下,是绝对多如牛毛的,特别是有两个叫做老张的病人时。
  4. 动态的总体控制
    Environment conditions 也能够提供统一的系统级别的控制,比如威胁等级或者按照区域划分安全级别,不同的区域使用 ABAC 时,可能环境上会有变化。例如我们常用红区来表示最高安全级别,那么我们默认就需要 deny 所有请求,并且会触发警报等等,但是在绿区这种办公区域,可能默认所有的请求都是被允许的等等。Environment conditions 可以提供“拉闸”这样的功能,而且它也是可以动态调整的。
ABAC优缺点
  优点:
  1. 对于大型组织,基于RBCA的控制模型需要维护大量的角色和授权关系,相比而言, ABAC 更加灵活。
  2. 新增资源时, ABAC 仅需要维护较少的资源,而 RBAC 需要维护所有相关的角色,ABAC 可扩展性更强、更方便。
  3. ABAC 有更加细粒度 控制和根据上下文动态执行, RBAC 只能基于静态的参数进行判断。
  4. 能力最为强大,基于描述主体、客体、动作和环境的属性进行鉴权,可实现非常精细化的权限管控。
  缺点 :
  1. 模型构建相对比较复杂。
  2. 无法直观审计用户拥有哪些权限、 规则 复杂会提高管理成本。
  3. 定义权限时,不能直观看出用户和对象间的关系。
  4. 规则 如果稍微复杂一点,或者设计混乱,会给管理者维护和追查带来麻烦。
  5. 权限判断需要实时执行, 规则过多会导致性能问题。

  PBAC

下一代权限模型

  NGAC

参考文档

https://en.wikipedia.org/wiki/Access-control_list
https://xie.infoq.cn/article/b1c8f075c354654d38e6fab0b
https://dinolai.com/notes/others/authorization-models-acl-dac-mac-rbac-abac.html
https://www.jianshu.com/p/115938c6294e
https://www.jianshu.com/p/e596f3b5d53e
https://nvlpubs.nist.gov/nistpubs/specialpublications/NIST.SP.800-162.pdf
https://casbin.org/zh/docs/overview
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zhangkaixuan456

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值