通用型认证与授权最小系统,以较小的代价实现中立开源项目:UniAuth

年前,就如很多创业公司刚开始的时候一样,点融网的主要业务架构在一个被称为MainApp + Workflow上的应用:

MainApp处理投资者的投资、充值、提现等投资端的操作;

用Workflow来处理所有的进件、审批、放款、催收等贷款端的操作。

当业务体量并不大的时候,世界一切都显得那么简单。前面一个包含了MainApp + Workflow的应用,后面一个包罗万象的数据库。一切为了快速的迭代和发展!彼时那是一个 App + 一个 DB的时代。

斗转星移,点融网渐渐从一个小荷才露尖尖角的小松鼠,发展成一个互金领域的映日荷花别样红的强力领军人物之一。到这个阶段,很多公司都会面临业务的复杂度极具上升,需要通过分拆业务系统来承载更多的业务流量及复杂性。于是很自然的,前边的应用,从1变成了N。点融的世界开始变得不是那么简单。我们的工程师面对着N * App 的场景。每个应用都在做自己的权限控制模块。因为,似乎每个应用的服务对象都不尽相同。有销售、有运营、有技术支持、有财务审计等等。

所以,谁/何种角色,对于某些特征资源拥有怎样的访问权限?这是萦绕在很多点融工程师心中的一个问题。

新的突破:认证和授权功能- UniAuth

两年前,几位点融的工程师,被赋予了这样的使命:以较小的代价实现中立的,脱离于特定业务场景的认证(authentication) 和授权(authorization) 功能 – UniAuth。

摆在这些工程师面前的有这样一些问题:市场上是否有类似的系统可以被使用呢?

UniAuth 这种类型的系统,在市场上叫做 IAM (Identity Access Management) 。UniAuth 既是以做轻量化的 IAM为目标。 这方面在美帝做得最好的公司叫做 okta(15年估值15亿美金) ,  amazon 的 AWS 里面也有 IAM。

美国的公司,对于国内有墙,服务器不稳定的可能性、对中国 support 不好 这些因素导致我们很难选择美国的 IAM产品,并且这些服务,本身的资费对于一个处于成长期的公司而言太贵,同时对于互金类公司而言,对于数据/系统的安全性的考量,我们会更倾向于基于框架上的二次开发模式。纵观当时市场上轻量级的IAM 系统, 要么没有开源满足点融需求可用的、要么仅存在于精美的PPT当中。

基于成本的考量,当时的点融还没有相应的预算投入到这些在业务价值中的优先级较小的项目中。基于定制化需求的考量自己公司做一个,持续投入资源在 support 上形成优势。UniAuth 的目标:

1)兼容目前子系统的权限控制模型

子系统的权限模型可以适配转换进入新系统的权限模型。

2)具有广泛的第三方鉴权系统或协议的可接纳性

需要广泛的鉴权系统/协议的集成支持,包括但不限于:sso,oauth,ntlm/kerberos,openid,ldap/ms active directory,saml...因为说不清未来要集成什么东西。但有需要时可以通过配置,或以较小的代价接入。

3)具有良好扩展性

扩展性表现在认证和授权的各个阶段和环节,每个环节都有默认实现,但可以提供途径根据需要进行覆写和干预。这通常发生在子系统认为框架提供的某个环节不爽的场景,比如我觉得公共登录页很丑,我要定制自己的登录页。

4)方便的组,角色,人员权限分配和控制

当新加入业务操作人员时,管理人员只需要简短操作就可以方便的进行账户权限分配,控制,管理,权限开关设置等。

5)代码侵入性

权限控制对系统业务级代码无侵入性,或有较少侵入性。

6)使用成熟解决方案,不重复造轮子

使用业界久经考验的成熟开源方案,少些代码,遇到问题通过社区很快得到解决。

基于以上设计原则,我们选择了 CAS + Spring Security 的开源组合来作为我们 Uniauth 框架开发的基础、设计一套基于mysql的权限模型将其融入到 SpringSecurity 中,为点融网的前后端分离进行了定制化,Uniauth 雏形就有了。

