keycloak User Storage SPI

 一 , 二为官网内容简介,  三为楼主实战案例, 通过插件项目打成jar包去部署到keycloak二进制文件目录下完成jar包部署相关功能

目录

一, 简介

二, 流程概述

1.提供者接口

2. 提供者其他功能接口

3.用户模型接口

4. 打包和部署

 三, 实战案例

(1)表结构

(2)相关依赖

(3)创建自定义用户表的实体UserEntity

 (4)创建用户字段适配器 UserAdapter

(5)实现提供者类

 (6)实现工厂类

 (7)添加插件的配置

 (8) 打包部署

(9)最后页面效果展示

 (10)插件项目地址


一, 简介

您可以使用用户存储SPI向Keycloak 写入扩展,以连接到外部用户数据库和凭证存储。内置的LDAP和ActiveDirectory支持是此SPI的实际实现。开箱即用,Keycloak 使用其本地数据库来创建、更新和查找用户并验证凭据。然而,组织通常拥有现有的外部专有用户数据库,无法迁移到Keycloak 的数据模型。对于这些情况,应用程序开发人员可以编写用户存储SPI的实现,以连接外部用户存储和内部用户对象模型,Keycloak 使用该模型登录用户并管理用户。

当Keycloak运行时需要查找一个用户时,例如当一个用户正在登录时,它会执行许多步骤来定位该用户。 它首先查看用户是否在用户缓存中; 如果找到用户,它就使用内存中的表示。 然后它在Keycloak本地数据库中查找用户。 如果没有找到用户,则循环用户存储SPI提供程序实现来执行用户查询,直到其中一个实现返回运行时正在寻找的用户。 提供者为用户查询外部用户存储,并将用户的外部数据表示映射到Keycloak的用户元模型。

二, 流程概述

1.提供者接口

在构建用户存储SPI的实现时,您必须定义一个提供者类和一个提供者工厂。 提供者类实例由提供者工厂为每个事务创建。 提供者类执行所有繁重的用户查找和其他用户操作。 它们必须实现org.keycloak.storage.UserStorageProvider接口。

package org.keycloak.storage;

public interface UserStorageProvider extends Provider {


    /**
     * Callback when a realm is removed.  Implement this if, for example, you want to do some
     * cleanup in your user storage when a realm is removed
     *
     * @param realm
     */
    default
    void preRemove(RealmModel realm) {

    }

    /**
     * Callback when a group is removed.  Allows you to do things like remove a user
     * group mapping in your external store if appropriate
     *
     * @param realm
     * @param group
     */
    default
    void preRemove(RealmModel realm, GroupModel group) {

    }

    /**
     * Callback when a role is removed.  Allows you to do things like remove a user
     * role mapping in your external store if appropriate

     * @param realm
     * @param role
     */
    default
    void preRemove(RealmModel realm, RoleModel role) {

    }

}

每个事务创建一次UserStorageProvider实例。事务完成后,调用UserStorageProvider.close()方法,然后对实例进行垃圾收集。实例由提供程序工厂创建。提供程序工厂实现org.keyclok.storage.UserStorageProviderFactory接口。

package org.keycloak.storage;

/**
 * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
 * @version $Revision: 1 $
 */
public interface UserStorageProviderFactory<T extends UserStorageProvider> extends ComponentFactory<T, UserStorageProvider> {

    /**
     * This is the name of the provider and will be shown in the admin console as an option.
     *
     * @return
     */
    @Override
    String getId();

    /**
     * called per Keycloak transaction.
     *
     * @param session
     * @param model
     * @return
     */
    T create(KeycloakSession session, ComponentModel model);
...
}

在实现UserStorageProviderFactory时,提供者工厂类必须指定具体的提供者类作为模板参数。 这是必须的,因为运行时将对该类进行内省,以扫描其功能(它实现的其他接口)。 例如,如果你的提供者类名为FileProvider,那么工厂类应该是这样的:

public class FileProviderFactory implements UserStorageProviderFactory<FileProvider> {

    public String getId() { return "file-provider"; }

    public FileProvider create(KeycloakSession session, ComponentModel model) {
       ...
    }

getId()方法返回用户存储提供程序的名称。 当您希望为特定领域启用提供者时,此id将显示在管理控制台的User Federation页面中。

create()方法负责分配提供者类的实例。 它接受一个org.keycloak.models.KeycloakSession参数。 此对象可用于查找其他信息和元数据,以及提供对运行时内各种其他组件的访问。 ComponentModel参数表示在特定领域中如何启用和配置提供者。 它包含已启用的提供程序的实例id,以及通过管理控制台启用时为其指定的任何配置。

2. 提供者其他功能接口

实现UserStorageProvider接口,可能会注意到它没有定义任何用于定位或管理用户的方法。 这些方法实际上是在其他功能接口中定义的 , 如果需要能够实现特性实现功能接口。 你可以实现以下这些接口:

SPI

描述

org.keycloak.storage.user.UserLookupProvider

外部存储的用户, 能登录keycloak就需要实现这个接口

org.keycloak.storage.user.UserQueryProvider

实现后能从管理控制台查看和管理用户

org.keycloak.storage.user.UserRegistrationProvider

实现后能从添加和删除用户

org.keycloak.storage.user.UserBulkUpdateProvider

实现后支持一组用户的批量更新

org.keycloak.credential.CredentialInputValidator

实现后可以编写验证密码的逻辑

org.keycloak.credential.CredentialInputUpdater

实现后可以更新用户密码

3.用户模型接口

(1) 简介

功能接口中定义的大多数方法要么返回,要么以用户的表示形式传递。 这些表示是由org.keycloak.models.UserModel接口定义的。 应用程序开发人员需要实现这个接口。 它提供了外部用户存储和Keycloak使用的用户元模型之间的映射。

package org.keycloak.models;

public interface UserModel extends RoleMapperModel {
    String getId();

