You've already forked DataMate
feat(auth): 角色管理CRUD与角色权限绑定功能
Some checks failed
Some checks failed
新增角色创建/编辑/删除接口和角色-权限绑定接口,支持管理员自定义角色并灵活配置权限。 前端新增角色CRUD弹窗、按模块分组的权限配置面板,内置角色禁止删除但允许编辑和配置权限。 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -10,6 +10,7 @@ import com.datamate.common.auth.interfaces.rest.dto.AuthCurrentUserResponse;
|
||||
import com.datamate.common.auth.interfaces.rest.dto.AuthLoginResponse;
|
||||
import com.datamate.common.auth.interfaces.rest.dto.AuthUserView;
|
||||
import com.datamate.common.auth.interfaces.rest.dto.AuthUserWithRolesResponse;
|
||||
import com.datamate.common.auth.interfaces.rest.dto.RoleWithPermissionsResponse;
|
||||
import com.datamate.common.infrastructure.exception.BusinessAssert;
|
||||
import com.datamate.common.security.JwtUtils;
|
||||
import io.jsonwebtoken.Claims;
|
||||
@@ -17,6 +18,7 @@ import io.jsonwebtoken.JwtException;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
@@ -26,6 +28,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
@@ -129,6 +132,56 @@ public class AuthApplicationService {
|
||||
authMapper.insertUserRoles(userId, new ArrayList<>(distinctRoleIds));
|
||||
}
|
||||
|
||||
public AuthRoleInfo createRole(String roleCode, String roleName, String description) {
|
||||
AuthRoleInfo existing = authMapper.findRoleByCode(roleCode);
|
||||
BusinessAssert.isTrue(existing == null, AuthErrorCode.ROLE_CODE_DUPLICATE);
|
||||
|
||||
String id = UUID.randomUUID().toString();
|
||||
authMapper.insertRole(id, roleCode, roleName, description);
|
||||
return authMapper.findRoleById(id);
|
||||
}
|
||||
|
||||
public AuthRoleInfo updateRole(String roleId, String roleName, String description, Boolean enabled) {
|
||||
AuthRoleInfo role = authMapper.findRoleById(roleId);
|
||||
BusinessAssert.notNull(role, AuthErrorCode.ROLE_NOT_FOUND);
|
||||
|
||||
authMapper.updateRole(roleId, roleName, description, enabled);
|
||||
return authMapper.findRoleById(roleId);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void deleteRole(String roleId) {
|
||||
AuthRoleInfo role = authMapper.findRoleById(roleId);
|
||||
BusinessAssert.notNull(role, AuthErrorCode.ROLE_NOT_FOUND);
|
||||
BusinessAssert.isTrue(!Boolean.TRUE.equals(role.getIsBuiltIn()), AuthErrorCode.ROLE_DELETE_BUILT_IN);
|
||||
|
||||
authMapper.deleteRolePermissions(roleId);
|
||||
authMapper.deleteUserRolesByRoleId(roleId);
|
||||
authMapper.deleteRoleById(roleId);
|
||||
}
|
||||
|
||||
public RoleWithPermissionsResponse getRoleWithPermissions(String roleId) {
|
||||
AuthRoleInfo role = authMapper.findRoleById(roleId);
|
||||
BusinessAssert.notNull(role, AuthErrorCode.ROLE_NOT_FOUND);
|
||||
|
||||
List<String> permissionIds = authMapper.findPermissionIdsByRoleId(roleId);
|
||||
return new RoleWithPermissionsResponse(role, permissionIds);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void bindRolePermissions(String roleId, List<String> permissionIds) {
|
||||
AuthRoleInfo role = authMapper.findRoleById(roleId);
|
||||
BusinessAssert.notNull(role, AuthErrorCode.ROLE_NOT_FOUND);
|
||||
|
||||
authMapper.deleteRolePermissions(roleId);
|
||||
if (permissionIds != null && !permissionIds.isEmpty()) {
|
||||
Set<String> distinctIds = new LinkedHashSet<>(permissionIds);
|
||||
int existingCount = authMapper.countPermissionsByIds(new ArrayList<>(distinctIds));
|
||||
BusinessAssert.isTrue(existingCount == distinctIds.size(), AuthErrorCode.PERMISSION_NOT_FOUND);
|
||||
authMapper.insertRolePermissions(roleId, new ArrayList<>(distinctIds));
|
||||
}
|
||||
}
|
||||
|
||||
private String buildToken(AuthBundle authBundle) {
|
||||
Map<String, Object> claims = Map.of(
|
||||
"userId", authBundle.user().getId(),
|
||||
|
||||
@@ -14,5 +14,6 @@ public class AuthRoleInfo {
|
||||
private String roleName;
|
||||
private String description;
|
||||
private Boolean enabled;
|
||||
private Boolean isBuiltIn;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,10 @@ public enum AuthErrorCode implements ErrorCode {
|
||||
TOKEN_INVALID("auth.0003", "登录状态已失效"),
|
||||
USER_NOT_FOUND("auth.0004", "用户不存在"),
|
||||
ROLE_NOT_FOUND("auth.0005", "角色不存在"),
|
||||
AUTHORIZATION_DENIED("auth.0006", "无权限执行该操作");
|
||||
AUTHORIZATION_DENIED("auth.0006", "无权限执行该操作"),
|
||||
ROLE_CODE_DUPLICATE("auth.0007", "角色编码已存在"),
|
||||
ROLE_DELETE_BUILT_IN("auth.0008", "内置角色不允许删除"),
|
||||
PERMISSION_NOT_FOUND("auth.0009", "权限不存在");
|
||||
|
||||
private final String code;
|
||||
private final String message;
|
||||
|
||||
@@ -35,5 +35,27 @@ public interface AuthMapper {
|
||||
int deleteUserRoles(@Param("userId") Long userId);
|
||||
|
||||
int insertUserRoles(@Param("userId") Long userId, @Param("roleIds") List<String> roleIds);
|
||||
|
||||
AuthRoleInfo findRoleById(@Param("roleId") String roleId);
|
||||
|
||||
AuthRoleInfo findRoleByCode(@Param("roleCode") String roleCode);
|
||||
|
||||
int insertRole(@Param("id") String id, @Param("roleCode") String roleCode,
|
||||
@Param("roleName") String roleName, @Param("description") String description);
|
||||
|
||||
int updateRole(@Param("roleId") String roleId, @Param("roleName") String roleName,
|
||||
@Param("description") String description, @Param("enabled") Boolean enabled);
|
||||
|
||||
int deleteRoleById(@Param("roleId") String roleId);
|
||||
|
||||
List<String> findPermissionIdsByRoleId(@Param("roleId") String roleId);
|
||||
|
||||
int deleteRolePermissions(@Param("roleId") String roleId);
|
||||
|
||||
int insertRolePermissions(@Param("roleId") String roleId, @Param("permissionIds") List<String> permissionIds);
|
||||
|
||||
int countPermissionsByIds(@Param("permissionIds") List<String> permissionIds);
|
||||
|
||||
int deleteUserRolesByRoleId(@Param("roleId") String roleId);
|
||||
}
|
||||
|
||||
|
||||
@@ -7,12 +7,17 @@ import com.datamate.common.auth.interfaces.rest.dto.AssignUserRolesRequest;
|
||||
import com.datamate.common.auth.interfaces.rest.dto.AuthCurrentUserResponse;
|
||||
import com.datamate.common.auth.interfaces.rest.dto.AuthLoginResponse;
|
||||
import com.datamate.common.auth.interfaces.rest.dto.AuthUserWithRolesResponse;
|
||||
import com.datamate.common.auth.interfaces.rest.dto.BindRolePermissionsRequest;
|
||||
import com.datamate.common.auth.interfaces.rest.dto.CreateRoleRequest;
|
||||
import com.datamate.common.auth.interfaces.rest.dto.LoginRequest;
|
||||
import com.datamate.common.auth.interfaces.rest.dto.RoleWithPermissionsResponse;
|
||||
import com.datamate.common.auth.interfaces.rest.dto.UpdateRoleRequest;
|
||||
import com.datamate.common.infrastructure.exception.BusinessAssert;
|
||||
import com.datamate.common.auth.infrastructure.exception.AuthErrorCode;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
@@ -69,6 +74,33 @@ public class AuthController {
|
||||
return authApplicationService.listPermissions();
|
||||
}
|
||||
|
||||
@PostMapping("/roles")
|
||||
public AuthRoleInfo createRole(@RequestBody @Valid CreateRoleRequest request) {
|
||||
return authApplicationService.createRole(request.roleCode(), request.roleName(), request.description());
|
||||
}
|
||||
|
||||
@PutMapping("/roles/{roleId}")
|
||||
public AuthRoleInfo updateRole(@PathVariable("roleId") String roleId,
|
||||
@RequestBody @Valid UpdateRoleRequest request) {
|
||||
return authApplicationService.updateRole(roleId, request.roleName(), request.description(), request.enabled());
|
||||
}
|
||||
|
||||
@DeleteMapping("/roles/{roleId}")
|
||||
public void deleteRole(@PathVariable("roleId") String roleId) {
|
||||
authApplicationService.deleteRole(roleId);
|
||||
}
|
||||
|
||||
@GetMapping("/roles/{roleId}/permissions")
|
||||
public RoleWithPermissionsResponse getRolePermissions(@PathVariable("roleId") String roleId) {
|
||||
return authApplicationService.getRoleWithPermissions(roleId);
|
||||
}
|
||||
|
||||
@PutMapping("/roles/{roleId}/permissions")
|
||||
public void bindRolePermissions(@PathVariable("roleId") String roleId,
|
||||
@RequestBody @Valid BindRolePermissionsRequest request) {
|
||||
authApplicationService.bindRolePermissions(roleId, request.permissionIds());
|
||||
}
|
||||
|
||||
private String extractBearerToken(String authorizationHeader) {
|
||||
BusinessAssert.isTrue(
|
||||
authorizationHeader != null && authorizationHeader.startsWith("Bearer "),
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.datamate.common.auth.interfaces.rest.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 角色权限绑定请求
|
||||
*/
|
||||
public record BindRolePermissionsRequest(
|
||||
@NotNull(message = "权限列表不能为null") List<String> permissionIds
|
||||
) {
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.datamate.common.auth.interfaces.rest.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
|
||||
/**
|
||||
* 创建角色请求
|
||||
*/
|
||||
public record CreateRoleRequest(
|
||||
@NotBlank(message = "角色编码不能为空") String roleCode,
|
||||
@NotBlank(message = "角色名称不能为空") String roleName,
|
||||
String description
|
||||
) {
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.datamate.common.auth.interfaces.rest.dto;
|
||||
|
||||
import com.datamate.common.auth.domain.model.AuthRoleInfo;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 角色及其权限响应
|
||||
*/
|
||||
public record RoleWithPermissionsResponse(
|
||||
AuthRoleInfo role,
|
||||
List<String> permissionIds
|
||||
) {
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.datamate.common.auth.interfaces.rest.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
|
||||
/**
|
||||
* 更新角色请求
|
||||
*/
|
||||
public record UpdateRoleRequest(
|
||||
@NotBlank(message = "角色名称不能为空") String roleName,
|
||||
String description,
|
||||
Boolean enabled
|
||||
) {
|
||||
}
|
||||
@@ -73,10 +73,11 @@
|
||||
|
||||
<select id="listRoles" resultType="com.datamate.common.auth.domain.model.AuthRoleInfo">
|
||||
SELECT id,
|
||||
role_code AS roleCode,
|
||||
role_name AS roleName,
|
||||
role_code AS roleCode,
|
||||
role_name AS roleName,
|
||||
description,
|
||||
enabled
|
||||
enabled,
|
||||
is_built_in AS isBuiltIn
|
||||
FROM t_auth_roles
|
||||
ORDER BY role_code ASC
|
||||
</select>
|
||||
@@ -116,5 +117,80 @@
|
||||
(#{userId}, #{roleId})
|
||||
</foreach>
|
||||
</insert>
|
||||
|
||||
<select id="findRoleById" resultType="com.datamate.common.auth.domain.model.AuthRoleInfo">
|
||||
SELECT id,
|
||||
role_code AS roleCode,
|
||||
role_name AS roleName,
|
||||
description,
|
||||
enabled,
|
||||
is_built_in AS isBuiltIn
|
||||
FROM t_auth_roles
|
||||
WHERE id = #{roleId}
|
||||
LIMIT 1
|
||||
</select>
|
||||
|
||||
<select id="findRoleByCode" resultType="com.datamate.common.auth.domain.model.AuthRoleInfo">
|
||||
SELECT id,
|
||||
role_code AS roleCode,
|
||||
role_name AS roleName,
|
||||
description,
|
||||
enabled,
|
||||
is_built_in AS isBuiltIn
|
||||
FROM t_auth_roles
|
||||
WHERE role_code = #{roleCode}
|
||||
LIMIT 1
|
||||
</select>
|
||||
|
||||
<insert id="insertRole">
|
||||
INSERT INTO t_auth_roles (id, role_code, role_name, description, is_built_in, enabled)
|
||||
VALUES (#{id}, #{roleCode}, #{roleName}, #{description}, 0, 1)
|
||||
</insert>
|
||||
|
||||
<update id="updateRole">
|
||||
UPDATE t_auth_roles
|
||||
SET role_name = #{roleName},
|
||||
description = #{description},
|
||||
enabled = #{enabled}
|
||||
WHERE id = #{roleId}
|
||||
</update>
|
||||
|
||||
<delete id="deleteRoleById">
|
||||
DELETE FROM t_auth_roles
|
||||
WHERE id = #{roleId}
|
||||
</delete>
|
||||
|
||||
<select id="findPermissionIdsByRoleId" resultType="string">
|
||||
SELECT permission_id
|
||||
FROM t_auth_role_permissions
|
||||
WHERE role_id = #{roleId}
|
||||
</select>
|
||||
|
||||
<delete id="deleteRolePermissions">
|
||||
DELETE FROM t_auth_role_permissions
|
||||
WHERE role_id = #{roleId}
|
||||
</delete>
|
||||
|
||||
<insert id="insertRolePermissions">
|
||||
INSERT INTO t_auth_role_permissions (role_id, permission_id)
|
||||
VALUES
|
||||
<foreach collection="permissionIds" item="permissionId" separator=",">
|
||||
(#{roleId}, #{permissionId})
|
||||
</foreach>
|
||||
</insert>
|
||||
|
||||
<select id="countPermissionsByIds" resultType="int">
|
||||
SELECT COUNT(1)
|
||||
FROM t_auth_permissions
|
||||
WHERE id IN
|
||||
<foreach collection="permissionIds" item="permissionId" open="(" separator="," close=")">
|
||||
#{permissionId}
|
||||
</foreach>
|
||||
</select>
|
||||
|
||||
<delete id="deleteUserRolesByRoleId">
|
||||
DELETE FROM t_auth_user_roles
|
||||
WHERE role_id = #{roleId}
|
||||
</delete>
|
||||
</mapper>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user