架构设计

1)系统依赖架构图


在这样的架构下,cas 服务器独立地存在,拥有了扩展 uniauth-server 数据源的可能性,可以同时 从 LDAP 取得企业用户数据,也允许客户端以纯 webservice 的形式集成到 Uniauth 当中。在authentication、authorization、data level filter 的整个链条中,每一个链条都可以被打断。

举例来讲,我们来了一个python base 的客户,他只想要做 authentication,那么在上图中,只需要通过 cas 完成 authentication拿到用户的 identity,然后再使用 identity 去 uniauth-server 得到该 identity 的 profile即可满足其需求,整个流程全部以 webservice 的形式完成, 与客户端语言无关。

再举例来讲,数据系统想要数据,直接问 uniauth-server 的 API 索取即可。当然默认情况下我们提供了强大的基于 SpringSecurity 的 CAS 客户端,内网 Java base 的客户集成起来要相对容易很多。

2)项目内部的模块依赖图


源代码模块化,比如客户端(uniauth-server的)如:业务系统、数据系统仅依赖 common 模块即可访问 uniauth-server 的读接口,并且尽可能少的依赖 jar包。客户端(uniauth-server的) 如:techops、cas 仅依赖 share-rw 即可访问 uniauth-server的读和写接口。同时 uniauth-server 提供的接口是基于 jax-rs 标准的 json 接口,所以异构系统完全可以自己写客户端访问uniauth-server。

集成系统集成 Uniauth 框架图:


该图描述了一个以正常形式集成 Uniauth 系统的 java base 的集成方的流程。

(SSO 纯 API 集成流程图)

Uniauth 提供了纯 API 的形式集成 Uniauth 的系统,该方式给予了客户最大化的自由,不想使用我们提供的任何 jar 包,或者异构系统。集成想要自由,就给它自由,而且自由的同时保证了认证机制的安全。

数据库模型图(在没有加上 SaaS 化之前的版本):


现在的数据库里面对于 User 表又扩展了其 EAV 模型,增加了 SaaS etc..几个比较重要的点:

1.所有的实体数据都不会被删除,只会被禁用(status 字段)

2.角色可以通过组赋予与集成、也可以直接赋予人

3.组树状结构的 closure table 设计

4.不同的 domain 拥有不同的 role 和 permission,权限数据在不同集成系统之间隔离,而 user 和 group 数据又是共享的

5.audit 表通过 aop, 记录下对数据库和 API 的一切访问轨迹

6.树状数据库设计

为了避免过长的篇幅描述 UniAuth 中多个树状数据结构,以一个网易评论树来做讲解:


UniAuth采取了闭包表的数据设计方式:


Comment Table Data:


Comment Path Table Data:


这种设计,comment table 本身并不保存评论与评论之间的关系,而将该关系用另外一张表(comment_path)保存起来,理论上讲需要 O(n²)的空间来存储关系,但现实中并不会需要这么多。


3)数据结构关系

每根红线都是 comment_path 中的一条数据,线条上的数字为 depth。

1.查询直接回复4号 comment 的 comment(父查子)

select c.* from comment c join comment_path cp on (c.id = cp.descendant) where cp.ancestor = 4 and depth = 1;

2.查询所有回复4号的子 comment(父查所有子)

select c.* from comment c join comment_path cp on (c.id = cp.descendant) where cp.ancestor = 4;

如果你需要保留层级关系,则将 cp 中的值也返回即可

3.查询所有7号的父 comment(子查所有父)

select c.* from comment c JOIN comment_path cp on (c.id = cp.ancestor) where cp.descendant = 7;

4.添加一条子回复到 6号 comment 上(新增)

step a:

insert into comment(value, topic_id, user_id) values('(10)我以gin食阼啦', 1, 2);

拿到该句返回的 id, 假设为10

step b:

insert into comment_path (ancestor, descendant, depth) select cp.ancestor, 10, cp.depth+1 from comment_path as cp where cp.descendant=6 union all select 10, 10, 0;

