mirror of
https://github.com/PowerJob/PowerJob.git
synced 2025-07-17 00:00:04 +08:00
feat: support user manager #860
This commit is contained in:
parent
5e7751f092
commit
9b5916daf3
@ -16,6 +16,10 @@ public enum AuthErrorCode {
|
||||
USER_NOT_LOGIN("-100", "UserNotLoggedIn"),
|
||||
USER_NOT_EXIST("-101", "UserNotExist"),
|
||||
USER_AUTH_FAILED("-102", "UserAuthFailed"),
|
||||
/**
|
||||
* 账户被停用
|
||||
*/
|
||||
USER_DISABLED("-103", "UserDisabled"),
|
||||
|
||||
|
||||
NO_PERMISSION("-200", "NoPermission"),
|
||||
|
@ -22,6 +22,7 @@ import tech.powerjob.server.auth.login.*;
|
||||
import tech.powerjob.server.auth.service.login.LoginRequest;
|
||||
import tech.powerjob.server.auth.service.login.PowerJobLoginService;
|
||||
import tech.powerjob.server.common.Loggers;
|
||||
import tech.powerjob.server.common.constants.SwitchableStatus;
|
||||
import tech.powerjob.server.persistence.remote.model.UserInfoDO;
|
||||
import tech.powerjob.server.persistence.remote.repository.UserInfoRepository;
|
||||
|
||||
@ -108,9 +109,11 @@ public class PowerJobLoginServiceImpl implements PowerJobLoginService {
|
||||
powerJobUserOpt = userInfoRepository.findByUsername(dbUserName);
|
||||
} else {
|
||||
|
||||
// 更新二次校验的 TOKEN 信息
|
||||
UserInfoDO dbUserInfoDO = powerJobUserOpt.get();
|
||||
|
||||
checkUserStatus(dbUserInfoDO);
|
||||
|
||||
// 更新二次校验的 TOKEN 信息
|
||||
dbUserInfoDO.setTokenLoginVerifyInfo(JsonUtils.toJSONString(bizUser.getTokenLoginVerifyInfo()));
|
||||
dbUserInfoDO.setGmtModified(new Date());
|
||||
|
||||
@ -147,6 +150,8 @@ public class PowerJobLoginServiceImpl implements PowerJobLoginService {
|
||||
|
||||
UserInfoDO dbUser = dbUserInfoOpt.get();
|
||||
|
||||
checkUserStatus(dbUser);
|
||||
|
||||
PowerJobUser powerJobUser = new PowerJobUser();
|
||||
|
||||
String tokenLoginVerifyInfoStr = dbUser.getTokenLoginVerifyInfo();
|
||||
@ -174,6 +179,17 @@ public class PowerJobLoginServiceImpl implements PowerJobLoginService {
|
||||
return Optional.of(powerJobUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查 user 状态
|
||||
* @param dbUser user
|
||||
*/
|
||||
private void checkUserStatus(UserInfoDO dbUser) {
|
||||
int accountStatus = Optional.ofNullable(dbUser.getStatus()).orElse(SwitchableStatus.ENABLE.getV());
|
||||
if (accountStatus == SwitchableStatus.DISABLE.getV()) {
|
||||
throw new PowerJobAuthException(AuthErrorCode.USER_DISABLED);
|
||||
}
|
||||
}
|
||||
|
||||
private ThirdPartyLoginService fetchBizLoginService(String loginType) {
|
||||
final ThirdPartyLoginService loginService = code2ThirdPartyLoginService.get(loginType);
|
||||
if (loginService == null) {
|
||||
|
@ -71,6 +71,11 @@ public class UserInfoDO {
|
||||
*/
|
||||
private String originUsername;
|
||||
|
||||
/**
|
||||
* 账号当前状态
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
private Date gmtCreate;
|
||||
|
||||
private Date gmtModified;
|
||||
|
@ -1,7 +1,8 @@
|
||||
package tech.powerjob.server.persistence.remote.repository;
|
||||
|
||||
import tech.powerjob.server.persistence.remote.model.UserInfoDO;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import tech.powerjob.server.persistence.remote.model.UserInfoDO;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
@ -12,7 +13,7 @@ import java.util.Optional;
|
||||
* @author tjq
|
||||
* @since 2020/4/12
|
||||
*/
|
||||
public interface UserInfoRepository extends JpaRepository<UserInfoDO, Long> {
|
||||
public interface UserInfoRepository extends JpaRepository<UserInfoDO, Long>, JpaSpecificationExecutor<UserInfoDO> {
|
||||
|
||||
Optional<UserInfoDO> findByUsername(String username);
|
||||
|
||||
|
@ -52,5 +52,11 @@ public interface WebAuthService {
|
||||
*/
|
||||
boolean hasPermission(RoleScope roleScope, Long target, Permission permission);
|
||||
|
||||
/**
|
||||
* 是否为全局管理员
|
||||
* @return true or false
|
||||
*/
|
||||
boolean isGlobalAdmin();
|
||||
|
||||
Map<Role, List<Long>> fetchMyPermissionTargets(RoleScope roleScope);
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import tech.powerjob.common.serialize.JsonUtils;
|
||||
import tech.powerjob.server.auth.*;
|
||||
import tech.powerjob.server.auth.common.AuthConstants;
|
||||
import tech.powerjob.server.auth.common.AuthErrorCode;
|
||||
import tech.powerjob.server.auth.common.PowerJobAuthException;
|
||||
import tech.powerjob.server.auth.service.WebAuthService;
|
||||
@ -71,6 +72,11 @@ public class WebAuthServiceImpl implements WebAuthService {
|
||||
return powerJobPermissionService.hasPermission(powerJobUser.getId(), roleScope, target, permission);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isGlobalAdmin() {
|
||||
return hasPermission(RoleScope.GLOBAL, AuthConstants.GLOBAL_ADMIN_TARGET_ID, Permission.SU);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Role, List<Long>> fetchMyPermissionTargets(RoleScope roleScope) {
|
||||
|
||||
|
@ -4,18 +4,23 @@ import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import tech.powerjob.common.exception.PowerJobException;
|
||||
import tech.powerjob.common.response.ResultDTO;
|
||||
import tech.powerjob.server.auth.Permission;
|
||||
import tech.powerjob.server.auth.PowerJobUser;
|
||||
import tech.powerjob.server.auth.Role;
|
||||
import tech.powerjob.server.auth.RoleScope;
|
||||
import tech.powerjob.server.auth.common.AuthErrorCode;
|
||||
import tech.powerjob.server.auth.common.PowerJobAuthException;
|
||||
import tech.powerjob.server.auth.interceptor.ApiPermission;
|
||||
import tech.powerjob.server.auth.service.WebAuthService;
|
||||
import tech.powerjob.server.auth.service.login.PowerJobLoginService;
|
||||
import tech.powerjob.server.common.constants.SwitchableStatus;
|
||||
import tech.powerjob.server.persistence.remote.model.AppInfoDO;
|
||||
import tech.powerjob.server.persistence.remote.model.NamespaceDO;
|
||||
import tech.powerjob.server.persistence.remote.model.UserInfoDO;
|
||||
@ -25,10 +30,12 @@ import tech.powerjob.server.persistence.remote.repository.UserInfoRepository;
|
||||
import tech.powerjob.server.web.converter.NamespaceConverter;
|
||||
import tech.powerjob.server.web.converter.UserConverter;
|
||||
import tech.powerjob.server.web.request.ModifyUserInfoRequest;
|
||||
import tech.powerjob.server.web.request.QueryUserRequest;
|
||||
import tech.powerjob.server.web.response.AppBaseVO;
|
||||
import tech.powerjob.server.web.response.NamespaceBaseVO;
|
||||
import tech.powerjob.server.web.response.UserBaseVO;
|
||||
import tech.powerjob.server.web.response.UserDetailVO;
|
||||
import tech.powerjob.server.web.service.UserWebService;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
@ -41,9 +48,14 @@ import java.util.stream.Collectors;
|
||||
* @author tjq
|
||||
* @since 2020/4/12
|
||||
*/
|
||||
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/user")
|
||||
public class UserInfoController {
|
||||
|
||||
@Resource
|
||||
private UserWebService userWebService;
|
||||
@Resource
|
||||
private UserInfoRepository userInfoRepository;
|
||||
@Resource
|
||||
@ -59,21 +71,14 @@ public class UserInfoController {
|
||||
@PostMapping("/modify")
|
||||
public ResultDTO<Void> modifyUser(@RequestBody ModifyUserInfoRequest modifyUserInfoRequest, HttpServletRequest httpServletRequest) {
|
||||
|
||||
Optional<PowerJobUser> powerJobUserOpt = powerJobLoginService.ifLogin(httpServletRequest);
|
||||
if (!powerJobUserOpt.isPresent()) {
|
||||
throw new PowerJobAuthException(AuthErrorCode.USER_NOT_LOGIN);
|
||||
}
|
||||
|
||||
Long userId = modifyUserInfoRequest.getId();
|
||||
checkModifyUserPermission(userId, httpServletRequest);
|
||||
|
||||
Optional<UserInfoDO> userOpt = userInfoRepository.findById(userId);
|
||||
if (!userOpt.isPresent()) {
|
||||
throw new IllegalArgumentException("can't find user by userId:" + userId);
|
||||
}
|
||||
|
||||
if (!Objects.equals(powerJobUserOpt.get().getId(), userId)) {
|
||||
throw new IllegalAccessException("no permission to change others user info");
|
||||
}
|
||||
|
||||
UserInfoDO dbUser = userOpt.get();
|
||||
|
||||
// 拷入允许修改的内容
|
||||
@ -111,6 +116,19 @@ public class UserInfoController {
|
||||
return ResultDTO.success(convert(result));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询用户信息(用于管理员操作,会返回敏感信息)
|
||||
* @param queryUserRequest 查询请求
|
||||
* @return 响应
|
||||
*/
|
||||
@PostMapping("/query")
|
||||
@ApiPermission(name = "User-Query", roleScope = RoleScope.GLOBAL, requiredPermission = Permission.SU)
|
||||
public ResultDTO<List<UserBaseVO>> query(@RequestBody QueryUserRequest queryUserRequest) {
|
||||
List<UserInfoDO> userInfoDos = userWebService.list(queryUserRequest);
|
||||
List<UserBaseVO> userBaseVOS = userInfoDos.stream().map(x -> UserConverter.do2BaseVo(x, true)).collect(Collectors.toList());
|
||||
return ResultDTO.success(userBaseVOS);
|
||||
}
|
||||
|
||||
@GetMapping("/detail")
|
||||
public ResultDTO<UserDetailVO> getUserDetail(HttpServletRequest httpServletRequest) {
|
||||
Optional<PowerJobUser> powerJobUserOpt = powerJobLoginService.ifLogin(httpServletRequest);
|
||||
@ -171,11 +189,62 @@ public class UserInfoController {
|
||||
return ResultDTO.success(userDetailVO);
|
||||
}
|
||||
|
||||
@PostMapping("/disable")
|
||||
public ResultDTO<Void> disableUser(Long uid, HttpServletRequest httpServletRequest) {
|
||||
changeAccountStatus(uid, SwitchableStatus.DISABLE, httpServletRequest);
|
||||
return ResultDTO.success(null);
|
||||
}
|
||||
|
||||
@PostMapping("/enable")
|
||||
public ResultDTO<Void> enableUser(Long uid, HttpServletRequest httpServletRequest) {
|
||||
changeAccountStatus(uid, SwitchableStatus.ENABLE, httpServletRequest);
|
||||
return ResultDTO.success(null);
|
||||
}
|
||||
|
||||
private void changeAccountStatus(Long uid, SwitchableStatus targetStatus, HttpServletRequest httpServletRequest) {
|
||||
checkModifyUserPermission(uid, httpServletRequest);
|
||||
|
||||
Optional<UserInfoDO> userOpt = userInfoRepository.findById(uid);
|
||||
if (!userOpt.isPresent()) {
|
||||
throw new IllegalArgumentException("can't find user by userId:" + uid);
|
||||
}
|
||||
|
||||
UserInfoDO dbUser = userOpt.get();
|
||||
|
||||
dbUser.setStatus(targetStatus.getV());
|
||||
dbUser.setGmtModified(new Date());
|
||||
|
||||
userInfoRepository.saveAndFlush(dbUser);
|
||||
log.info("[UserInfoController] changeAccountStatus, userId={},targetStatus={}", uid, targetStatus);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查针对 user 处理的权限
|
||||
* @param uid 目标 userId
|
||||
* @param httpServletRequest http 上下文请求
|
||||
*/
|
||||
private void checkModifyUserPermission(Long uid, HttpServletRequest httpServletRequest) {
|
||||
Optional<PowerJobUser> powerJobUserOpt = powerJobLoginService.ifLogin(httpServletRequest);
|
||||
if (!powerJobUserOpt.isPresent()) {
|
||||
throw new PowerJobAuthException(AuthErrorCode.USER_NOT_LOGIN);
|
||||
}
|
||||
PowerJobUser currentLoginUser = powerJobUserOpt.get();
|
||||
|
||||
boolean myself = uid.equals(currentLoginUser.getId());
|
||||
boolean globalAdmin = webAuthService.isGlobalAdmin();
|
||||
|
||||
if (myself || globalAdmin) {
|
||||
return;
|
||||
}
|
||||
|
||||
throw new PowerJobException("Only the administrator and account owner can modify the account");
|
||||
}
|
||||
|
||||
private static List<UserBaseVO> convert(List<UserInfoDO> data) {
|
||||
if (CollectionUtils.isEmpty(data)) {
|
||||
return Lists.newLinkedList();
|
||||
}
|
||||
return data.stream().map(UserConverter::do2BaseVo).collect(Collectors.toList());
|
||||
return data.stream().map(x -> UserConverter.do2BaseVo(x, false)).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private static Set<Long> mergeIds(Map<?, List<Long>> map) {
|
||||
|
@ -1,8 +1,11 @@
|
||||
package tech.powerjob.server.web.converter;
|
||||
|
||||
import tech.powerjob.server.common.constants.SwitchableStatus;
|
||||
import tech.powerjob.server.persistence.remote.model.UserInfoDO;
|
||||
import tech.powerjob.server.web.response.UserBaseVO;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* UserConverter
|
||||
*
|
||||
@ -11,13 +14,21 @@ import tech.powerjob.server.web.response.UserBaseVO;
|
||||
*/
|
||||
public class UserConverter {
|
||||
|
||||
public static UserBaseVO do2BaseVo(UserInfoDO x) {
|
||||
public static UserBaseVO do2BaseVo(UserInfoDO x, boolean includeSensitiveInfo) {
|
||||
|
||||
UserBaseVO userBaseVO = new UserBaseVO();
|
||||
|
||||
userBaseVO.setId(x.getId());
|
||||
userBaseVO.setAccountType(x.getAccountType());
|
||||
userBaseVO.setUsername(x.getUsername());
|
||||
userBaseVO.setNick(x.getNick());
|
||||
userBaseVO.setStatus(Optional.ofNullable(x.getStatus()).orElse(SwitchableStatus.ENABLE.getV()));
|
||||
userBaseVO.setEnable(userBaseVO.getStatus() == SwitchableStatus.ENABLE.getV());
|
||||
|
||||
if (includeSensitiveInfo) {
|
||||
userBaseVO.setPhone(x.getPhone());
|
||||
userBaseVO.setEmail(x.getEmail());
|
||||
}
|
||||
|
||||
userBaseVO.genShowName();
|
||||
return userBaseVO;
|
||||
|
@ -0,0 +1,32 @@
|
||||
package tech.powerjob.server.web.request;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 用户查询请求
|
||||
*
|
||||
* @author tjq
|
||||
* @since 2024/3/16
|
||||
*/
|
||||
@Data
|
||||
public class QueryUserRequest implements Serializable {
|
||||
|
||||
/**
|
||||
* 通过 userId 精确查询
|
||||
*/
|
||||
private Long userIdEq;
|
||||
|
||||
private String accountTypeEq;
|
||||
|
||||
/**
|
||||
* nick 模糊查询
|
||||
*/
|
||||
private String nickLike;
|
||||
|
||||
/**
|
||||
* 手机号模糊查询
|
||||
*/
|
||||
private String phoneLike;
|
||||
}
|
@ -15,10 +15,32 @@ import org.apache.commons.lang3.StringUtils;
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
public class UserBaseVO {
|
||||
|
||||
protected Long id;
|
||||
protected String username;
|
||||
protected String nick;
|
||||
|
||||
/**
|
||||
* 账户类型
|
||||
*/
|
||||
private String accountType;
|
||||
|
||||
/**
|
||||
* 手机号
|
||||
*/
|
||||
private String phone;
|
||||
/**
|
||||
* 邮箱地址
|
||||
*/
|
||||
private String email;
|
||||
|
||||
/**
|
||||
* 账号当前状态
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
private boolean enable;
|
||||
|
||||
/**
|
||||
* 前端展示名称,更容易辨认
|
||||
*/
|
||||
|
@ -18,23 +18,12 @@ import java.util.Map;
|
||||
@ToString
|
||||
public class UserDetailVO extends UserBaseVO {
|
||||
|
||||
/**
|
||||
* 账户类型
|
||||
*/
|
||||
private String accountType;
|
||||
|
||||
/**
|
||||
* 密码
|
||||
*/
|
||||
private String password;
|
||||
|
||||
/**
|
||||
* 手机号
|
||||
*/
|
||||
private String phone;
|
||||
/**
|
||||
* 邮箱地址
|
||||
*/
|
||||
private String email;
|
||||
/**
|
||||
* webHook
|
||||
*/
|
||||
|
@ -1,7 +1,10 @@
|
||||
package tech.powerjob.server.web.service;
|
||||
|
||||
import tech.powerjob.server.persistence.remote.model.UserInfoDO;
|
||||
import tech.powerjob.server.web.request.QueryUserRequest;
|
||||
import tech.powerjob.server.web.response.UserBaseVO;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
@ -13,4 +16,6 @@ import java.util.Optional;
|
||||
public interface UserWebService {
|
||||
|
||||
Optional<UserBaseVO> fetchBaseUserInfo(Long userId);
|
||||
|
||||
List<UserInfoDO> list(QueryUserRequest queryUserRequest);
|
||||
}
|
||||
|
@ -2,14 +2,21 @@ package tech.powerjob.server.web.service.impl;
|
||||
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.collect.Lists;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.stereotype.Service;
|
||||
import tech.powerjob.server.persistence.QueryConvertUtils;
|
||||
import tech.powerjob.server.persistence.remote.model.UserInfoDO;
|
||||
import tech.powerjob.server.persistence.remote.repository.UserInfoRepository;
|
||||
import tech.powerjob.server.web.converter.UserConverter;
|
||||
import tech.powerjob.server.web.request.QueryUserRequest;
|
||||
import tech.powerjob.server.web.response.UserBaseVO;
|
||||
import tech.powerjob.server.web.service.UserWebService;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.persistence.criteria.Predicate;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@ -50,9 +57,47 @@ public class UserWebServiceImpl implements UserWebService {
|
||||
throw new IllegalArgumentException("can't find user by userId: " + userId);
|
||||
});
|
||||
|
||||
return Optional.of(UserConverter.do2BaseVo(userInfoDO));
|
||||
return Optional.of(UserConverter.do2BaseVo(userInfoDO, false));
|
||||
} catch (Exception e) {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserInfoDO> list(QueryUserRequest q) {
|
||||
|
||||
Long userIdEq = q.getUserIdEq();
|
||||
String accountTypeEq = q.getAccountTypeEq();
|
||||
String nickLike = q.getNickLike();
|
||||
String phoneLike = q.getPhoneLike();
|
||||
|
||||
|
||||
Specification<UserInfoDO> specification = (root, query, cb) -> {
|
||||
|
||||
List<Predicate> predicates = Lists.newArrayList();
|
||||
|
||||
if (userIdEq != null) {
|
||||
predicates.add(cb.equal(root.get("id"), userIdEq));
|
||||
}
|
||||
|
||||
if (StringUtils.isNotEmpty(accountTypeEq)) {
|
||||
predicates.add(cb.equal(root.get("accountType"), accountTypeEq));
|
||||
}
|
||||
|
||||
if (StringUtils.isNotEmpty(nickLike)) {
|
||||
predicates.add(cb.like(root.get("nick"), QueryConvertUtils.convertLikeParams(nickLike)));
|
||||
}
|
||||
|
||||
if (StringUtils.isNotEmpty(phoneLike)) {
|
||||
predicates.add(cb.like(root.get("phone"), QueryConvertUtils.convertLikeParams(phoneLike)));
|
||||
}
|
||||
|
||||
if (predicates.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return query.where(predicates.toArray(new Predicate[0])).getRestriction();
|
||||
};
|
||||
|
||||
return userInfoRepository.findAll(specification);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user