前情提要

接上一篇《 使用 Keycloak 接管 SpringBoot 应用的用户认证功能 - Jeff Tian的文章 - 知乎 》。使用 Keycloak 接管 SpringBoot 应用的用户认证功能,通过手动迁移一个用户,并重点详述了登录验证的接口改造,验证了端到端的可行性。现在,来优化第一步,批量迁移用户

可能有多种方案,这里详细列举两种简单可行的,其他方案比较复杂,比如需要停机,以及写复杂的命令行脚本。一种是利用 Keycloak 的导入功能,一种是通过编程调用 Keycloak 的接口方式。

(推荐!)方案一: partialImport

首先,使用一个 Keycloak 实例,导出一个 json 文件,比如我用我的线上 Keycloak 实例(目前是 16.1.1 版本): keycloak.jiwai.win,点击导出:
image.png

这样,得到了一个 JSON 文件。打开它,然后,在最后一个 }前加入用户信息。比如:

json { ... users: [ { username: user, enabled: true, credentials: [ { type: password, value: password } ] } ] ... }

当然,你可以使用程序,创建多个用户的 JSON 数组,添加到导出的 JSON 文件,然后保存。接着,在目标 Keycloak 中,选择导入(我这里用了 19.0.1 版本):

image.png

当选择好刚刚保存的 JSON 文件后,就能在预览框里看到内容了(同时你还可以编辑它,比如删除不想导入的信息等)。在导入前,勾上用户(Keycloak 会贴心地显示出一共会导入多少个用户),然后,选择遇到冲突时跳过的选项,就可以点击“导入”按钮了:
image.png

导入成功后,Keycloak 会再次显示,一共添加了多少个用户到系统里:
image.png

关闭弹窗,再去用户面板,就可以看见导入的用户了:
image.png

这里演示只导入了一个用户,如果要导入多个用户,准备好一个用户 JSON 数组就好了。这一步,当然是可以用程序生成了,只要格式符合上面的例子就行了。

方案二:调用 Keycloak 创建用户接口来创建用户

Keycloak 的创建用户接口,一次可以创建一个,但是既然是编程调用,自然一个循环就可以搞定多个用户的批量创建了。以 Java 代码为例,可以这样调用创建用户接口(以我的线上 Keycloak 实例为目标):

java public String createUser(UserPayload user) throws IOException { var url = https://keycloak.jiwai.win/auth/admin/realms/UniHeart/users; var payload = java.lang.String.format({firstName:Sergey,lastName:Kargopolov, email:%s, enabled:true, username:%s, credentials:[{type:password,value:%s,temporary:false}]}, user.getEmail(), user.getUsername(), user.getPassword()); return this.jsonRequest(url, payload).toString(); }

上面的代码,在真正执行前,需要先获取到管理员的访问令牌,这可以通过调用 Keycloak 的 master 域的管理员登录接口来完成:

java public KeycloakAccessTokenPayload getAdminAccessToken() throws IOException { var username = System.getenv(KC_ADMIN); var password = System.getenv(KC_PASSWORD);

System.out.println(java.lang.String.format(username = %s; password = %s, username, password));

var mediaType = MediaType.parse(application/x-www-form-urlencoded);
var body = RequestBody.create(mediaType, java.lang.String.format(username=%s&password=%s&grant_type=password&client_id=admin-cli, username, password));
var request = new Request.Builder()
        .url(https://keycloak.jiwai.win/auth/realms/master/protocol/openid-connect/token)
        .method(POST, body)
        .addHeader(Content-Type, application/x-www-form-urlencoded)
        .build();
var response = client.newCall(request).execute();

var s = Objects.requireNonNull(response.body()).string();

System.out.println(java.lang.String.format(token response = %s, s));

return JsonHelper.parseFrom(s);

}

获取到管理员的访问令牌后,需要在后续的接口请求中,将其添加到 http header 中去:

java public Response jsonRequest(String url, String payload) throws IOException { var mediaType = MediaType.parse(application/json); var body = RequestBody.create(mediaType, payload); var request = new Request.Builder().url(url).method(POST, body).addHeader(Content-Type, application/json).addHeader(Authorization, java.lang.String.format(Bearer %s, getAdminAccessToken().access_token)).build(); var res = client.newCall(request).execute();

System.out.println(java.lang.String.format(jsonRequest response = %s, res.toString()));

return res;

}

这就是创建用户的核心代码了,完整的可运行代码,详见: https://github.com/Jeff-Tian/keycloak-springboot/blob/main/src/main/java/com/example/keycloakspringboot/KeycloakHelper.java#L29

总结

有两种简单直接的方式来将旧系统中的用户迁移到 Keycloak 里,一是手工导入、二是编程调用接口创建。从少写代码的角度来看,推荐使用第一种手工导入方式。