Table of Contents generated with DocToc
doorkeeper是一个可扩展的认证管理平台,可以在此平台上快速接入权限管理,(不是认证授权框架 Shiro\Spring Security),但可以集成认证授权框架一起使用。这是一个独立的权限配置管理服务的脚手架,用于将登录认证、权限验证、权限管理从业务中抽离,便于业务系统快速接入权限功能。
通过定义资源,通过名称、uri、标签等方式进行查找,平台会基于配置的策略和权限告诉调用方拥有权限的资源。 调用方基于返回数据则可以实现,api控制、菜单控制等,而权限处理逻辑从业务中抽离,便于业务系统快速接入权限功能。
- Vue、ElementUI 前端组件库;
- SpringBoot Spring全家桶作为应用主框架;
- jwt 令牌认证;
- MyBatis、MyBatisPlus DAO层;
- MySQL 持久化关系数据库;
-
域 :
域表示独立的一块区域,域与域之间的所有数据是隔离的,如用户等。Doorkeeper属于特殊的域,用于数据管理操作。 域secret是用于域用户登录时候进行认证处理使用的。 -
用户/用户组 :
用户/用户组在域下唯一,一个用户可以加入多个用户组。用户/用户组都可以进行角色绑定。 -
委托方 :
根据项目需求,委托方可以是一个模块,也可以是一个项目。比如,定义域为仓储项目,那么委托方可以是库位模块等。 -
角色 :
可以给域或者委托方创建角色,主要用于绑定后的权限鉴定。 -
资源 :
资源是一个最小的认证单位,可以通过URI,也可以通过名称或者标签进行定义,平台提供了相应的搜索功能。 比如定义一组资源为菜单,资源名称为菜单ID,用户可以通过搜索是否有对应ID的资源权限来达到菜单访问的能力。 -
策略 :
策略是通过不同权限访问的规则定义。目前策略类型有: 1. 角色 2. 用户 3. 用户组 4. 参数
-
权限 :
权限其实就是真正定义资源和访问策略的绑定关系的一个模型。
核心的思想就是根据调用参数,找到需要认证的资源,通过不同的权限策略流程后返回可访问的资源。
域是完全隔离的,在设计初衷就是希望内部只部署一套doorkeeper服务,来为多个业务方提供服务。可以给doorkeeper域的用户分配多个域的操作权限来管理其他域的资源、角色等创建, 对于非doorkeeper域的用户来说,他们只关心资源的认证。对于不同项目组,可以分配不同的doorkeeper用户,他们自行创建域,就可以达到配置隔离的目的。
ps: 目前权限只控制到域级别,没有对更细的操作进行配置,更加详细的配置可以通过自定义扩展实现。
在doorkeeper域的用户组可以发现父级为realm的用户组,子组名称都是域的名称 (新增域的时候会创建)。当前的逻辑中,只要将用户加入对于的域名的用户组,刷新页面即可在右上角切换新增的域。
这里具体的实现就直接使用了doorkeeper整套处理流程来实现。当新增一个域的时候,doorkeeper域下会新增一个client并且名称就是对应域名。 同时增加对应uri资源、角色、策略和权限,并绑定对应的用户组。具体实现可以从上图进入对应域的client来查看。
这里使用例子实现一个菜单和api的权限认证。
-
创建委托方
-
创建菜单资源
名称是唯一编码,可用于前端标识,对于用户管理这个菜单,只要授予权限,则默认拥有用户列表接口的访问权限。定义标签
type=menu
表示告诉前端这个是一个菜单资源。 -
创建按钮资源
这是一个按钮操作控制,
type=fn
表示告诉前端这个是功能而不是菜单。 -
创建策略/权限
可以创建一个用户策略绑定对应的测试用户,然后再使用此策略创建一个权限绑定对应的资源,实现资源访问的能力。
目前前端就可以根据权限返回,控制页面渲染,而且业务方也可以在具体服务或者网关配置拦截,进行资源认证,达到接口权限的访问控制。
ps: 可以设置一个资源为默认权限,然后把对所有人开放的api配置进去,并通过默认角色绑定,分配给所有人。
- 接口文档
http://host:port/api-docs.html
- 请求参数
请求接口时,额外带上Header
Authorization: 登录后获取到的token
doorkeeper的域下,会有个名为api
的client用于管理系统的访问。用户可以根据接口文档,自行进行权限的配置。注意注意的是 realm 和 client 的获取。
目前,系统判断doorkeeper用户是否拥有某个 realm 是通过用户是否可以访问带有标签type=realmOwn
和realmId=xxx
的资源,所以需要有这个资源的定义和分配。
其次,系统判断doorkeeper用户是否拥有某个 client 是通过资源标签type=clientOwn
和clientId=xxx
。最后一点是,如果绑定doorkeeper域角色admin则可以理解为超级管理员,有任何平台资源访问权限。
管理端处理逻辑在 DoorkeeperService 可以按照需要进行重写
-
初始化数据库
执行脚本
init/init-table.sql
,导入初始数据。 -
修改配置文件
注意修改
spring.datasource
配置,修改数据库名与用户名、密码。 -
启动Java后端服务
通过命令行启动Java后端服务,命令类似
java -jar doorkeeper.jar
。doorkeeper.jar
位于doorkeeper-server/target
目录下。此步骤需要JDK8及以上版本
-
调用Java服务初始化接口
Doorkeeper域的数据通过代码进行初始化,需在启动Java服务后访问初始化接口。该接口在Doorkeeper部署后访问一次即可。
http://host:port/api/init
假设Java服务所在机器(容器)IP地址为
127.0.0.1
,Http服务端口配置为8088
,初始化接口为http://127.0.0.1:8088/init
。初始账户 admin admin
-
启动Node前端服务
vue
目录下执行npm install
安装依赖,然后通过npm run build
编译前端资源,编译完成后的前端静态资源在vue/dist
目录中。本地调试可以使用
npm run serve
启动前端服务,vue/vue.config.js
中可以指定前端项目端口,默认为8082。此步骤需要先安装NodeJs服务
-
配置Nginx代理服务器
假设Nginx与Java后端服务在同一机器上,后端服务启动为
8088
端口。server { listen 80; # Doorkeeper服务域名可自行设置,或使用 doorkeeper.limbo.org server_name doorkeeper.limbo.org; # A 前端静态资源代理,如果采用 npm run build 方式对前端资源进行编译,使用此方法代理前端 location / { # 编译产生的静态资源目录 vue/dist root /path/of/dist; autoindex on; autoindex_exact_size on; autoindex_localtime on; } # B 前端Node服务代理,如果采用 npm run serve 方式启动前端服务,使用此方法代理前端 location / { proxy_pass http://127.0.0.1:8082/; } # 后端Java服务代理 location /api/ { # 后端接口 proxy_pass http://127.0.0.1:8088; } }
前端代理的方式 A B 选择一种即可。建议生产环境使用A方式,将Nginx作为前端服务器,利用Nginx的缓存、gzip等;开发环境使用B方式,可以实现修改前端代码后热更新,便于调试。
管理端访问,进行登录
访问 http://doorkeeper.limbo.org
,以默认用户名admin
密码admin
登录Doorkeeper管理平台,进行配置。
假设Nginx服务所在服务器地址为10.10.10.10
,需配置DNS解析,将doorkeeper.limbo.org
(或自己的其他域名)映射到该IP。
如果没有DNS解析,可在需访问Doorkeeper管理平台的机器hosts
文件中添加一行10.10.10.10 doorkeeper.limbo.org
-
前端打包 通过
npm run build
编译前端资源,可以参考上文nginx配置使用nginx托管静态资源。还可以将静态资源直接放到doorkeeper-server/src/main/resources/static目录下,打包到jar中由java进程提供访问能力。 -
Java服务打包
项目根目录下,执行如下命令打包编译,通过
-P
参数指定环境,如开发环境为-P dev
mvn clean package -Dmaven.test.skip=true -P dev
此步骤需要先安装maven
-
如何修改默认用户名密码?
默认用户名密码在
DoorkeeperService.initDoorkeeper
方法中设置,可以自行修改。需在第一次启动Java服务前修改,修改完后编译、运行Java服务。// 创建管理员账户 User user = new User(); user.setRealmId(realm.getRealmId()); // 设置默认用户名 admin user.setUsername(DoorkeeperConstants.ADMIN); user.setNickname(DoorkeeperConstants.ADMIN); // 设置默认密码 admin user.setPassword(MD5Utils.md5WithSalt(DoorkeeperConstants.ADMIN)); user.setIsEnabled(true); userMapper.insert(user);
如已完成初始化,此时建议只修改密码,可登陆管理平台后进行修改。