我想为Keycloak用户添加一个自定义字段,即创建用户后用户电子邮件的MD5哈希。
我也搜索了Keycloak用户的自定义字段,但似乎无法对其进行编程。我正在考虑开发一个 Keycloak 包装器,但如果已经有一个内置的解决方案那就太好了。
可以吗?
用户的
Attribute
可以保存用户邮箱的MD5哈希值。
Keycloak API 也支持按哈希值搜索。
用户更新API
PUT {Keycloak URL}/admin/realms/{realm}/users/{user-id}
体内
{
"id": <user id>,
"username": <user name>,
"attributes": { "MD5": [ <user email MD5 hash >] }
}
按属性搜索用户
GET {Keycloak URL}/admin/realms/{realm}/users?q={attribute key}:{attribute value}
例子,按用户的MD5值搜索
GET http://localhost:8080/auth/admin/realms/test/users?q=MD5:3b7c8c7791f4f4c7cdd712635277a1f2
使用 node.js 的演示
const axios = require('axios')
const crypto = require('crypto')
const getMasterToken = async () => {
try {
const response = await axios.post(
url = 'http://localhost:8080/auth/realms/master/protocol/openid-connect/token',
data = new URLSearchParams({
'client_id': 'admin-cli',
'username': 'admin',
'password': 'admin',
'grant_type': 'password'
}),
config = {
headers:
{
'Content-Type': 'application/x-www-form-urlencoded'
}
})
return Promise.resolve(response.data.access_token)
} catch (error) {
return Promise.reject(error)
}
}
const getUser = async (token, username) => {
try {
const response = await axios.get(
url = `http://localhost:8080/auth/admin/realms/test/users?username=${username}`,
config = {
headers: {
'Accept-Encoding': 'application/json',
'Authorization': `Bearer ${token}`,
}
}
);
return Promise.resolve(response.data[0])
} catch (error) {
return Promise.reject(error)
}
}
const addUserAttribute = async (token, user_data) => {
try {
const MD5 = crypto.createHash('md5').update(`${user_data.email}`).digest("hex")
const newUserData = {
"id": user_data.id,
"username": user_data.username,
"attributes": { "MD5": [MD5] }
}
const response = await axios.put(
url = `http://localhost:8080/auth/admin/realms/test/users/${user_data.id}`,
data = newUserData,
config = {
headers:
{
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`,
}
})
// response.status = 204 No Content. it means success to update
return Promise.resolve(MD5)
} catch (error) {
return Promise.reject(error)
}
}
const getUserByMD5 = async (token, MD5) => {
try {
const response = await axios.get(
url = `http://localhost:8080/auth/admin/realms/test/users?q=MD5:${MD5}`,
config = {
headers: {
'Accept-Encoding': 'application/json',
'Authorization': `Bearer ${token}`,
}
}
);
return Promise.resolve(response.data)
} catch (error) {
return Promise.reject(error)
}
}
getMasterToken()
.then((token) => {
getUser(token, 'user2')
.then((user_data) => {
console.log(JSON.stringify(user_data, null, 4))
addUserAttribute(token, user_data)
.then((MD5) => {
console.log(`${user_data.username}'s MD5:` + MD5)
getUserByMD5(token, MD5)
.then((user_update_data) => {
console.log(JSON.stringify(user_update_data, null, 4))
})
})
})
})
.catch(error => console.log(error));
结果
$ node update-user.js
{
"id": "a3831b6a-63e5-471d-b71c-6c7d9f49ee47",
"createdTimestamp": 1677063973333,
"username": "user2",
"enabled": true,
"totp": false,
"emailVerified": false,
"firstName": "Tom",
"lastName": "Cruise",
"email": "[email protected]",
"disableableCredentialTypes": [],
"requiredActions": [],
"notBefore": 0,
"access": {
"manageGroupMembership": true,
"view": true,
"mapRoles": true,
"impersonate": true,
"manage": true
}
}
user2's MD5:fa7c3fcb670a58aa3e90a391ea533c99
[
{
"id": "a3831b6a-63e5-471d-b71c-6c7d9f49ee47",
"createdTimestamp": 1677063973333,
"username": "user2",
"enabled": true,
"totp": false,
"emailVerified": false,
"firstName": "Tom",
"lastName": "Cruise",
"email": "[email protected]",
"attributes": {
"MD5": [
"fa7c3fcb670a58aa3e90a391ea533c99"
]
},
"disableableCredentialTypes": [],
"requiredActions": [],
"notBefore": 0,
"access": {
"manageGroupMembership": true,
"view": true,
"mapRoles": true,
"impersonate": true,
"manage": true
}
}
]
在 Java 中我们有一个名为 keycloak-admin-client 的依赖,首先你应该在你的 pom.xml 文件中添加这个依赖。我们使用弹簧靴。
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-admin-client</artifactId>
<version>${keycloak.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak.bom</groupId>
<artifactId>keycloak-adapter-bom</artifactId>
<version>${keycloak.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
Keycloak-admin-client 提供了一些使用 keycloak 的 api 首先,您应该创建一个名为 KeycloakService 的类和一个名为 UserDTO 的 Dto,如下所示
@Data
public class UserDTO {
private static final long serialVersionUID = 1L;
private String username;
private String emailAddress;
private String firstName;
private String lastName;
private String password;
private String telephone;
private String preferredLocal;
private String accessibleViaMobile;
private String fax;
private String mobile;
private String profile;
private String accessProfile;
private String notificationPreference;
}
而 keycloakService 就像:
@Service
public class KeycloakService {
@Autowired
private final Keycloak keycloak;
public UserDTO createUserInKeyCloak(UserDTO userDTO) {
int statusId = 0;
RealmResource realmResource = keycloak.realm(REALM);
UsersResource userRessource = realmResource.users();
UserRepresentation user = new UserRepresentation();
user.setUsername(userDTO.getUsername());
user.setEmail(userDTO.getEmailAddress());
user.setFirstName(userDTO.getFirstName());
user.setLastName(userDTO.getLastName());
user.setEnabled(true);
Map<String, List<String>> attr = new HashMap<>;
List<String> attrString = new ArrayList<>;
attrString.add("YOUR_MD5");
attr.put("MD5",attrString)
user.setAttributes(attr);
// Create user
Response result = userRessource.create(user);
statusId = result.getStatus();
if (statusId == 201) {
String userId =
result.getLocation().getPath().replaceAll(".*/([^/]+)$",
"$1");
System.out.println("User created with userId:" +
userId);
// Define password credential
CredentialRepresentation passwordCred = new
CredentialRepresentation();
passwordCred.setTemporary(false);
passwordCred.setType(CredentialRepresentation.PASSWORD);
passwordCred.setValue(userDTO.getPassword());
// Set password credential
userRessource.get(userId).resetPassword(passwordCred);
System.out.println("Username==" + userDTO.getUsername()
+ " created in keycloak successfully");
return userDTO;
} else if (statusId == 409) {
throw new CustomException(409, "the user is currently
exists");
} else {
throw new CustomException(statusId, "the user could not be
created in keycloak");
}
}
}