Java案例如何实现权限控制?

wen java案例 15

本文目录导读:

Java案例如何实现权限控制?

  1. 基于RBAC(基于角色的访问控制)
  2. 基于Spring Security + JWT的权限控制
  3. 基于注解的细粒度权限控制(方法级别)
  4. 基于过滤器链的URL级别权限控制
  5. 基于ABAC(基于属性的访问控制)
  6. 最佳实践建议
  7. 实际项目案例结构

在Java中实现权限控制,通常有几种主流方案,具体选择取决于你的应用类型(单体应用、微服务、分布式系统)和安全级别需求,以下是几种最常用的实现方式及案例:

基于RBAC(基于角色的访问控制)

这是最常见也是最成熟的权限控制模型。

核心思想:

  • 用户(User):系统的使用者
  • 角色(Role):权限的集合(如管理员、普通用户)
  • 权限(Permission):具体的操作许可(如创建文章、删除用户)

数据结构设计:

-- 用户表
CREATE TABLE `sys_user` (
    `id` BIGINT PRIMARY KEY AUTO_INCREMENT,
    `username` VARCHAR(50) NOT NULL,
    `password` VARCHAR(100) NOT NULL
);
-- 角色表
CREATE TABLE `sys_role` (
    `id` BIGINT PRIMARY KEY AUTO_INCREMENT,
    `role_name` VARCHAR(50) NOT NULL,
    `role_code` VARCHAR(50) NOT NULL UNIQUE
);
-- 权限表
CREATE TABLE `sys_permission` (
    `id` BIGINT PRIMARY KEY AUTO_INCREMENT,
    `perm_name` VARCHAR(50) NOT NULL,
    `perm_code` VARCHAR(50) NOT NULL UNIQUE  -- 如 "user:create"
);
-- 用户-角色关系表
CREATE TABLE `sys_user_role` (
    `user_id` BIGINT,
    `role_id` BIGINT,
    PRIMARY KEY (`user_id`, `role_id`)
);
-- 角色-权限关系表
CREATE TABLE `sys_role_permission` (
    `role_id` BIGINT,
    `permission_id` BIGINT,
    PRIMARY KEY (`role_id`, `permission_id`)
);

Spring Security + RBAC 实现示例:

@Configuration
@EnableWebSecurity
public class SecurityConfig {
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(auth -> auth
                // 基于权限码控制
                .requestMatchers("/api/user/create").hasAuthority("user:create")
                .requestMatchers("/api/user/delete").hasAuthority("user:delete")
                // 基于角色控制(注意角色需要加ROLE_前缀)
                .requestMatchers("/api/admin/**").hasRole("ADMIN")
                .anyRequest().authenticated()
            )
            .formLogin(Customizer.withDefaults());
        return http.build();
    }
}

基于Spring Security + JWT的权限控制

适用于前后端分离的单体或微服务架构。

实现流程:

// 1. JWT Token工具类
@Component
public class JwtTokenUtil {
    public String generateToken(UserDetails userDetails) {
        Map<String, Object> claims = new HashMap<>();
        // 将用户的角色和权限加入JWT
        claims.put("roles", userDetails.getAuthorities());
        return Jwts.builder()
            .setClaims(claims)
            .setSubject(userDetails.getUsername())
            .setIssuedAt(new Date())
            .setExpiration(new Date(System.currentTimeMillis() + 3600000))
            .signWith(SignatureAlgorithm.HS512, "secret")
            .compact();
    }
}
// 2. JWT认证过滤器
public class JwtAuthenticationFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request, 
                                   HttpServletResponse response, 
                                   FilterChain chain) {
        String token = request.getHeader("Authorization");
        if (token != null) {
            // 解析token并设置SecurityContext
            Authentication auth = jwtTokenUtil.parseToken(token);
            SecurityContextHolder.getContext().setAuthentication(auth);
        }
        chain.doFilter(request, response);
    }
}

基于注解的细粒度权限控制(方法级别)

使用Spring的@PreAuthorize注解实现方法级别的权限控制:

