package com.inspur.customer.service.keycloak; import com.alibaba.cola.dto.PageResponse; import com.alibaba.cola.dto.Response; import com.alibaba.cola.dto.SingleResponse; import com.google.common.collect.Lists; import com.inspur.customer.client.keycloak.KeycloakService; import com.inspur.customer.client.org.SmsbDepartmentUserService; import com.inspur.customer.constant.Constant; import com.inspur.customer.object.keycloak.KeycloakUserCO; import com.inspur.customer.object.keycloak.SwitchDTO; import com.inspur.customer.object.keycloak.UsersRoleMappingDTO; import com.inspur.customer.object.org.SmsbDepartmentCmd; import com.inspur.customer.object.org.SmsbUserAdd; import com.inspur.customer.object.wechat.Pair; import lombok.extern.slf4j.Slf4j; import org.apache.dubbo.config.annotation.DubboReference; import org.apache.dubbo.config.annotation.DubboService; import org.keycloak.admin.client.resource.GroupResource; import org.keycloak.admin.client.resource.GroupsResource; import org.keycloak.admin.client.resource.RealmResource; import org.keycloak.admin.client.resource.RoleScopeResource; import org.keycloak.admin.client.resource.UserResource; import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.CredentialRepresentation; import org.keycloak.representations.idm.GroupRepresentation; import org.keycloak.representations.idm.RoleRepresentation; import org.keycloak.representations.idm.UserRepresentation; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.Cacheable; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; import javax.annotation.Resource; import java.time.LocalDateTime; import java.util.*; import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; import java.util.stream.Stream; @Slf4j @DubboService public class KeycloakServiceImpl implements KeycloakService { @Resource private RealmResource realmResource; @DubboReference private SmsbDepartmentUserService userService; @Override @Cacheable(value = "smsb:users") public Map getUserMap() { Map userMap = new HashMap<>(); realmResource.users().list().forEach(user ->{ if (user.getFirstName() != null && user.getLastName() != null && !"".equals(user.getLastName())) { user.setUsername(user.getLastName() + user.getFirstName()); } userMap.put(user.getId(),user.getUsername()); }); return userMap; } @CacheEvict(value = "smsb:users" , allEntries = true) public void clearUserMapCache(){ log.info("users map cache clear:{}",LocalDateTime.now() ); } /** * get users in role (xuzhou environment) * @param role role * @return users */ @Override public List getUsersInRole(String role) { List keycloakUserCos = new ArrayList<>(); realmResource.roles().get(role).getRoleUserMembers().forEach(user -> { log.info("name: {}", user.getUsername()); KeycloakUserCO keycloakUserCo = new KeycloakUserCO(); keycloakUserCo.setId(user.getId()); keycloakUserCo.setEmail(user.getEmail()); Map> attributes = user.getAttributes(); if (attributes != null) { keycloakUserCo.setPhone(attributes.get("phone")); keycloakUserCo.setWechat(attributes.get("wechat")); keycloakUserCo.setWechatApplet(attributes.get("wechat-applet-openid")); } keycloakUserCos.add(keycloakUserCo); }); return keycloakUserCos; } /** * get users by ids * according to device's related users is no too much, temporary use that way to get users * @deprecated * @param userIds ids * @return users */ @Override public List getUsersByIds(List userIds) { List keycloakUserCos = new ArrayList<>(userIds.size()); userIds.forEach(id -> { UserRepresentation userRepresentation = realmResource.users().get(id).toRepresentation(); if (userRepresentation != null) { KeycloakUserCO keycloakUserCo = new KeycloakUserCO(); keycloakUserCo.setId(userRepresentation.getId()); keycloakUserCo.setUsername(userRepresentation.getUsername()); keycloakUserCo.setEmail(userRepresentation.getEmail()); Map> attributes = userRepresentation.getAttributes(); if (attributes != null) { keycloakUserCo.setPhone(attributes.get("phone")); keycloakUserCo.setWechat(attributes.get("wechat")); keycloakUserCo.setWechatApplet(attributes.get("wechat-applet-openid")); } keycloakUserCos.add(keycloakUserCo); } }); return keycloakUserCos; } @Override public Boolean checkUserRole(String userId, String role) { AtomicReference result = new AtomicReference<>(false); List roles = realmResource.users().get(userId).roles().getAll().getRealmMappings(); log.info("userId:{},roles:{}",userId , roles); roles.forEach(n ->{ if(n.getName().equalsIgnoreCase(role)){ result.set(true); } }); return result.get(); } @Override public void updateAttribute(String userId ,String property , String value) { KeycloakUserCO userCO = new KeycloakUserCO(); userCO.setId(userId); if(Objects.nonNull(property) && property.equals("wechat")){ userCO.setWechat(Collections.singletonList(value)); }else if(Objects.nonNull(property) && property.equals("wechat-applet-openid")){ userCO.setWechatAppletOpenId(value); } userService.updateUserAttribute(userCO); UserResource user = realmResource.users().get(userId); UserRepresentation userRepresentation = user.toRepresentation(); if (userRepresentation.getAttributes() == null) { userRepresentation.setAttributes(new HashMap<>()); } userRepresentation.getAttributes().put(property, Collections.singletonList(value)); user.update(userRepresentation); } @Override public List getAttrByGroupPath(String groupPath, String key) { GroupRepresentation groupRepresentation = realmResource.getGroupByPath(groupPath); if (groupRepresentation != null) { Map> attributes = groupRepresentation.getAttributes(); return attributes != null ? attributes.get(key) : Collections.emptyList(); } return Collections.emptyList(); } @Override public List getGroupSupervisor(String group) { return getUserByRoleAndGroup(group, "ROLE_OPERATION_SUPERVISOR"); } @Override public List getSuperAdmin() { return realmResource.roles().get("ROLE_SUPER_ADMIN").getRoleUserMembers() .stream() .map(this::transfer) .collect(Collectors.toList()); } @Override public List getAllRoleAdmin() { return realmResource.roles().get("ROLE_ADMIN").getRoleUserMembers() .stream() .map(this::transfer) .collect(Collectors.toList()); } @Override public List getAllRoleOperationSupervisor() { return realmResource.roles().get("ROLE_OPERATION_SUPERVISOR").getRoleUserMembers() .stream() .map(this::transfer) .collect(Collectors.toList()); } @Override public List getGroupAdmin(String group) { return getUserByRoleAndGroup(group, "ROLE_ADMIN"); } @Override public String getSingleAttrByGroupPath(String group, String key) { List list = getAttrByGroupPath(group, key); if (CollectionUtils.isEmpty(list)) { return null; } else { return list.get(0); } } @Override public List searchUserByAttrEntry(List pairList) { String search = pairList.stream() .filter(t -> Objects.nonNull(t) && Objects.nonNull(t.getKey()) && Objects.nonNull(t.getValue())) .map(t -> t.getKey() + ":" + t.getValue()) .collect(Collectors.joining(" ")); return realmResource.users().searchByAttributes(search).stream().map(this::transfer).collect(Collectors.toList()); } @Override public List getUserGroupPathList(String userId) { return realmResource.users().get(userId).groups().stream().map(GroupRepresentation::getPath).collect(Collectors.toList()); } @Override public List getUserRealmRoles(String userId) { return realmResource .users() .get(userId) .roles() .getAll() .getRealmMappings() .stream() .map(RoleRepresentation::getName) .collect(Collectors.toList()); } @Override public Map> getUserClientRoles(String userId) { Map> resultMap = new HashMap<>(); List clientList = realmResource.clients().findAll(); for (ClientRepresentation clientRepresentation : clientList) { String clientId = clientRepresentation.getClientId(); List roleList = realmResource.users().get(userId).roles().clientLevel(clientRepresentation.getId()).listEffective(); if (!roleList.isEmpty()) { resultMap.put(clientId, roleList.stream().map(RoleRepresentation::getName).collect(Collectors.toList())); } } return resultMap; } @Override public void changePassword(String userId, String newPassword) { CredentialRepresentation cr = new CredentialRepresentation(); cr.setType(CredentialRepresentation.PASSWORD); cr.setValue(newPassword); realmResource.users().get(userId).resetPassword(cr); } private List getUserByRoleAndGroup(String group, String role) { if (!StringUtils.hasText(group) || !StringUtils.hasText(role)) { return Collections.emptyList(); } List members = realmResource.groups().group(realmResource.getGroupByPath(group).getId()).members(); Set set = realmResource.roles().get(role).getRoleUserMembers().stream().map(UserRepresentation::getId).collect(Collectors.toSet()); return members.stream().filter(t -> set.contains(t.getId())).map(this::transfer).collect(Collectors.toList()); } private KeycloakUserCO transfer(UserRepresentation userRepresentation) { if (userRepresentation == null) { return null; } KeycloakUserCO keycloakUserCo = new KeycloakUserCO(); keycloakUserCo.setId(userRepresentation.getId()); keycloakUserCo.setUsername(userRepresentation.getUsername()); keycloakUserCo.setEmail(userRepresentation.getEmail()); Map> attributes = userRepresentation.getAttributes(); if (attributes != null) { keycloakUserCo.setPhone(attributes.get("phone")); keycloakUserCo.setWechat(attributes.get("wechat")); keycloakUserCo.setWechatApplet(attributes.get("wechat-applet-openid")); } return keycloakUserCo; } @Override public Response addKeycloakGroup(String id , GroupRepresentation groupRepresentation) { return SingleResponse.of(realmResource.groups().group(id).subGroup(groupRepresentation).getDate()); } @Override public Response updateKeycloakGroup(String id, GroupRepresentation groupRepresentation) { realmResource.groups().group(id).update(groupRepresentation); return SingleResponse.buildSuccess(); } @Override public Response removeGroup(String id) { realmResource.groups().group(id).remove(); return SingleResponse.buildSuccess(); } @Override public SingleResponse addKeyClaokUser(SmsbUserAdd user) { UserRepresentation userRepresentation = new UserRepresentation(); userRepresentation.setUsername(user.getUsername()); userRepresentation.setGroups(Collections.singletonList(user.getTenant())); userRepresentation.setEnabled(Boolean.TRUE); userRepresentation.setCredentials(user.getCredentials()); userRepresentation.setFirstName(user.getName()); if(Objects.nonNull(getUserIdByName(userRepresentation.getUsername()))){ return SingleResponse.buildFailure("400","该用户名已存在!"); } javax.ws.rs.core.Response response = realmResource.users().create(userRepresentation); log.info("create keycloak user response status:{}", response.getStatus()); Optional.ofNullable(response.getStatusInfo()).ifPresent(t -> log.info(t.getReasonPhrase())); int status = response.getStatus(); if(status != 201){ return SingleResponse.buildFailure("400","账号创建失败!"); } return SingleResponse.of(getUserIdByName(userRepresentation.getUsername())); } /** * 通过用户名获取用户id * * @param username * @return */ private String getUserIdByName(String username){ Optional optional = realmResource.users().search(username) .stream().filter(user -> user.getUsername().equals(username)) .map(UserRepresentation::getId) .findFirst(); return optional.isEmpty()?null:optional.get(); } @Override @CacheEvict(value = "smsb:users" , allEntries = true) public Response updateUser(String userId, Object representation, Integer operateType) { UserResource userResource = realmResource.users().get(userId); UserRepresentation user = userResource.toRepresentation(); switch (operateType) { case Constant.ENABLE: SwitchDTO switchDTO = (SwitchDTO) representation; user.setEnabled(switchDTO.getEnabled()); break; case Constant.RESET_PASSWORD: CredentialRepresentation credential = (CredentialRepresentation) representation; user.setCredentials(Stream.of(credential).collect(Collectors.toList())); break; case Constant.RESET_ATTRIBUTE: UserRepresentation userRepresentation = (UserRepresentation) representation; user.setEmail(Optional.ofNullable(userRepresentation.getEmail()).orElse(null)); user.setFirstName(userRepresentation.getFirstName()); user.setAttributes(userRepresentation.getAttributes()); break; case Constant.UPDATE_USER_FIRSTNAME: user.setFirstName((String) representation); break; default: break; } userResource.update(user); return SingleResponse.buildSuccess(); } @Override @CacheEvict(value = "smsb:users" , allEntries = true) public Response removeUser(String userId) { realmResource.users().get(userId).remove(); return Response.buildSuccess(); } @Override public Response regrouping(String userId, String groupId) { UserResource userResource = realmResource.users().get(userId); List groups = userResource.groups(); // 移除 groups.forEach(groupRepresentation-> userResource.leaveGroup(groupRepresentation.getId())); // 添加 GroupsResource initGroups = realmResource.groups(); List targetGroups = Arrays.stream(initGroups.group(groupId).toRepresentation().getPath().split("/")).filter(Objects::nonNull).collect(Collectors.toList()); getGroupId(targetGroups ,initGroups.groups()).forEach(userResource::joinGroup); return SingleResponse.buildSuccess(); } private List getGroupId(List targetGroups , List groups){ List groupIds = Lists.newArrayList(); GroupRepresentation group = groups.stream().filter(ele -> targetGroups.contains(ele.getName())).findFirst().get(); groupIds.add(group.getId()); if(!CollectionUtils.isEmpty(group.getSubGroups())){ groupIds.addAll(getGroupId(targetGroups , group.getSubGroups())); } return groupIds; } public List getRoles() { return realmResource.roles().list(); } public List getUserRoles(String userId) { return realmResource.users().get(userId).roles().getAll().getRealmMappings(); } @Override public Response roleMapping(UsersRoleMappingDTO usersRoleMappingDTO) { RoleScopeResource roleScopeResource = realmResource.users().get(usersRoleMappingDTO.getUserId()).roles().realmLevel(); roleScopeResource.add(Optional.ofNullable(usersRoleMappingDTO.getAddRoleList()).orElse(Lists.newArrayList())); roleScopeResource.remove(Optional.ofNullable(usersRoleMappingDTO.getRemoveRoleList()).orElse(Lists.newArrayList())); return SingleResponse.buildSuccess(); } @Override public Response queryUserCredentials(String userId) { return SingleResponse.of(realmResource.users().get(userId).credentials()); } @Override public Response removeUserCredentials(String userId , String credentialId) { realmResource.users().get(userId).removeCredential(credentialId); return SingleResponse.buildSuccess(); } @Override public Response addKeycloakTenant(GroupRepresentation groupRepresentation) { return SingleResponse.of(realmResource.groups().add(groupRepresentation).getDate()); } @Override public Boolean isExitTargetRole(String userId ,String role) { List roleList = realmResource.users().get(userId).roles().realmLevel().listAll() .stream().map(RoleRepresentation::getName).collect(Collectors.toList()); return roleList.contains(role); } @Override @Cacheable(value = "msr:user:tenant", key = "#userId") public String queryUserTenant(String userId) { List groupList = realmResource.users().get(userId).groups() .stream() .sorted(Comparator.comparing(i -> i.getPath().length())) .collect(Collectors.toList()); return CollectionUtils.isEmpty(groupList) ? "" : groupList.stream().findFirst().get().getPath(); } @Override public Boolean addAttributesByUserId(String userId, String key, List values) { UserResource userResource = realmResource.users().get(userId); UserRepresentation user = userResource.toRepresentation(); Map> attributes = Optional.ofNullable(user.getAttributes()).orElse(new HashMap<>()); attributes.put(key ,values); user.setAttributes(attributes); userResource.update(user); return Boolean.TRUE; } @Override public PageResponse getUserListByIds(SmsbDepartmentCmd departmentCmd , List userId) { if(CollectionUtils.isEmpty(userId)){ GroupResource groupResource = realmResource.groups() .group(realmResource.getGroupByPath(departmentCmd.getTenant()).getId()); log.info("查询分页参数:first:{},max:{}",departmentCmd.getOffset() ,departmentCmd.getPageSize()); return PageResponse.of( groupResource.members(departmentCmd.getOffset() ,departmentCmd.getPageSize()) , groupResource.members().size() ,departmentCmd.getPageSize() ,departmentCmd.getPageIndex()); }else{ List userRepresentations = realmResource.groups() .group(realmResource.getGroupByPath(departmentCmd.getTenant()).getId()) .members() .stream() .filter(i -> userId.contains(i.getId())) .collect(Collectors.toList()); return PageResponse.of(userRepresentations ,0 ,0,0); } } @Override public List queryWechatByRole(String role) { List openIds = Lists.newArrayList(); realmResource.roles().get(role).getRoleUserMembers().forEach(user->{ Map> attributes = user.getAttributes(); if (attributes != null && attributes.get("wechat") != null) { openIds.addAll(attributes.get("wechat")); } }); return openIds; } }