    String getUsername();
    void setUsername(String username);

    String getFirstName();
    void setFirstName(String firstName);

    String getLastName();
    void setLastName(String lastName);

    String getEmail();
    void setEmail(String email);
...
}

UserModel实现提供了读取和更新关于用户的元数据的访问,包括用户名、姓名、电子邮件、角色和组映射,以及其他任意属性。

(2) 存储用户id

UserModel的一个重要方法是getId()方法。 在实现UserModel时,开发人员必须注意用户id格式。 格式必须为:

"f:" + component id + ":" + external id

例如: f:332a234e31234:wburke

Keycloak运行时经常需要根据用户id查找用户。 用户id包含足够的信息,因此运行时不必查询系统中的每个UserStorageProvider来查找用户。

4. 打包和部署

用户存储提供程序被打包在一个 JAR 中 , 将 JAR 复制到standalone/deployments/服务器目录部署

为了让Keycloak能够识别提供者,您需要在JAR中添加一个文件:META-INF/services/org.keycloak.storage.UserStorageProviderFactory。 这个文件必须包含一个行分隔的UserStorageProviderFactory实现的全限定类名列表:

示例:

org.keycloak.examples.federation.properties.ClasspathPropertiesStorageFactory

org.keycloak.examples.federation.properties.FilePropertiesStorageFactory

 三, 实战案例

(1)表结构

Mysql数据库表

 建表语句:

CREATE TABLE `sys_user`  (
  `user_id` bigint(0) NOT NULL AUTO_INCREMENT COMMENT '用户ID',
  `dept_id` bigint(0) NULL DEFAULT NULL COMMENT '部门ID',
  `login_name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '登录账号',
  `user_name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '用户昵称',
  `user_type` varchar(2) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '00' COMMENT '用户类型(00系统用户 01注册用户)',
  `email` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '用户邮箱',
  `phonenumber` varchar(11) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '手机号码',
  `sex` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '0' COMMENT '用户性别(0男 1女 2未知)',
  `avatar` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '头像路径',
  `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '密码',
  `salt` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '盐加密',
  `status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '0' COMMENT '帐号状态(0正常 1停用)',
  `del_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '0' COMMENT '删除标志(0代表存在 2代表删除)',
  `login_date` datetime(0) NULL DEFAULT NULL COMMENT '最后登录时间',
  `pwd_update_date` datetime(0) NULL DEFAULT NULL COMMENT '密码最后更新时间',
  `create_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '创建者',
  `create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
  `update_by` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '更新者',
  `update_time` datetime(0) NULL DEFAULT NULL COMMENT '更新时间',
  `remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '备注',
  PRIMARY KEY (`user_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 393 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '用户信息表' ROW_FORMAT = Dynamic;

表数据:

INSERT INTO `sys_user` VALUES (1, 103, 'zs', '张三', '00', 'zs@163.com', '15888888888', '1', '', '2e4d72b714790d7b37d4033d8d68be7a', '111111', '0', '0', '2021-09-17 09:24:47', '2021-09-03 19:58:47', 'admin', '2021-09-03 19:58:47', '', '2021-09-17 09:24:46', '管理员');

INSERT INTO `sys_user` VALUES (2, 105, 'ls', '李四', '00', 'ls@qq.com', '15666666666', '1', '', '782fbd827bfb1b8d89dda6697d60a875', '222222', '0', '0', '2021-09-03 19:58:47', '2021-09-03 19:58:47', 'admin', '2021-09-03 19:58:47', '', NULL, '测试员');

(2)相关依赖

<dependencies>
        <!-- keycloak -->
        <dependency>
            <groupId>org.keycloak</groupId>
            <artifactId>keycloak-core</artifactId>
            <version>15.0.2</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.keycloak</groupId>
            <artifactId>keycloak-server-spi</artifactId>
            <version>15.0.2</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.22</version>
        </dependency>
        <dependency>
            <groupId>com.zaxxer</groupId>
            <artifactId>HikariCP</artifactId>
            <version>4.0.3</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.7</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.3</version>
        </dependency>
</dependencies>

(3)创建自定义用户表的实体UserEntity

 先根据自己的表结构创建一个用户表对应的pojo类

 (4)创建用户字段适配器 UserAdapter

用来把自定义的用户字段转换成keycloak能够识别的用户字段

(5)实现提供者类

实现以下接口 :

UserStorageProvider, 自定义用户必须要实现的接口

UserLookupProvider, 实现后,可以从keycloak进行登录

CredentialInputValidator, 实现后,可以更新密码

UserRegistrationProvider, 实现后,可以往自己数据库中增加删除修改用户数据

UserQueryProvider 实现后,可以从自己数据库中查询用户

 

 实现数据库CRUD

UserMapper

数据库连接配置

system.properties

 (6)实现工厂类

CustomUserStorageProviderFactory工厂类

读取配置,将配置加载到插件中,需要实现用户入口的工厂类

 (7)添加插件的配置

要让插件正常使用,我们还需要添加一个配置文件,在项目的resources路径下,新建

META-INF\services\org.keycloak.storage.UserStorageProviderFactory

 

在org.keycloak.storage.UserStorageProviderFactory文件中写入刚才创建的入口factory类的路径

比如我们的CustomUserStorageProviderFactory,带完整包名

 (8) 打包部署

将插件打包部署到keycloak 目录 standalone/deployments/ 下

(9)最后页面效果展示

 

 

 (10)插件项目地址

https://gitee.com/its-not-ripe-yet/keycloak-service-user-storage.git

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值