使用keycloak自定义SPI接入外部用户角色

上一篇已经通过SPI的方式将将外部用户接入,有些用户会涉及到角色,所以还需要将外部角色也接进来

上一篇博客的地址:https://www.ycblog.top/article?articleId=399&pageNum=1

转载于:https://www.ycblog.top/article?articleId=401&pageNum=1

1、定义角色实体(我这里只取了id和name两个字段,有其他字段需求自己添加)

public class BaseRoleEntity {

    private String id;
    private String roleName;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getRoleName() {
        return roleName;
    }

    public void setRoleName(String roleName) {
        this.roleName = roleName;
    }
}

2、修改用户实体(BaseUserEntity),添加角色关联id和角色名称

private String roleId;
private String roleName;

3、添加角色DAO层(BaseUserStorageProvider)

我这里获取角色的字段是固定的,没有通过提供者工厂配置查询角色字段的对应,有需要的话可以参考用户实体字段的映射配置

public class BaseRoleStorageRepository {

    private static final Logger logger = Logger.getLogger(BaseRoleStorageRepository.class);

    private final ComponentModel model;

    private final String tableName;

    public BaseRoleStorageRepository(ComponentModel model,
                                     String tableName) {
        this.model = model;
        this.tableName = tableName;
    }

    /**
     * 根据用户获取关联的角色id和角色名称
     *
     * @return BaseUserEntity
     */
    public List<BaseRoleEntity> getAll() {
        logger.infof("获取所有角色");
        String getAll = "select id,role_name as roleName from "+ tableName;
        return queryList(getAll);
    }


    /**
     * 根据ID查询角色
     *
     * @param id id
     * @return BaseUserEntity
     */
    public BaseRoleEntity getById(String id) {
        logger.infof("根据ID查询:%s", id);
        String getRoleById = "select id,role_name as roleName from " + tableName + " where id = ?";
        return queryOne(getRoleById, id);
    }

