上一篇已经通过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,创建用户联盟,成功之后可以在角色界面查看到对应库中的所有角色,并且点击对应用户可查看已经分配的角色