Skip to content

权限控制 RBAC

一、RBAC 模型

基本概念

┌─────────────────────────────────────────────────────────────────┐
│                       RBAC 模型                                   │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│                      ┌──────────┐                                │
│                      │   用户    │                              │
│                      └─────┬────┘                                │
│                            │                                     │
│                      ┌─────┴─────┐                              │
│                      │ 用户-角色  │                              │
│                      │   (M:N)   │                              │
│                      └─────┬─────┘                              │
│                            │                                     │
│                      ┌─────┴─────┐                              │
│                      │   角色     │                              │
│                      └─────┬─────┘                              │
│                            │                                     │
│                      ┌─────┴─────┐                              │
│                      │ 角色-权限  │                              │
│                      │   (M:N)   │                              │
│                      └─────┬─────┘                              │
│                            │                                     │
│                      ┌─────┴─────┐                              │
│                      │   权限     │                              │
│                      └───────────┘                              │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

数据库设计

sql
-- 用户表
CREATE TABLE users (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    username VARCHAR(50) UNIQUE NOT NULL,
    password VARCHAR(100) NOT NULL,
    enabled BOOLEAN DEFAULT TRUE
);

-- 角色表
CREATE TABLE roles (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(50) UNIQUE NOT NULL,
    description VARCHAR(100)
);

-- 权限表
CREATE TABLE permissions (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(50) UNIQUE NOT NULL,
    description VARCHAR(100),
    url VARCHAR(200),        -- 资源路径
    method VARCHAR(10)       -- 请求方法
);

-- 用户-角色关联表
CREATE TABLE user_roles (
    user_id BIGINT,
    role_id BIGINT,
    PRIMARY KEY (user_id, role_id)
);

-- 角色-权限关联表
CREATE TABLE role_permissions (
    role_id BIGINT,
    permission_id BIGINT,
    PRIMARY KEY (role_id, permission_id)
);

二、Spring Security 集成

UserDetailsService 实现

java
@Service
public class CustomUserDetailsService implements UserDetailsService {
    
    @Autowired
    private UserRepository userRepository;
    
    @Autowired
    private RoleRepository roleRepository;
    
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userRepository.findByUsername(username)
            .orElseThrow(() -> new UsernameNotFoundException("用户不存在"));
        
        List<Role> roles = roleRepository.findByUserId(user.getId());
        
        List<GrantedAuthority> authorities = roles.stream()
            .map(role -> new SimpleGrantedAuthority("ROLE_" + role.getName()))
            .collect(Collectors.toList());
        
        return new org.springframework.security.core.userdetails.User(
            user.getUsername(),
            user.getPassword(),
            user.getEnabled(),
            true, true, true,
            authorities
        );
    }
}

动态权限配置

java
@Configuration
@EnableWebSecurity
public class SecurityConfig {
    
    @Autowired
    private CustomUserDetailsService userDetailsService;
    
    @Autowired
    private PermissionService permissionService;
    
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/login", "/register", "/public/**").permitAll()
                .requestMatchers("/admin/**").hasRole("ADMIN")
                .anyRequest().authenticated()
            )
            .formLogin(form -> form
                .loginProcessingUrl("/login")
                .successHandler((req, res, auth) -> {
                    res.setContentType("application/json");
                    res.getWriter().write("{\"code\":0,\"message\":\"登录成功\"}");
                })
            );
        
        return http.build();
    }
}

三、方法级权限

@PreAuthorize

java
@Service
public class UserService {
    
    @PreAuthorize("hasRole('ADMIN')")
    public void deleteUser(Long id) {
        userRepository.deleteById(id);
    }
    
    @PreAuthorize("hasAuthority('USER_READ') or hasRole('ADMIN')")
    public User getUser(Long id) {
        return userRepository.findById(id);
    }
    
    // 复杂表达式
    @PreAuthorize("#userId == authentication.principal.id or hasRole('ADMIN')")
    public void updateUser(Long userId, User user) {
        // ...
    }
}

自定义权限表达式

java
@Component
public class CustomPermissionEvaluator implements PermissionEvaluator {
    
    @Autowired
    private PermissionService permissionService;
    
    @Override
    public boolean hasPermission(Authentication auth, Object targetDomainObject, Object permission) {
        if (auth == null) return false;
        
        String username = auth.getName();
        String permissionName = (String) permission;
        
        return permissionService.hasPermission(username, targetDomainObject, permissionName);
    }
    
    @Override
    public boolean hasPermission(Authentication auth, Serializable targetId, 
                                String targetType, Object permission) {
        if (auth == null) return false;
        
        String username = auth.getName();
        return permissionService.hasPermission(username, targetType, targetId, permission);
    }
}

// 配置
@EnableMethodSecurity(prePostEnabled = true)
public class MethodSecurityConfig {
    
    @Autowired
    private CustomPermissionEvaluator permissionEvaluator;
    
    @Bean
    public MethodSecurityExpressionHandler expressionHandler() {
        DefaultMethodSecurityExpressionHandler handler = new DefaultMethodSecurityExpressionHandler();
        handler.setPermissionEvaluator(permissionEvaluator);
        return handler;
    }
}

四、按钮级权限

前端方案

html
<!-- Thymeleaf -->
<div sec:authorize="hasRole('ADMIN')">
    <button>删除</button>
</div>

<!-- Vue -->
<el-button v-if="$hasPermission('USER_DELETE')">删除</el-button>

自定义注解

java
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequirePermission {
    String value();
}

// 切面实现
@Aspect
@Component
public class PermissionAspect {
    
    @Autowired
    private PermissionService permissionService;
    
    @Around("@annotation(requirePermission)")
    public Object checkPermission(ProceedingJoinPoint joinPoint, 
                                 RequirePermission requirePermission) throws Throwable {
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        
        if (!permissionService.hasPermission(auth.getName(), requirePermission.value())) {
            throw new SecurityException("没有权限: " + requirePermission.value());
        }
        
        return joinPoint.proceed();
    }
}

五、面试高频问题

Q1: RBAC 是什么?

基于角色的访问控制(Role-Based Access Control),通过角色连接用户和权限

Q2: hasRole 和 hasAuthority 区别?

  • hasRole('ADMIN')hasAuthority('ROLE_ADMIN')(自动加前缀)
  • hasAuthority('USER_READ') → 直接匹配权限

Q3: 如何动态更新权限?

将权限数据缓存到 Redis,修改后刷新缓存

六、下一章预告

下一章我们将学习 OAuth2 第三方登录

  • OAuth2 流程
  • 授权码模式
  • 第三方登录集成

基于 MIT 许可发布