fix:角色改密
This commit is contained in:
@@ -93,6 +93,7 @@ public class WebMvcConfig implements WebMvcConfigurer {
|
||||
.addPathPatterns("/api/**")
|
||||
.excludePathPatterns(
|
||||
"/api/v1/device/status",
|
||||
"/api/v1/masterKey/status",
|
||||
"/api/v1/auth/password-login",
|
||||
"/api/v1/auth/ukey-login",
|
||||
"/api/v1/auth/ukey-login/randoms",
|
||||
|
||||
@@ -4,6 +4,7 @@ import com.cisd.tms.common.api.ApiResponse;
|
||||
import com.cisd.tms.common.enums.ErrorCode;
|
||||
import com.cisd.tms.common.exception.BizException;
|
||||
import com.cisd.tms.modules.auth.dto.AdminChangePasswordRequest;
|
||||
import com.cisd.tms.modules.auth.dto.AuthUserListResponse;
|
||||
import com.cisd.tms.modules.auth.enums.AuthLevel;
|
||||
import com.cisd.tms.modules.auth.enums.RoleCode;
|
||||
import com.cisd.tms.modules.auth.security.RequireInternalAuth;
|
||||
@@ -25,25 +26,11 @@ import org.springframework.web.bind.annotation.*;
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/auth")
|
||||
@RequiredArgsConstructor
|
||||
@Tag(name = "认证管理", description = "角色启用、密码重置与 UKey 绑定接口")
|
||||
@Tag(name = "认证管理", description = "角色密码重置、改密与 UKey 绑定接口")
|
||||
public class AuthAdminController {
|
||||
|
||||
private final AuthAdminService authAdminService;
|
||||
|
||||
@PostMapping("/roles/{roleCode}/enable")
|
||||
@Operation(summary = "启用角色", description = "仅允许 SUPER_ADMIN FULL 会话启用目标角色。")
|
||||
@ReplayProtected
|
||||
@RequireInternalAuth(role = RoleCode.SUPER_ADMIN, authLevel = AuthLevel.FULL)
|
||||
@AuditedOperation(module = ModuleCode.AUTH, action = ActionType.ENABLE, summary = "启用角色")
|
||||
public ApiResponse<Void> enableRole(@PathVariable("roleCode") String roleCode, HttpServletRequest request) {
|
||||
authAdminService.enableRole(
|
||||
(String) request.getAttribute(InternalApiAuthInterceptor.ATTR_ROLE_CODE),
|
||||
(String) request.getAttribute(InternalApiAuthInterceptor.ATTR_AUTH_LEVEL),
|
||||
roleCode
|
||||
);
|
||||
return ApiResponse.success();
|
||||
}
|
||||
|
||||
@PostMapping("/roles/{roleCode}/reset-password")
|
||||
@Operation(summary = "重置角色密码", description = "仅允许 SUPER_ADMIN FULL 会话重置目标角色密码。")
|
||||
@ReplayProtected
|
||||
@@ -58,6 +45,18 @@ public class AuthAdminController {
|
||||
return ApiResponse.success();
|
||||
}
|
||||
|
||||
@GetMapping("/users")
|
||||
@Operation(summary = "查询用户管理列表", description = "按角色类型和状态分页查询角色席位账号列表。")
|
||||
// @RequireInternalAuth(role = RoleCode.SUPER_ADMIN, authLevel = AuthLevel.FULL)
|
||||
public ApiResponse<AuthUserListResponse> listUsers(
|
||||
@RequestParam(value = "roleCode", required = false) String roleCode,
|
||||
@RequestParam(value = "status", required = false) String status,
|
||||
@RequestParam(value = "page", defaultValue = "1") int page,
|
||||
@RequestParam(value = "pageSize", defaultValue = "10") int pageSize
|
||||
) {
|
||||
return ApiResponse.success(authAdminService.listUsers(roleCode, status, page, pageSize));
|
||||
}
|
||||
|
||||
@PostMapping("/roles/{roleCode}/accounts/{uid}/change-password")
|
||||
@Operation(summary = "管理员修改角色席位口令", description = "管理员为目标角色指定席位账号设置新口令。")
|
||||
@ReplayProtected
|
||||
|
||||
@@ -11,5 +11,7 @@ public interface AuthFullAccountMapper extends BaseMapperX<AuthFullAccountEntity
|
||||
|
||||
AuthFullAccountEntity selectByRoleCodeAndUid(@Param("roleCode") String roleCode, @Param("uid") Integer uid);
|
||||
|
||||
List<AuthFullAccountEntity> selectAllAccounts();
|
||||
|
||||
List<AuthFullAccountEntity> selectByRoleCode(@Param("roleCode") String roleCode);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.cisd.tms.modules.auth.mapper;
|
||||
|
||||
import com.cisd.tms.infrastructure.persistence.mapper.BaseMapperX;
|
||||
import com.cisd.tms.modules.auth.entity.RoleAccountEntity;
|
||||
import java.util.List;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
@@ -9,4 +10,6 @@ import org.apache.ibatis.annotations.Param;
|
||||
public interface RoleAccountMapper extends BaseMapperX<RoleAccountEntity> {
|
||||
|
||||
RoleAccountEntity selectByRoleCode(@Param("roleCode") String roleCode);
|
||||
|
||||
List<RoleAccountEntity> selectAllRoles();
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@ public interface AuthFullAccountRepository {
|
||||
|
||||
Optional<AuthFullAccountEntity> findByRoleCodeAndUid(String roleCode, Integer uid);
|
||||
|
||||
List<AuthFullAccountEntity> findAll();
|
||||
|
||||
List<AuthFullAccountEntity> findByRoleCode(String roleCode);
|
||||
|
||||
void save(AuthFullAccountEntity entity);
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
package com.cisd.tms.modules.auth.repository;
|
||||
|
||||
import com.cisd.tms.modules.auth.entity.RoleAccountEntity;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface RoleAccountRepository {
|
||||
|
||||
Optional<RoleAccountEntity> findByRoleCode(String roleCode);
|
||||
|
||||
List<RoleAccountEntity> findAll();
|
||||
|
||||
void save(RoleAccountEntity entity);
|
||||
|
||||
void update(RoleAccountEntity entity);
|
||||
|
||||
+5
@@ -21,6 +21,11 @@ public class AuthFullAccountRepositoryImpl implements AuthFullAccountRepository
|
||||
return Optional.ofNullable(authFullAccountMapper.selectByRoleCodeAndUid(roleCode, uid));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AuthFullAccountEntity> findAll() {
|
||||
return authFullAccountMapper.selectAllAccounts();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AuthFullAccountEntity> findByRoleCode(String roleCode) {
|
||||
return authFullAccountMapper.selectByRoleCode(roleCode);
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.cisd.tms.modules.auth.repository.impl;
|
||||
import com.cisd.tms.modules.auth.entity.RoleAccountEntity;
|
||||
import com.cisd.tms.modules.auth.mapper.RoleAccountMapper;
|
||||
import com.cisd.tms.modules.auth.repository.RoleAccountRepository;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@@ -20,6 +21,11 @@ public class RoleAccountRepositoryImpl implements RoleAccountRepository {
|
||||
return Optional.ofNullable(roleAccountMapper.selectByRoleCode(roleCode));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RoleAccountEntity> findAll() {
|
||||
return roleAccountMapper.selectAllRoles();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(RoleAccountEntity entity) {
|
||||
roleAccountMapper.insert(entity);
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
package com.cisd.tms.modules.auth.service;
|
||||
|
||||
import com.cisd.tms.modules.auth.dto.AuthUserListResponse;
|
||||
import com.cisd.tms.modules.mk.dto.UKeySignDTO;
|
||||
import com.cisd.tms.modules.mk.dto.UKeySignResult;
|
||||
|
||||
public interface AuthAdminService {
|
||||
|
||||
void enableRole(String operatorRoleCode, String operatorAuthLevel, String targetRoleCode);
|
||||
|
||||
void resetPassword(String operatorRoleCode, String operatorAuthLevel, String targetRoleCode);
|
||||
|
||||
AuthUserListResponse listUsers(String roleCode, String status, int page, int pageSize);
|
||||
|
||||
void changeAccountPassword(
|
||||
String operatorRoleCode,
|
||||
String operatorAuthLevel,
|
||||
|
||||
@@ -3,6 +3,8 @@ package com.cisd.tms.modules.auth.service.impl;
|
||||
import com.cisd.tms.common.enums.ErrorCode;
|
||||
import com.cisd.tms.common.exception.BizException;
|
||||
import com.cisd.tms.common.util.TraceIdUtil;
|
||||
import com.cisd.tms.modules.auth.dto.AuthUserListItemResponse;
|
||||
import com.cisd.tms.modules.auth.dto.AuthUserListResponse;
|
||||
import com.cisd.tms.modules.auth.entity.AuthFullAccountEntity;
|
||||
import com.cisd.tms.modules.auth.entity.RoleAccountEntity;
|
||||
import com.cisd.tms.modules.auth.entity.RoleUkeyBindingEntity;
|
||||
@@ -25,8 +27,13 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import java.time.Clock;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@@ -45,8 +52,26 @@ public class AuthAdminServiceImpl implements AuthAdminService {
|
||||
private final Clock clock;
|
||||
private final PasswordSaltGenerator passwordSaltGenerator;
|
||||
|
||||
@Override
|
||||
public void enableRole(String operatorRoleCode, String operatorAuthLevel, String targetRoleCode) {
|
||||
private void enableRoleAfterUkeyBindingIfReady(RoleCode targetRole) {
|
||||
if (!hasRequiredActiveUkeyBindings(targetRole)) {
|
||||
return;
|
||||
}
|
||||
RoleAccountEntity target = loadRole(targetRole.getCode());
|
||||
if (RoleAccountStatus.ACTIVE.name().equals(target.getStatus())) {
|
||||
return;
|
||||
}
|
||||
activateRoleAndSeatAccounts(targetRole.getCode());
|
||||
}
|
||||
|
||||
private boolean hasRequiredActiveUkeyBindings(RoleCode targetRole) {
|
||||
return roleUkeyBindingRepository.findActiveByRoleCode(targetRole.getCode()).stream()
|
||||
.map(RoleUkeyBindingEntity::getUid)
|
||||
.filter(Objects::nonNull)
|
||||
.distinct()
|
||||
.count() >= targetRole.getRequiredUkeyCount();
|
||||
}
|
||||
|
||||
private void activateRoleAndSeatAccounts(String targetRoleCode) {
|
||||
RoleAccountEntity target = loadRole(targetRoleCode);
|
||||
target.setStatus(RoleAccountStatus.ACTIVE.name());
|
||||
roleAccountRepository.update(target);
|
||||
@@ -64,7 +89,6 @@ public class AuthAdminServiceImpl implements AuthAdminService {
|
||||
account.setLastLoginAt(null);
|
||||
authFullAccountRepository.update(account);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -94,6 +118,35 @@ public class AuthAdminServiceImpl implements AuthAdminService {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthUserListResponse listUsers(String roleCode, String status, int page, int pageSize) {
|
||||
int normalizedPage = Math.max(page, 1);
|
||||
int normalizedPageSize = Math.min(Math.max(pageSize, 1), 100);
|
||||
Map<String, RoleAccountEntity> rolesByCode = roleAccountRepository.findAll().stream()
|
||||
.collect(Collectors.toMap(RoleAccountEntity::getRoleCode, item -> item, (left, right) -> left));
|
||||
List<AuthFullAccountEntity> accounts = authFullAccountRepository.findAll();
|
||||
|
||||
String normalizedRoleFilter = normalizeRoleFilter(roleCode, rolesByCode);
|
||||
String normalizedStatusFilter = normalizeStatusFilter(status);
|
||||
List<AuthUserListItemResponse> filteredItems = accounts.stream()
|
||||
.map(account -> toUserListItem(account, rolesByCode.get(account.getRoleCode())))
|
||||
.filter(item -> normalizedRoleFilter == null || normalizedRoleFilter.equals(item.getRoleCode()))
|
||||
.filter(item -> normalizedStatusFilter == null || normalizedStatusFilter.equals(item.getStatus()))
|
||||
.sorted(Comparator
|
||||
.comparingInt((AuthUserListItemResponse item) -> roleOrder(item.getRoleCode()))
|
||||
.thenComparing(AuthUserListItemResponse::getUid, Comparator.nullsLast(Integer::compareTo)))
|
||||
.toList();
|
||||
|
||||
int fromIndex = Math.min((normalizedPage - 1) * normalizedPageSize, filteredItems.size());
|
||||
int toIndex = Math.min(fromIndex + normalizedPageSize, filteredItems.size());
|
||||
AuthUserListResponse response = new AuthUserListResponse();
|
||||
response.setPage(normalizedPage);
|
||||
response.setPageSize(normalizedPageSize);
|
||||
response.setTotal(filteredItems.size());
|
||||
response.setItems(new ArrayList<>(filteredItems.subList(fromIndex, toIndex)));
|
||||
return response;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changeAccountPassword(
|
||||
String operatorRoleCode,
|
||||
@@ -118,6 +171,103 @@ public class AuthAdminServiceImpl implements AuthAdminService {
|
||||
authFullAccountRepository.update(account);
|
||||
}
|
||||
|
||||
private AuthUserListItemResponse toUserListItem(AuthFullAccountEntity account, RoleAccountEntity role) {
|
||||
List<String> ukeySerials = roleUkeyBindingRepository
|
||||
.findActiveByRoleCodeAndUid(account.getRoleCode(), account.getUid())
|
||||
.stream()
|
||||
.map(RoleUkeyBindingEntity::getUkeySerial)
|
||||
.filter(Objects::nonNull)
|
||||
.distinct()
|
||||
.toList();
|
||||
String effectiveStatus = effectiveStatus(role, account);
|
||||
AuthUserListItemResponse item = new AuthUserListItemResponse();
|
||||
item.setRoleCode(account.getRoleCode());
|
||||
item.setRoleName(role == null ? roleDisplayName(account.getRoleCode()) : role.getDisplayName());
|
||||
item.setUid(account.getUid());
|
||||
item.setUsername(account.getAccountName());
|
||||
item.setDisplayName(account.getDisplayName());
|
||||
item.setStatus(effectiveStatus);
|
||||
item.setStatusName(statusName(effectiveStatus));
|
||||
item.setLastLoginAt(account.getLastLoginAt());
|
||||
item.setPasswordChangedAt(account.getPasswordChangedAt());
|
||||
item.setFailedCount(account.getFailedCount() == null ? 0 : account.getFailedCount());
|
||||
item.setUkeySerials(ukeySerials);
|
||||
item.setUkeySerialNo(String.join(",", ukeySerials));
|
||||
return item;
|
||||
}
|
||||
|
||||
private String effectiveStatus(RoleAccountEntity role, AuthFullAccountEntity account) {
|
||||
if (role != null && !RoleAccountStatus.ACTIVE.name().equals(role.getStatus())) {
|
||||
return role.getStatus();
|
||||
}
|
||||
return account.getStatus();
|
||||
}
|
||||
|
||||
private String normalizeRoleFilter(String roleFilter, Map<String, RoleAccountEntity> rolesByCode) {
|
||||
if (roleFilter == null || roleFilter.isBlank()) {
|
||||
return null;
|
||||
}
|
||||
String value = roleFilter.trim();
|
||||
for (RoleCode roleCode : RoleCode.values()) {
|
||||
if (roleCode.getCode().equals(value) || roleCode.getDisplayName().equals(value)) {
|
||||
return roleCode.getCode();
|
||||
}
|
||||
}
|
||||
return rolesByCode.values().stream()
|
||||
.filter(role -> value.equals(role.getDisplayName()))
|
||||
.map(RoleAccountEntity::getRoleCode)
|
||||
.findFirst()
|
||||
.orElse(value);
|
||||
}
|
||||
|
||||
private String normalizeStatusFilter(String status) {
|
||||
if (status == null || status.isBlank()) {
|
||||
return null;
|
||||
}
|
||||
String value = status.trim();
|
||||
if (Set.of("ACTIVE", "NORMAL", "正常").contains(value)) {
|
||||
return RoleAccountStatus.ACTIVE.name();
|
||||
}
|
||||
if (Set.of("LOCKED", "锁定").contains(value)) {
|
||||
return RoleAccountStatus.LOCKED.name();
|
||||
}
|
||||
if (Set.of("UNENABLED", "DISABLED", "未启用").contains(value)) {
|
||||
return RoleAccountStatus.UNENABLED.name();
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
private String statusName(String status) {
|
||||
if (RoleAccountStatus.ACTIVE.name().equals(status)) {
|
||||
return "正常";
|
||||
}
|
||||
if (RoleAccountStatus.LOCKED.name().equals(status)) {
|
||||
return "锁定";
|
||||
}
|
||||
if (RoleAccountStatus.UNENABLED.name().equals(status)) {
|
||||
return "未启用";
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
private String roleDisplayName(String roleCode) {
|
||||
return List.of(RoleCode.values()).stream()
|
||||
.filter(item -> item.getCode().equals(roleCode))
|
||||
.map(RoleCode::getDisplayName)
|
||||
.findFirst()
|
||||
.orElse(roleCode);
|
||||
}
|
||||
|
||||
private int roleOrder(String roleCode) {
|
||||
RoleCode[] values = RoleCode.values();
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
if (values[i].getCode().equals(roleCode)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return values.length;
|
||||
}
|
||||
|
||||
private void bindUkey(
|
||||
String targetRoleCode,
|
||||
Integer uid,
|
||||
@@ -188,6 +338,7 @@ public class AuthAdminServiceImpl implements AuthAdminService {
|
||||
request.getPubKey(),
|
||||
signValue
|
||||
);
|
||||
enableRoleAfterUkeyBindingIfReady(targetRole);
|
||||
return UKeySignResult.builder()
|
||||
.sign(signValue)
|
||||
.backupPacket(backupPacket)
|
||||
|
||||
@@ -377,7 +377,7 @@ VALUES
|
||||
'SUPER_ADMIN',
|
||||
'超级管理员',
|
||||
2,
|
||||
'UNENABLED',
|
||||
'ACTIVE',
|
||||
CURRENT_TIMESTAMP(3),
|
||||
CURRENT_TIMESTAMP(3)
|
||||
),
|
||||
|
||||
@@ -46,6 +46,27 @@
|
||||
LIMIT 1
|
||||
</select>
|
||||
|
||||
<select id="selectAllAccounts" resultMap="AuthFullAccountResultMap">
|
||||
SELECT id,
|
||||
role_code,
|
||||
uid,
|
||||
account_name,
|
||||
display_name,
|
||||
password_hash,
|
||||
password_salt,
|
||||
status,
|
||||
need_change_password,
|
||||
password_changed_at,
|
||||
failed_count,
|
||||
locked_until,
|
||||
last_login_at,
|
||||
last_active_at,
|
||||
create_time,
|
||||
update_time
|
||||
FROM tms_auth_full_account
|
||||
ORDER BY role_code ASC, uid ASC
|
||||
</select>
|
||||
|
||||
<select id="selectByRoleCode" resultMap="AuthFullAccountResultMap">
|
||||
SELECT id,
|
||||
role_code,
|
||||
|
||||
@@ -26,4 +26,16 @@
|
||||
WHERE role_code = #{roleCode}
|
||||
LIMIT 1
|
||||
</select>
|
||||
|
||||
<select id="selectAllRoles" resultMap="RoleAccountResultMap">
|
||||
SELECT id,
|
||||
role_code,
|
||||
display_name,
|
||||
required_ukey_count,
|
||||
status,
|
||||
create_time,
|
||||
update_time
|
||||
FROM tms_role_account
|
||||
ORDER BY role_code ASC
|
||||
</select>
|
||||
</mapper>
|
||||
|
||||
Reference in New Issue
Block a user