本文目录导读:

在Java中实现权限管理,通常有基于角色(RBAC)、基于资源(URL/方法)和基于属性(ABAC)几种模式,最经典且应用最广泛的是RBAC (Role-Based Access Control)。
下面我会从核心组件、数据库设计、代码实现(结合Spring Security)和核心判断逻辑四个部分,给出一个可落地的案例方案。
核心思想与组件
- 用户 (User):系统的操作者。
- 角色 (Role):权限的集合(如:管理员、普通用户)。
- 权限 (Permission):具体的操作能力(如:用户创建、订单查询、删除文章)。
- 资源/接口:被保护的对象(如:
/api/order/delete)。
判断逻辑: 用户 → 拥有 → 角色 → 拥有 → 权限 → 允许访问 → 资源
数据库表设计(五张核心表)
这是RBAC的经典设计,支持多对多关系。
-- 1. 用户表
CREATE TABLE sys_user (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) NOT NULL UNIQUE,
password VARCHAR(100) NOT NULL,
status INT DEFAULT 1 -- 1正常 0禁用
);
-- 2. 角色表
CREATE TABLE sys_role (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
role_name VARCHAR(50) NOT NULL, -- 如: ADMINISTRATOR
role_desc VARCHAR(100)
);
-- 3. 权限表(菜单/按钮/接口)
CREATE TABLE sys_permission (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
perms VARCHAR(100) NOT NULL, -- 权限标识符,如: user:create, user:delete
name VARCHAR(50), -- 权限名称
url VARCHAR(200), -- 对应资源路径(可选)
type INT -- 1菜单 2按钮 3接口
);
-- 4. 用户-角色 关联表
CREATE TABLE sys_user_role (
user_id BIGINT NOT NULL,
role_id BIGINT NOT NULL,
PRIMARY KEY (user_id, role_id)
);
-- 5. 角色-权限 关联表
CREATE TABLE sys_role_permission (
role_id BIGINT NOT NULL,
permission_id BIGINT NOT NULL,
PRIMARY KEY (role_id, permission_id)
);
Java代码实战(基于 Spring Boot + Spring Security)
1 实体对象(简化版)
// 用户实体
@Data
public class SysUser {
private Long id;
private String username;
private String password;
private Integer status;
}
// 角色实体
@Data
public class SysRole {
private Long id;
private String roleName; // ADMIN, USER
}
// 权限实体(核心)
@Data
public class SysPermission {
private Long id;
private String perms; // user:add 或 sys:user:delete
private String url;
}
2 核心权限判断工具类
这个类负责判断「当前用户是否拥有某个权限字符串」。
import org.springframework.stereotype.Component;
@Component("permissionChecker") // 给一个Bean名称,方便在表达式中调用
public class PermissionChecker {
/**
* 判断用户是否有某个权限
* @param permission 权限标识,如 "user:delete"
* @return 是否拥有
*/
public boolean hasPermission(String permission) {
// 1. 获取当前登录用户(通过SecurityContextHolder)
// 2. 从用户信息中提取权限集合
// 3. 检查是否包含该权限
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null && authentication.getPrincipal() instanceof UserDetails) {
UserDetails userDetails = (UserDetails) authentication.getPrincipal();
// 假设我们在用户Details中存了权限列表
// 或者从角色中查询
return userDetails.getAuthorities().stream()
.anyMatch(grantedAuthority -> grantedAuthority.getAuthority().equals(permission));
// 更常见的做法是:从redis或数据库中查
// List<String> perms = permissionService.getAllPermsByUserId(userId);
// return perms.contains(permission);
}
return false;
}
}
3 拦截器或注解方式实现权限校验
使用Spring Security的 @PreAuthorize 注解(推荐)
在Controller方法上直接声明需要的权限。
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/user")
public class UserController {
/**
* 删除用户 - 需要 user:delete 权限
*/
@DeleteMapping("/{id}")
@PreAuthorize("hasAuthority('user:delete')") // 直接检查权限字符串
// 或者使用自定义Bean: @PreAuthorize("@permissionChecker.hasPermission('user:delete')")
public Result deleteUser(@PathVariable Long id) {
// 业务逻辑删除
return Result.success();
}
/**
* 创建用户 - 需要 user:create 权限
*/
@PostMapping
@PreAuthorize("hasAuthority('user:create')")
public Result createUser(@RequestBody User user) {
return Result.success();
}
/**
* 查看用户列表 - 登录即可(仅认证,不鉴权)
*/
@GetMapping("/list")
@PreAuthorize("isAuthenticated()")
public Result listUsers() {
return Result.success();
}
}
自定义权限注解
如果需要更复杂的业务逻辑(如:只能删除自己的文章),可以自定义注解。
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface CheckPermission {
String value(); // 权限标识
}
// 在AOP中实现
@Aspect
@Component
public class PermissionAspect {
@Around("@annotation(checkPermission)")
public Object around(ProceedingJoinPoint joinPoint, CheckPermission checkPermission) throws Throwable {
String permission = checkPermission.value();
// 调用上面写的 PermissionChecker 判断
if (!permissionChecker.hasPermission(permission)) {
throw new AccessDeniedException("无权限");
}
return joinPoint.proceed();
}
}
4 实现动态URL拦截(过滤器的方式)
如果不想在每个方法上加注解,也可以配置一个过滤器,根据请求的URL去数据库查询该URL需要哪些权限。
@Component
public class DynamicUrlFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain chain)
throws ServletException, IOException {
String requestUri = request.getRequestURI();
String method = request.getMethod();
// 1. 查数据库,获取当前URL需要的权限 (sys_permission表)
// SELECT perms FROM sys_permission WHERE url = ?
// 或者维护一个 Map<URL, String> 缓存
// 2. 如果不需要权限,直接放行
// 3. 如果需要权限,检查当前登录用户是否拥有该权限
// 从SecurityContextHolder获取用户权限列表
// 判断是否包含
// 4. 有权限:放行;无权限:返回403
}
}
完整的权限判断流程图
graph TD
A[用户请求 /api/user/delete] --> B{是否已登录?}
B -- 否 --> C[返回401]
B -- 是 --> D{是否有角色?}
D -- 是 --> E[查询角色关联的权限集合]
E --> F{是否包含 user:delete ?}
F -- 是 --> G[允许访问]
F -- 否 --> H[返回403 禁止访问]
项目中的最佳实践建议
- 权限粒度:建议使用权限字符串(如
order:update)而非硬编码URL,因为URL可能会变,权限字符串更稳定。 - 缓存:用户每次请求都查数据库很慢,通常的做法是:
- 用户登录成功后,一次性查询该用户所有的权限字符串。
- 存入 Redis 或 Session 中(键:
user:permissions:{userId},值:Set<String>)。 - 后续鉴权时,直接从缓存中取数据判断。
- Spring Security + JWT 整合:
- JWT Token 中只存
userId和username。 - 每次请求在 Filter 中解析 Token,获取
userId。 - 然后从 Redis 中取出该用户的权限列表,构造
UsernamePasswordAuthenticationToken(存入GrantedAuthority列表)。 - Spring Security 会自动根据
hasAuthority()进行匹配。
- JWT Token 中只存
- 树形菜单:如果涉及前端菜单权限,后端返回的权限列表通常是一个树形结构,前端根据
perms字段控制菜单显示/隐藏。
简单示例(权限数据初始化)
// 假设用户 admin (角色: ROLE_ADMIN)
// 角色 ROLE_ADMIN 拥有的权限:
// - user:create
// - user:delete
// - user:update
// - order:view
// 用户 zhangsan (角色: ROLE_USER)
// 角色 ROLE_USER 拥有的权限:
// - order:view
// 当 admin 访问 /api/user/delete -> @PreAuthorize("hasAuthority('user:delete')") -> true -> 成功
// 当 zhangsan 访问 /api/user/delete -> @PreAuthorize("hasAuthority('user:delete')") -> false -> 403
在Java中实现权限管理,最核心的三件事是:
- 设计好数据模型(用户、角色、权限的关联关系)。
- 用户登录后加载权限到Security上下文中。
- 在接口调用点(AOP注解或过滤器)对比用户权限与接口所需权限。
推荐使用 Spring Security + RBAC + 权限字符串 的组合,这是目前Java生态中最成熟、最灵活的方案。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播