只要拥有子 comment_id 为6作为子节点的节点,全都新增一个 id 为10,depth+1的子节点 并且插入一个10, 10, 0的节点

5.从评论链中删除4号 comment 及其子 comment(删除子或者子树)

delete a from comment_path a join comment_path b on (a.descendant = b.descendant) where b.ancestor=4;这句话等价于"delete from comment_path where descendant in (select descendant from comment_path where ancestor = 4);”

但 mysql 会报 from 句子中的表不能用于 update

6.将6号 comment 的父 comment 更改为2号(移动子或者子树)

step a:

delete a from comment_path as a join comment_path as d on a.descendant = d.descendant left join comment_path as x on x.ancestor = d.ancestor and x.descendant = a.ancestor where d.ancestor = 6 and x.ancestor is null;

这样删除的原因和需求5一致

step b:

insert into comment_path (ancestor, descendant, depth) select supertree.ancestor, subtree.descendant, supertree.depth+subtree.depth+1 from comment_path as supertree join comment_path as subtree where subtree.ancestor = 6 and supertree.descendant = 2;

以上6种需求覆盖了最为常用的几种情况,解决了基本上 UniAuth 在闭包表上遇到的所有的问题。

closure table 是反模式设计的一种经典设计,在结构化数据库里面,SQL 可以很轻易高效地支持对树的各种各样的增、删、改、查、移的需求。这种设计给到 UniAuth 系统中的数据表设计特点给了很大的支持。

UniAuth 优势分析

1)成熟度

市场上会有一些开源的 IAM 系统。相较于很多项目在项目前期处于 bug 较多的探索时期,UniAuth 经过两年多的点融内部研发,和互金生产环境的检验,UniAuth 已经是一个成熟的生产环境质量的产品。

2)成本

相较于市场上一些 IAM 系统不菲的软件授权费用/授权使用费用,UniAuth 将项目源码完全开源。并鼓励更多的极客可以贡献的代码,让很多公共模块可以投入更少,成效更快。

3)框架支持与拓展

UniAuth 的底层架构实现了 SpringSecurity,并且通过 CAS 实现 Authentication,因此可以支持包括SPEL在内的所有 CAS,SpringSecurity 的特性及其拓展。

4)数据库及设计

对于早期的创业公司而言,成本控制永远是一个中心话题。基于 MySQL 的数据库,降低了很大的运营成本。并且,UniAuth 的数据库设计,对于树状结构数据的增改,做了大量优化和特定设计。这会在后文提到。

5)客户端支持/跨域访问

同时支持客户端和 REST API 访问,解决跨域访问问题。

6)SSO

CAS 天生自带 SSO 的实现,为应用的 Authentication 提供更多的扩展可能性。

UniAuth 开源

随着点融的发展,点融技术部门也以更加开放的心态回馈社会,将UniAuth项目加入到开源项目社区。

点融开源社区:https://github.com/dianrong/UniAuth

本文作者:钱晟龙 Arc_Qian(点融黑帮),现任点融网架构组产品研发工程师,主要任务是思考并尝试解决各类点融网迈出第一公里之后遇到的现实问题。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,802评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,109评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,683评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,458评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,452评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,505评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,901评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,550评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,763评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,556评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,629评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,330评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,898评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,897评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,140评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,807评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,339评论 2 342

推荐阅读更多精彩内容

  • 我们时常会遇到这样的场景,如:组织结构图、回复评论的评论链、用于组织资源的树形资源组。 如图: 而作为一名程序员如...
    点融黑帮阅读 25,117评论 4 37
  • 世间万物皆空。唯其空,便能包容万物。昨晚,读书分享会结束后,我在世界之窗转车,在392公交车上,结识了来自英国的年...
    詹尼佛阅读 213评论 1 1
  • 一个偶然的机会,去参加了一场读书人的小聚会,正襟危坐之后,听了几个说书人分享了自己带来的一些读书感受,或是谈家国战...
    壹百度阅读 310评论 1 2