KeycloakServiceImpl.java 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486
  1. package com.inspur.customer.service.keycloak;
  2. import com.alibaba.cola.dto.PageResponse;
  3. import com.alibaba.cola.dto.Response;
  4. import com.alibaba.cola.dto.SingleResponse;
  5. import com.google.common.collect.Lists;
  6. import com.inspur.customer.client.keycloak.KeycloakService;
  7. import com.inspur.customer.client.org.SmsbDepartmentUserService;
  8. import com.inspur.customer.constant.Constant;
  9. import com.inspur.customer.object.keycloak.KeycloakUserCO;
  10. import com.inspur.customer.object.keycloak.SwitchDTO;
  11. import com.inspur.customer.object.keycloak.UsersRoleMappingDTO;
  12. import com.inspur.customer.object.org.SmsbDepartmentCmd;
  13. import com.inspur.customer.object.org.SmsbUserAdd;
  14. import com.inspur.customer.object.wechat.Pair;
  15. import lombok.extern.slf4j.Slf4j;
  16. import org.apache.dubbo.config.annotation.DubboReference;
  17. import org.apache.dubbo.config.annotation.DubboService;
  18. import org.keycloak.admin.client.resource.GroupResource;
  19. import org.keycloak.admin.client.resource.GroupsResource;
  20. import org.keycloak.admin.client.resource.RealmResource;
  21. import org.keycloak.admin.client.resource.RoleScopeResource;
  22. import org.keycloak.admin.client.resource.UserResource;
  23. import org.keycloak.representations.idm.ClientRepresentation;
  24. import org.keycloak.representations.idm.CredentialRepresentation;
  25. import org.keycloak.representations.idm.GroupRepresentation;
  26. import org.keycloak.representations.idm.RoleRepresentation;
  27. import org.keycloak.representations.idm.UserRepresentation;
  28. import org.springframework.cache.annotation.CacheEvict;
  29. import org.springframework.cache.annotation.Cacheable;
  30. import org.springframework.util.CollectionUtils;
  31. import org.springframework.util.StringUtils;
  32. import javax.annotation.Resource;
  33. import java.time.LocalDateTime;
  34. import java.util.*;
  35. import java.util.concurrent.atomic.AtomicReference;
  36. import java.util.stream.Collectors;
  37. import java.util.stream.Stream;
  38. @Slf4j
  39. @DubboService
  40. public class KeycloakServiceImpl implements KeycloakService {
  41. @Resource
  42. private RealmResource realmResource;
  43. @DubboReference
  44. private SmsbDepartmentUserService userService;
  45. @Override
  46. @Cacheable(value = "smsb:users")
  47. public Map<String, String> getUserMap() {
  48. Map<String, String> userMap = new HashMap<>();
  49. realmResource.users().list().forEach(user ->{
  50. if (user.getFirstName() != null && user.getLastName() != null && !"".equals(user.getLastName())) {
  51. user.setUsername(user.getLastName() + user.getFirstName());
  52. }
  53. userMap.put(user.getId(),user.getUsername());
  54. });
  55. return userMap;
  56. }
  57. @CacheEvict(value = "smsb:users" , allEntries = true)
  58. public void clearUserMapCache(){
  59. log.info("users map cache clear:{}",LocalDateTime.now() );
  60. }
  61. /**
  62. * get users in role (xuzhou environment)
  63. * @param role role
  64. * @return users
  65. */
  66. @Override
  67. public List<KeycloakUserCO> getUsersInRole(String role) {
  68. List<KeycloakUserCO> keycloakUserCos = new ArrayList<>();
  69. realmResource.roles().get(role).getRoleUserMembers().forEach(user -> {
  70. log.info("name: {}", user.getUsername());
  71. KeycloakUserCO keycloakUserCo = new KeycloakUserCO();
  72. keycloakUserCo.setId(user.getId());
  73. keycloakUserCo.setEmail(user.getEmail());
  74. Map<String, List<String>> attributes = user.getAttributes();
  75. if (attributes != null) {
  76. keycloakUserCo.setPhone(attributes.get("phone"));
  77. keycloakUserCo.setWechat(attributes.get("wechat"));
  78. keycloakUserCo.setWechatApplet(attributes.get("wechat-applet-openid"));
  79. }
  80. keycloakUserCos.add(keycloakUserCo);
  81. });
  82. return keycloakUserCos;
  83. }
  84. /**
  85. * get users by ids
  86. * according to device's related users is no too much, temporary use that way to get users
  87. * @deprecated
  88. * @param userIds ids
  89. * @return users
  90. */
  91. @Override
  92. public List<KeycloakUserCO> getUsersByIds(List<String> userIds) {
  93. List<KeycloakUserCO> keycloakUserCos = new ArrayList<>(userIds.size());
  94. userIds.forEach(id -> {
  95. UserRepresentation userRepresentation = realmResource.users().get(id).toRepresentation();
  96. if (userRepresentation != null) {
  97. KeycloakUserCO keycloakUserCo = new KeycloakUserCO();
  98. keycloakUserCo.setId(userRepresentation.getId());
  99. keycloakUserCo.setUsername(userRepresentation.getUsername());
  100. keycloakUserCo.setEmail(userRepresentation.getEmail());
  101. Map<String, List<String>> attributes = userRepresentation.getAttributes();
  102. if (attributes != null) {
  103. keycloakUserCo.setPhone(attributes.get("phone"));
  104. keycloakUserCo.setWechat(attributes.get("wechat"));
  105. keycloakUserCo.setWechatApplet(attributes.get("wechat-applet-openid"));
  106. }
  107. keycloakUserCos.add(keycloakUserCo);
  108. }
  109. });
  110. return keycloakUserCos;
  111. }
  112. @Override
  113. public Boolean checkUserRole(String userId, String role) {
  114. AtomicReference<Boolean> result = new AtomicReference<>(false);
  115. List<RoleRepresentation> roles = realmResource.users().get(userId).roles().getAll().getRealmMappings();
  116. log.info("userId:{},roles:{}",userId , roles);
  117. roles.forEach(n ->{
  118. if(n.getName().equalsIgnoreCase(role)){
  119. result.set(true);
  120. }
  121. });
  122. return result.get();
  123. }
  124. @Override
  125. public void updateAttribute(String userId ,String property , String value) {
  126. KeycloakUserCO userCO = new KeycloakUserCO();
  127. userCO.setId(userId);
  128. if(Objects.nonNull(property) && property.equals("wechat")){
  129. userCO.setWechat(Collections.singletonList(value));
  130. }else if(Objects.nonNull(property) && property.equals("wechat-applet-openid")){
  131. userCO.setWechatAppletOpenId(value);
  132. }
  133. userService.updateUserAttribute(userCO);
  134. UserResource user = realmResource.users().get(userId);
  135. UserRepresentation userRepresentation = user.toRepresentation();
  136. if (userRepresentation.getAttributes() == null) {
  137. userRepresentation.setAttributes(new HashMap<>());
  138. }
  139. userRepresentation.getAttributes().put(property, Collections.singletonList(value));
  140. user.update(userRepresentation);
  141. }
  142. @Override
  143. public List<String> getAttrByGroupPath(String groupPath, String key) {
  144. GroupRepresentation groupRepresentation = realmResource.getGroupByPath(groupPath);
  145. if (groupRepresentation != null) {
  146. Map<String, List<String>> attributes = groupRepresentation.getAttributes();
  147. return attributes != null ? attributes.get(key) : Collections.emptyList();
  148. }
  149. return Collections.emptyList();
  150. }
  151. @Override
  152. public List<KeycloakUserCO> getGroupSupervisor(String group) {
  153. return getUserByRoleAndGroup(group, "ROLE_OPERATION_SUPERVISOR");
  154. }
  155. @Override
  156. public List<KeycloakUserCO> getSuperAdmin() {
  157. return realmResource.roles().get("ROLE_SUPER_ADMIN").getRoleUserMembers()
  158. .stream()
  159. .map(this::transfer)
  160. .collect(Collectors.toList());
  161. }
  162. @Override
  163. public List<KeycloakUserCO> getAllRoleAdmin() {
  164. return realmResource.roles().get("ROLE_ADMIN").getRoleUserMembers()
  165. .stream()
  166. .map(this::transfer)
  167. .collect(Collectors.toList());
  168. }
  169. @Override
  170. public List<KeycloakUserCO> getAllRoleOperationSupervisor() {
  171. return realmResource.roles().get("ROLE_OPERATION_SUPERVISOR").getRoleUserMembers()
  172. .stream()
  173. .map(this::transfer)
  174. .collect(Collectors.toList());
  175. }
  176. @Override
  177. public List<KeycloakUserCO> getGroupAdmin(String group) {
  178. return getUserByRoleAndGroup(group, "ROLE_ADMIN");
  179. }
  180. @Override
  181. public String getSingleAttrByGroupPath(String group, String key) {
  182. List<String> list = getAttrByGroupPath(group, key);
  183. if (CollectionUtils.isEmpty(list)) {
  184. return null;
  185. } else {
  186. return list.get(0);
  187. }
  188. }
  189. @Override
  190. public List<KeycloakUserCO> searchUserByAttrEntry(List<Pair> pairList) {
  191. String search = pairList.stream()
  192. .filter(t -> Objects.nonNull(t) && Objects.nonNull(t.getKey()) && Objects.nonNull(t.getValue()))
  193. .map(t -> t.getKey() + ":" + t.getValue())
  194. .collect(Collectors.joining(" "));
  195. return realmResource.users().searchByAttributes(search).stream().map(this::transfer).collect(Collectors.toList());
  196. }
  197. @Override
  198. public List<String> getUserGroupPathList(String userId) {
  199. return realmResource.users().get(userId).groups().stream().map(GroupRepresentation::getPath).collect(Collectors.toList());
  200. }
  201. @Override
  202. public List<String> getUserRealmRoles(String userId) {
  203. return realmResource
  204. .users()
  205. .get(userId)
  206. .roles()
  207. .getAll()
  208. .getRealmMappings()
  209. .stream()
  210. .map(RoleRepresentation::getName)
  211. .collect(Collectors.toList());
  212. }
  213. @Override
  214. public Map<String, List<String>> getUserClientRoles(String userId) {
  215. Map<String, List<String>> resultMap = new HashMap<>();
  216. List<ClientRepresentation> clientList = realmResource.clients().findAll();
  217. for (ClientRepresentation clientRepresentation : clientList) {
  218. String clientId = clientRepresentation.getClientId();
  219. List<RoleRepresentation> roleList = realmResource.users().get(userId).roles().clientLevel(clientRepresentation.getId()).listEffective();
  220. if (!roleList.isEmpty()) {
  221. resultMap.put(clientId, roleList.stream().map(RoleRepresentation::getName).collect(Collectors.toList()));
  222. }
  223. }
  224. return resultMap;
  225. }
  226. @Override
  227. public void changePassword(String userId, String newPassword) {
  228. CredentialRepresentation cr = new CredentialRepresentation();
  229. cr.setType(CredentialRepresentation.PASSWORD);
  230. cr.setValue(newPassword);
  231. realmResource.users().get(userId).resetPassword(cr);
  232. }
  233. private List<KeycloakUserCO> getUserByRoleAndGroup(String group, String role) {
  234. if (!StringUtils.hasText(group) || !StringUtils.hasText(role)) {
  235. return Collections.emptyList();
  236. }
  237. List<UserRepresentation> members = realmResource.groups().group(realmResource.getGroupByPath(group).getId()).members();
  238. Set<String> set = realmResource.roles().get(role).getRoleUserMembers().stream().map(UserRepresentation::getId).collect(Collectors.toSet());
  239. return members.stream().filter(t -> set.contains(t.getId())).map(this::transfer).collect(Collectors.toList());
  240. }
  241. private KeycloakUserCO transfer(UserRepresentation userRepresentation) {
  242. if (userRepresentation == null) {
  243. return null;
  244. }
  245. KeycloakUserCO keycloakUserCo = new KeycloakUserCO();
  246. keycloakUserCo.setId(userRepresentation.getId());
  247. keycloakUserCo.setUsername(userRepresentation.getUsername());
  248. keycloakUserCo.setEmail(userRepresentation.getEmail());
  249. Map<String, List<String>> attributes = userRepresentation.getAttributes();
  250. if (attributes != null) {
  251. keycloakUserCo.setPhone(attributes.get("phone"));
  252. keycloakUserCo.setWechat(attributes.get("wechat"));
  253. keycloakUserCo.setWechatApplet(attributes.get("wechat-applet-openid"));
  254. }
  255. return keycloakUserCo;
  256. }
  257. @Override
  258. public Response addKeycloakGroup(String id , GroupRepresentation groupRepresentation) {
  259. return SingleResponse.of(realmResource.groups().group(id).subGroup(groupRepresentation).getDate());
  260. }
  261. @Override
  262. public Response updateKeycloakGroup(String id, GroupRepresentation groupRepresentation) {
  263. realmResource.groups().group(id).update(groupRepresentation);
  264. return SingleResponse.buildSuccess();
  265. }
  266. @Override
  267. public Response removeGroup(String id) {
  268. realmResource.groups().group(id).remove();
  269. return SingleResponse.buildSuccess();
  270. }
  271. @Override
  272. public SingleResponse addKeyClaokUser(SmsbUserAdd user) {
  273. UserRepresentation userRepresentation = new UserRepresentation();
  274. userRepresentation.setUsername(user.getUsername());
  275. userRepresentation.setGroups(Collections.singletonList(user.getTenant()));
  276. userRepresentation.setEnabled(Boolean.TRUE);
  277. userRepresentation.setCredentials(user.getCredentials());
  278. userRepresentation.setFirstName(user.getName());
  279. if(Objects.nonNull(getUserIdByName(userRepresentation.getUsername()))){
  280. return SingleResponse.buildFailure("400","该用户名已存在!");
  281. }
  282. javax.ws.rs.core.Response response = realmResource.users().create(userRepresentation);
  283. log.info("create keycloak user response status:{}", response.getStatus());
  284. Optional.ofNullable(response.getStatusInfo()).ifPresent(t -> log.info(t.getReasonPhrase()));
  285. int status = response.getStatus();
  286. if(status != 201){
  287. return SingleResponse.buildFailure("400","账号创建失败!");
  288. }
  289. return SingleResponse.of(getUserIdByName(userRepresentation.getUsername()));
  290. }
  291. /**
  292. * 通过用户名获取用户id
  293. *
  294. * @param username
  295. * @return
  296. */
  297. private String getUserIdByName(String username){
  298. Optional<String> optional = realmResource.users().search(username)
  299. .stream().filter(user -> user.getUsername().equals(username))
  300. .map(UserRepresentation::getId)
  301. .findFirst();
  302. return optional.isEmpty()?null:optional.get();
  303. }
  304. @Override
  305. @CacheEvict(value = "smsb:users" , allEntries = true)
  306. public Response updateUser(String userId, Object representation, Integer operateType) {
  307. UserResource userResource = realmResource.users().get(userId);
  308. UserRepresentation user = userResource.toRepresentation();
  309. switch (operateType) {
  310. case Constant.ENABLE:
  311. SwitchDTO switchDTO = (SwitchDTO) representation;
  312. user.setEnabled(switchDTO.getEnabled());
  313. break;
  314. case Constant.RESET_PASSWORD:
  315. CredentialRepresentation credential = (CredentialRepresentation) representation;
  316. user.setCredentials(Stream.of(credential).collect(Collectors.toList()));
  317. break;
  318. case Constant.RESET_ATTRIBUTE:
  319. UserRepresentation userRepresentation = (UserRepresentation) representation;
  320. user.setEmail(Optional.ofNullable(userRepresentation.getEmail()).orElse(null));
  321. user.setFirstName(userRepresentation.getFirstName());
  322. user.setAttributes(userRepresentation.getAttributes());
  323. break;
  324. case Constant.UPDATE_USER_FIRSTNAME:
  325. user.setFirstName((String) representation);
  326. break;
  327. default:
  328. break;
  329. }
  330. userResource.update(user);
  331. return SingleResponse.buildSuccess();
  332. }
  333. @Override
  334. @CacheEvict(value = "smsb:users" , allEntries = true)
  335. public Response removeUser(String userId) {
  336. realmResource.users().get(userId).remove();
  337. return Response.buildSuccess();
  338. }
  339. @Override
  340. public Response regrouping(String userId, String groupId) {
  341. UserResource userResource = realmResource.users().get(userId);
  342. List<GroupRepresentation> groups = userResource.groups();
  343. // 移除
  344. groups.forEach(groupRepresentation-> userResource.leaveGroup(groupRepresentation.getId()));
  345. // 添加
  346. GroupsResource initGroups = realmResource.groups();
  347. List<String> targetGroups = Arrays.stream(initGroups.group(groupId).toRepresentation().getPath().split("/")).filter(Objects::nonNull).collect(Collectors.toList());
  348. getGroupId(targetGroups ,initGroups.groups()).forEach(userResource::joinGroup);
  349. return SingleResponse.buildSuccess();
  350. }
  351. private List<String> getGroupId(List<String> targetGroups , List<GroupRepresentation> groups){
  352. List<String> groupIds = Lists.newArrayList();
  353. GroupRepresentation group = groups.stream().filter(ele -> targetGroups.contains(ele.getName())).findFirst().get();
  354. groupIds.add(group.getId());
  355. if(!CollectionUtils.isEmpty(group.getSubGroups())){
  356. groupIds.addAll(getGroupId(targetGroups , group.getSubGroups()));
  357. }
  358. return groupIds;
  359. }
  360. public List<RoleRepresentation> getRoles() {
  361. return realmResource.roles().list();
  362. }
  363. public List<RoleRepresentation> getUserRoles(String userId) {
  364. return realmResource.users().get(userId).roles().getAll().getRealmMappings();
  365. }
  366. @Override
  367. public Response roleMapping(UsersRoleMappingDTO usersRoleMappingDTO) {
  368. RoleScopeResource roleScopeResource = realmResource.users().get(usersRoleMappingDTO.getUserId()).roles().realmLevel();
  369. roleScopeResource.add(Optional.ofNullable(usersRoleMappingDTO.getAddRoleList()).orElse(Lists.newArrayList()));
  370. roleScopeResource.remove(Optional.ofNullable(usersRoleMappingDTO.getRemoveRoleList()).orElse(Lists.newArrayList()));
  371. return SingleResponse.buildSuccess();
  372. }
  373. @Override
  374. public Response queryUserCredentials(String userId) {
  375. return SingleResponse.of(realmResource.users().get(userId).credentials());
  376. }
  377. @Override
  378. public Response removeUserCredentials(String userId , String credentialId) {
  379. realmResource.users().get(userId).removeCredential(credentialId);
  380. return SingleResponse.buildSuccess();
  381. }
  382. @Override
  383. public Response addKeycloakTenant(GroupRepresentation groupRepresentation) {
  384. return SingleResponse.of(realmResource.groups().add(groupRepresentation).getDate());
  385. }
  386. @Override
  387. public Boolean isExitTargetRole(String userId ,String role) {
  388. List<String> roleList = realmResource.users().get(userId).roles().realmLevel().listAll()
  389. .stream().map(RoleRepresentation::getName).collect(Collectors.toList());
  390. return roleList.contains(role);
  391. }
  392. @Override
  393. @Cacheable(value = "msr:user:tenant", key = "#userId")
  394. public String queryUserTenant(String userId) {
  395. List<GroupRepresentation> groupList = realmResource.users().get(userId).groups()
  396. .stream()
  397. .sorted(Comparator.comparing(i -> i.getPath().length()))
  398. .collect(Collectors.toList());
  399. return CollectionUtils.isEmpty(groupList) ? "" : groupList.stream().findFirst().get().getPath();
  400. }
  401. @Override
  402. public Boolean addAttributesByUserId(String userId, String key, List<String> values) {
  403. UserResource userResource = realmResource.users().get(userId);
  404. UserRepresentation user = userResource.toRepresentation();
  405. Map<String, List<String>> attributes = Optional.ofNullable(user.getAttributes()).orElse(new HashMap<>());
  406. attributes.put(key ,values);
  407. user.setAttributes(attributes);
  408. userResource.update(user);
  409. return Boolean.TRUE;
  410. }
  411. @Override
  412. public PageResponse<UserRepresentation> getUserListByIds(SmsbDepartmentCmd departmentCmd , List<String> userId) {
  413. if(CollectionUtils.isEmpty(userId)){
  414. GroupResource groupResource = realmResource.groups()
  415. .group(realmResource.getGroupByPath(departmentCmd.getTenant()).getId());
  416. log.info("查询分页参数:first:{},max:{}",departmentCmd.getOffset() ,departmentCmd.getPageSize());
  417. return PageResponse.of(
  418. groupResource.members(departmentCmd.getOffset() ,departmentCmd.getPageSize())
  419. , groupResource.members().size() ,departmentCmd.getPageSize() ,departmentCmd.getPageIndex());
  420. }else{
  421. List<UserRepresentation> userRepresentations = realmResource.groups()
  422. .group(realmResource.getGroupByPath(departmentCmd.getTenant()).getId())
  423. .members()
  424. .stream()
  425. .filter(i -> userId.contains(i.getId()))
  426. .collect(Collectors.toList());
  427. return PageResponse.of(userRepresentations ,0 ,0,0);
  428. }
  429. }
  430. @Override
  431. public List<String> queryWechatByRole(String role) {
  432. List<String> openIds = Lists.newArrayList();
  433. realmResource.roles().get(role).getRoleUserMembers().forEach(user->{
  434. Map<String, List<String>> attributes = user.getAttributes();
  435. if (attributes != null && attributes.get("wechat") != null) {
  436. openIds.addAll(attributes.get("wechat"));
  437. }
  438. });
  439. return openIds;
  440. }
  441. }