Java案例如何实现权限管理?

wen python案例 4

本文目录导读:

Java案例如何实现权限管理?

  1. 核心思想与组件
  2. 数据库表设计(五张核心表)
  3. Java代码实战(基于 Spring Boot + Spring Security)
  4. 完整的权限判断流程图
  5. 项目中的最佳实践建议
  6. 简单示例(权限数据初始化)

在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 禁止访问]

项目中的最佳实践建议

  1. 权限粒度:建议使用权限字符串(如 order:update)而非硬编码URL,因为URL可能会变,权限字符串更稳定。
  2. 缓存:用户每次请求都查数据库很慢,通常的做法是:
    • 用户登录成功后,一次性查询该用户所有的权限字符串。
    • 存入 Redis 或 Session 中(键:user:permissions:{userId},值:Set<String>)。
    • 后续鉴权时,直接从缓存中取数据判断。
  3. Spring Security + JWT 整合
    • JWT Token 中只存 userIdusername
    • 每次请求在 Filter 中解析 Token,获取 userId
    • 然后从 Redis 中取出该用户的权限列表,构造 UsernamePasswordAuthenticationToken(存入 GrantedAuthority 列表)。
    • Spring Security 会自动根据 hasAuthority() 进行匹配。
  4. 树形菜单:如果涉及前端菜单权限,后端返回的权限列表通常是一个树形结构,前端根据 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中实现权限管理,最核心的三件事是:

  1. 设计好数据模型(用户、角色、权限的关联关系)。
  2. 用户登录后加载权限到Security上下文中
  3. 在接口调用点(AOP注解或过滤器)对比用户权限与接口所需权限

推荐使用 Spring Security + RBAC + 权限字符串 的组合,这是目前Java生态中最成熟、最灵活的方案。

抱歉,评论功能暂时关闭!