KeyCloak 统一权限管理
如果你的系统需要对外提供 SSO 能力,那么可以使用 KeyCloak
能力介绍
KeyCloak 一般情况下,不需要使用,但是如果我们作为用户中心需要接入外部系统,即外部系统需要由我们管理用户体系,提供统一鉴权管理的能力,那么可以外接 KeyCloak 系统
集成方案
网上关于 KeyCloak 有很多说明,可以结合来看,本文档主要描述目前我们的项目如何集成 KeyCloak,用防疫子系统为例,来进行说明
KeyCloack 的配置
每个子系统都作为独立应用接入,即我们将会有以下 Clients,全部应用都由 TerryQi 来支持大家: Clients 状态 说明 负责人 fuxin_server 已创建 Server 端 TerryQi fuxin_web 已创建 Web 端 TerryQi
服务端应用创建方法
Springboot 的集成
本期项目我们使用 KeyCloak 来进行 SSO 和用户校验,具体权限通过 RBAC 管理。目前没有做 Security 的集成,我们的项目框架默认集成了 Security,因此需要先禁用 Security,即 FangYiApplication
在 pom.xml 中引入
<properties>
<log4j2.version>2.17.0</log4j2.version>
<qrqy.developer.version>3.0.5</qrqy.developer.version>
<keycloak.version>18.0.2</keycloak.version>
</properties>
<!-- keycloak -->
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-spring-boot-starter</artifactId>
<version>${keycloak.version}</version>
</dependency>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.keycloak.bom</groupId>
<artifactId>keycloak-adapter-bom</artifactId>
<version>${keycloak.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
yml 的配置文件 application-dev.yml
# keycloak
keycloak:
# 表示是一个public的client
public-client: true
# bearer-only
bearer-only: true
# keycloak的地址
auth-server-url: http://xxxxxxx:8080
# keycloak中的realm
realm: FuxinRealm
# client ID
resource: yqfk_server
# 安全约束
securityConstraints:
# 不设定角色,不进行约束
- authRoles:
securityCollections:
# name可以随便写
- name: public
patterns:
- /api/common/*
# 以下路径需要base_user角色才能访问
- authRoles:
- base_user
securityCollections:
# name可以随便写
- name: private
patterns:
- /api/*
ssl-required: none #一定是none
如何联调
进行登陆获取 token,调用接口为:
- 获取 AccessToken:http://xxxxxxx:8080/realms/FuxinRealm/protocol/openid-connect/token
- 刷新 AccessToken:http://xxxxxxx:8080/realms/FuxinRealm/protocol/openid-connect/token
调用 api/*的接口,需要在 header 中设置 Authorization
获取用户信息
UserUtil 方法中,curUser 方法
/**
* 获取当前的登陆用户
*
* @return 当前登陆用户信息
*/
@SneakyThrows
public IBaseUserDetails<String> curUser() {
UserLoginDo<String> userLoginDo = new UserLoginDo();
RefreshableKeycloakSecurityContext session = (RefreshableKeycloakSecurityContext) request.getAttribute(KeycloakSecurityContext.class.getName());
if (session == null) {
throw new BizException(CommonResponseCode.USER_INVALID, "用户未登录或无效");
}
AccessToken token = session.getToken();
//TODO 等待OA团队获取userId
userLoginDo.setUserId(token.getSubject());
userLoginDo.setRealName(token.getPreferredUsername());
return userLoginDo;
}
APP 端的集成
APP 集成的流程是先调用登陆接口,获取 token,后续的接口都将 token 放在 Header 中
如果接口返回 403,则重新刷新一下 token,否则重新登陆
Web 集成
Vue 的 Web 集成非常方便,直接在项目中引入 keycloak.js 即可,获取接口用其描述的方法,也是自动刷新 token 参考文档:https://www.keycloak.org/securing-apps/vue
前端集成非常简单,原则上,因为已经集成了 KeyCloak,所以就不需要开发登陆页面了
import Keycloak from "keycloak-js";
let initOptions = {
url: "http://keycloak.learn.isart.me:8080",
realm: "FuxinRealm",
clientId: "fuxin_web",
onLoad: "login-required",
};
let keycloak = Keycloak(initOptions);
keycloak
.init({ onLoad: initOptions.onLoad })
.then((auth) => {
if (!auth) {
window.location.reload();
} else {
Vue.$log.info("Authenticated");
new Vue({
el: "#app",
render: (h) => h(App, { props: { keycloak: keycloak } }),
});
}
//Token Refresh
setInterval(() => {
keycloak
.updateToken(70)
.then((refreshed) => {
if (refreshed) {
Vue.$log.info("Token refreshed" + refreshed);
} else {
Vue.$log.warn("Token not refreshed, valid for " + Math.round(keycloak.tokenParsed.exp + keycloak.timeSkew - new Date().getTime() / 1000) + " seconds");
}
})
.catch(() => {
Vue.$log.error("Failed to refresh token");
});
}, 6000);
})
.catch(() => {
Vue.$log.error("Authenticated Failed");
});