    /**
     * 查询
     *
     * @return BaseUserEntity
     */
    protected BaseRoleEntity queryOne(String sql, Object... params) {
        try (Connection ignored = DbConnectionPool.getInstance().getConnection(model.getId())) {
            //开启驼峰映射
            BeanProcessor bean = new GenerousBeanProcessor();
            RowProcessor processor = new BasicRowProcessor(bean);
            //从数据库查询
            return DbConnectionPool.getInstance().getQueryRunner(model.getId()).query(sql, new BeanHandler<>(BaseRoleEntity.class, processor), params);
        } catch (SQLException e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 查询列表
     *
     * @param sql    sql
     * @param params params
     * @return List
     */
    protected List<BaseRoleEntity> queryList(String sql, Object... params) {
        try (Connection ignored = DbConnectionPool.getInstance().getConnection(model.getId())) {
            //开启驼峰映射
            BeanProcessor bean = new GenerousBeanProcessor();
            RowProcessor processor = new BasicRowProcessor(bean);
            //从数据库查询
            return DbConnectionPool.getInstance().getQueryRunner(model.getId()).query(sql, new BeanListHandler<>(BaseRoleEntity.class, processor), params);
        } catch (SQLException e) {
            e.printStackTrace();
            return Collections.emptyList();
        }
    }
}

4、修改用户提供者工厂类(BaseUserStorageProviderFactory)

第一点:该类修改的地方在于校验数据库连接后添加所有角色到keycloak,这一步可以忽略掉,也可以在用户登录验证成功之后添加用对应的角色

第二点:添加角色表的配置项(在之前的基础上添加)

public BaseUserStorageProviderFactory() {
    logger.info("初始化用户提供器");

    //这些配置对应控制台的配置页面
    configMetadata = ProviderConfigurationBuilder.create()
            .add()
            .property()
            .name(DbConnectionPool.ROLE_TABLE_NAME)
            .label("角色表名")
            .type(ProviderConfigProperty.STRING_TYPE)
            .helpText("数据库表名,关联的角色表,主键必须为id,并且角色名称字段必须为role_name")
            .add()
            .build();
}

/**
 * 该方法会在数据库校验成功之后调用
 */
@Override
public void onCreate(KeycloakSession session, RealmModel realm, ComponentModel model) {
    System.out.println("#####################onCreate#############" + model.getId());
    //创建用户联盟的时候添加所有角色
    importAllRole(realm, model);
}

/**
 * 添加角色
 *
 * @param realm realm
 * @param model model
 */
private void importAllRole(RealmModel realm, ComponentModel model) {
    //初始化数据库连接
    DbConnectionPool.getInstance().initDb(model);
    BaseRoleStorageRepository repository = new BaseRoleStorageRepository(model, model.getConfig().getFirst(DbConnectionPool.ROLE_TABLE_NAME));
    List<BaseRoleEntity> baseRoleEntityList = repository.getAll();
    for (BaseRoleEntity baseRoleEntity : baseRoleEntityList) {
        String roleName = baseRoleEntity.getId() + "::" + baseRoleEntity.getRoleName();
        if (realm.getRole(roleName) == null) {
            realm.addRole(roleName);
        }
    }
}

5、修改用户提供器(BaseUserStorageProvider)

主要修改用户登陆成功之后将角色添加到keycloak

/**
 * 通过用户ID查询用户
 *
 * @param s          s
 * @param realmModel realmModel
 * @return UserModel
 */
@Override
public UserModel getUserById(String s, RealmModel realmModel) {
    logger.info("==============> getUserById:" + s);
    //注意:要用此方法获取到外部ID,直接传过来的ID是keycloak二次处理过的,需要处理为实际的id
    String externalId = StorageId.externalId(s);
    logger.infof(" 外部ID:id->%s,用户id:user_id->%s", s, externalId);
    //从数据库查询
    BaseUserEntity baseUser = userStorageDao.getById(externalId);
    if (null == baseUser) {
        logger.info("根据用户id为找到用户信息: " + externalId);
        return null;
    }
    //获取角色信息
    if(baseUser.getRoleId() != null && !"".equals(baseUser.getRoleId().trim())){
        BaseRoleEntity baseRoleEntity = roleStorageDao.getById(baseUser.getRoleId());
        baseUser.setRoleName(baseRoleEntity.getRoleName());
    }
    return new BaseUserAdapter(session, realmModel, model, baseUser);
}

@Override
public UserModel getUserByUsername(String username, RealmModel realmModel) {
    logger.info("==============> getUserByUsername: " + username);
    //从数据库查询
    BaseUserEntity baseUser = userStorageDao.getByUserName(username);
    if (null == baseUser) {
        logger.info("根据名称未找到用户信息: " + username);
        return null;
    }
    //获取角色信息
    if(baseUser.getRoleId() != null && !"".equals(baseUser.getRoleId().trim())){
        BaseRoleEntity baseRoleEntity = roleStorageDao.getById(baseUser.getRoleId());
        baseUser.setRoleName(baseRoleEntity.getRoleName());
    }
    return new BaseUserAdapter(session, realmModel, model, baseUser);
}

6、修改用户适配器(BaseUserAdapter)

将角色信息分配给具体用户

@Override
protected boolean appendDefaultRolesToRoleMappings() {
    return false;
}

@Override
protected Set<RoleModel> getRoleMappingsInternal() {
    HashSet<RoleModel> roleModels = new HashSet<>();
    logger.infof("============getRoleMappingsInternal=============");
    if(entity.getRoleId() != null && !entity.getRoleId().trim().isEmpty()){
        String roleName = entity.getRoleId() + "::" + entity.getRoleName();
        RoleModel role = realm.getRole(roleName);
        if (role == null) {
            role = realm.addRole(roleName);
        }
        roleModels.add(role);
    }
    return roleModels;
}

7、测试

将项目打成jar包,放到keyclaok对应位置,启动keyclaok,创建用户联盟,成功之后可以在角色界面查看到对应库中的所有角色,并且点击对应用户可查看已经分配的角色
在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值