@RestController
@RequestMapping("/api/users")
public class UserController {
    // 基于角色控制
    @PreAuthorize("hasRole('ADMIN')")
    @GetMapping("/all")
    public List<User> getAllUsers() {
        return userService.findAll();
    }
    // 基于权限码控制
    @PreAuthorize("hasAuthority('user:create')")
    @PostMapping
    public User createUser(@RequestBody User user) {
        return userService.create(user);
    }
    // 基于表达式控制(用户只能操作自己的数据)
    @PreAuthorize("#id == authentication.principal.id or hasRole('ADMIN')")
    @PutMapping("/{id}")
    public User updateUser(@PathVariable Long id, @RequestBody User user) {
        return userService.update(id, user);
    }
}
// 启用方法级别的安全控制
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MethodSecurityConfig {
    // 配置类
}

基于过滤器链的URL级别权限控制

适用于前后端分离的项目,在网关或应用层进行统一拦截:

@Component
public class PermissionFilter implements Filter {
    @Autowired
    private PermissionService permissionService;
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, 
                        FilterChain chain) {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        String requestURI = httpRequest.getRequestURI();
        String method = httpRequest.getMethod();
        // 获取当前用户权限
        User currentUser = getCurrentUser();
        // 检查用户是否有访问该URL的权限
        if (permissionService.checkPermission(currentUser, method, requestURI)) {
            chain.doFilter(request, response);
        } else {
            // 返回403禁止访问
            response.getWriter().write("Access Denied");
        }
    }
}

基于ABAC(基于属性的访问控制)

适用于更复杂的业务场景,支持基于用户属性、环境属性、资源属性进行动态决策:

public class ABACPolicyEngine {
    public boolean evaluate(Policy policy, Subject subject, Resource resource, Environment env) {
        // 示例规则:只有创建者或管理员可以删除文档
        return policy.getRules().stream().anyMatch(rule -> {
            if (rule.getAction().equals("delete_document")) {
                // 检查用户是否为管理员
                boolean isAdmin = subject.hasRole("ADMIN");
                // 检查用户是否为文档创建者
                boolean isOwner = resource.getAttribute("ownerId").equals(subject.getId());
                return isAdmin || isOwner;
            }
            return false;
        });
    }
}

最佳实践建议

  1. 选择合适粒度

    • URL级别:适合统一的权限拦截,性能好
    • 方法级别:更精细的控制,适合复杂业务
    • 数据级别:控制到具体的数据行(如用户只能看自己创建的数据)
  2. 缓存策略

    @Service
    public class PermissionService {
     @Cacheable(value = "userPermissions", key = "#userId")
     public Set<String> getUserPermissions(Long userId) {
         // 此处查询数据库获取用户所有权限
         return permissionDAO.findByUserId(userId);
     }
    }
  3. 性能优化

    • 权限数据使用Redis缓存
    • 使用Bloom Filter进行快速权限判断
    • 避免在循环中重复查询权限
  4. 安全考虑

    • 前端只显示用户有权限的菜单/按钮(体验优化,但不可作为安全依据)
    • 后端必须做全面的权限校验(前端限制不安全)
    • 防止权限绕过(如修改URL参数、篡改请求方式等)

实际项目案例结构

// 一个完整的权限校验服务
@Service
public class AccessControlService {
    // 检查是否有某个操作权限
    public boolean hasPermission(String permissionCode) {
        // 获取当前登录用户
        User currentUser = getCurrentUser();
        // 超级管理员拥有所有权限
        if (currentUser.isSuperAdmin()) {
            return true;
        }
        // 从缓存或数据库获取用户权限列表
        Set<String> permissions = getUserPermissions(currentUser.getId());
        return permissions.contains(permissionCode);
    }
    // 检查是否拥有某个角色
    public boolean hasRole(String roleCode) {
        User currentUser = getCurrentUser();
        return currentUser.getRoles().stream()
            .anyMatch(role -> role.getRoleCode().equals(roleCode));
    }
    // 检查是否拥有所有指定权限(AND逻辑)
    public boolean hasAllPermissions(String... permissionCodes) {
        return Arrays.stream(permissionCodes)
            .allMatch(this::hasPermission);
    }
    // 检查是否拥有任一指定权限(OR逻辑)
    public boolean hasAnyPermission(String... permissionCodes) {
        return Arrays.stream(permissionCodes)
            .anyMatch(this::hasPermission);
    }
}

选择哪种方式取决于你的具体需求:如果是简单的管理后台,RBAC通常足够;如果是复杂的多租户SaaS系统,可能需要ABAC;如果追求开发效率,直接用Spring Security的注解方式即可,建议从简单开始,逐步演进,避免过度设计。

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