SpringSecurity授权基于角色权限

wen java案例 2

本文目录导读:

SpringSecurity授权基于角色权限

  1. 核心概念
  2. 基础配置示例
  3. 数据库权限模型
  4. 方法级安全注解
  5. 动态权限控制
  6. 实际应用示例
  7. 最佳实践建议

我来详细讲解Spring Security中基于角色和权限的授权机制。

核心概念

角色(Role) vs 权限(Permission)

// 角色通常是粗粒度的权限集合
public enum Role {
    ADMIN, MANAGER, USER, GUEST
}
// 权限是细粒度的操作控制
public enum Permission {
    USER_CREATE, USER_READ, USER_UPDATE, USER_DELETE,
    ORDER_CREATE, ORDER_READ, ORDER_UPDATE, ORDER_DELETE
}

基础配置示例

内存用户配置

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
            .withUser("admin")
                .password(passwordEncoder().encode("admin123"))
                .roles("ADMIN")
                .authorities("ROLE_ADMIN", "USER_CREATE", "USER_DELETE")
            .and()
            .withUser("manager")
                .password(passwordEncoder().encode("manager123"))
                .roles("MANAGER")
                .authorities("ROLE_MANAGER", "USER_READ")
            .and()
            .withUser("user")
                .password(passwordEncoder().encode("user123"))
                .roles("USER")
                .authorities("ROLE_USER");
    }
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                // 基于角色的URL安全
                .antMatchers("/admin/**").hasRole("ADMIN")
                .antMatchers("/manager/**").hasAnyRole("ADMIN", "MANAGER")
                .antMatchers("/user/**").hasAnyRole("ADMIN", "MANAGER", "USER")
                .antMatchers("/public/**").permitAll()
                .anyRequest().authenticated()
            .and()
            .formLogin()
                .loginPage("/login")
                .permitAll()
            .and()
            .logout()
                .permitAll();
    }
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

数据库权限模型

数据库表结构

-- 用户表
CREATE TABLE users (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    username VARCHAR(50) NOT NULL UNIQUE,
    password VARCHAR(255) NOT NULL,
    enabled BOOLEAN DEFAULT true
);
-- 角色表
CREATE TABLE roles (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(50) NOT NULL UNIQUE
);
-- 权限表
CREATE TABLE permissions (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(100) NOT NULL UNIQUE
);
-- 用户-角色关联表
CREATE TABLE user_roles (
    user_id BIGINT,
    role_id BIGINT,
    PRIMARY KEY (user_id, role_id),
    FOREIGN KEY (user_id) REFERENCES users(id),
    FOREIGN KEY (role_id) REFERENCES roles(id)
);
-- 角色-权限关联表
CREATE TABLE role_permissions (
    role_id BIGINT,
    permission_id BIGINT,
    PRIMARY KEY (role_id, permission_id),
    FOREIGN KEY (role_id) REFERENCES roles(id),
    FOREIGN KEY (permission_id) REFERENCES permissions(id)
);

用户实体和权限服务

@Entity
@Table(name = "users")
public class User implements UserDetails {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    @Column(unique = true, nullable = false)
    private String username;
    @Column(nullable = false)
    private String password;
    private boolean enabled = true;
    @ManyToMany(fetch = FetchType.EAGER)
    @JoinTable(
        name = "user_roles",
        joinColumns = @JoinColumn(name = "user_id"),
        inverseJoinColumns = @JoinColumn(name = "role_id")
    )
    private Set<Role> roles = new HashSet<>();
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        Set<GrantedAuthority> authorities = new HashSet<>();
        // 添加角色(注意ROLE_前缀)
        roles.forEach(role -> {
            authorities.add(new SimpleGrantedAuthority("ROLE_" + role.getName()));
            // 添加角色对应的权限
            role.getPermissions().forEach(permission -> {
                authorities.add(new SimpleGrantedAuthority(permission.getName()));
            });
        });
        return authorities;
    }
    // 其他UserDetails方法实现...
}
@Entity
@Table(name = "roles")
public class Role {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    @ManyToMany(fetch = FetchType.EAGER)
    @JoinTable(
        name = "role_permissions",
        joinColumns = @JoinColumn(name = "role_id"),
        inverseJoinColumns = @JoinColumn(name = "permission_id")
    )
    private Set<Permission> permissions = new HashSet<>();
}
@Entity
@Table(name = "permissions")
public class Permission {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
}

自定义UserDetailsService

@Service
public class CustomUserDetailsService implements UserDetailsService {
    @Autowired
    private UserRepository userRepository;
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        return userRepository.findByUsername(username)
            .orElseThrow(() -> new UsernameNotFoundException("User not found: " + username));
    }
}

方法级安全注解

基于角色的注解

@RestController
public class UserController {
    // 只有ADMIN角色可以访问
    @GetMapping("/api/users")
    @Secured("ROLE_ADMIN")
    public List<User> getAllUsers() {
        return userService.findAll();
    }
    // ADMIN或MANAGER角色可以访问
    @GetMapping("/api/users/{id}")
    @Secured({"ROLE_ADMIN", "ROLE_MANAGER"})
    public User getUser(@PathVariable Long id) {
        return userService.findById(id);
    }
}

基于权限的注解(推荐方式)

@RestController
@RequestMapping("/api")
public class AdminController {
    @PreAuthorize("hasRole('ADMIN')")
    @PostMapping("/users")
    public User createUser(@RequestBody User user) {
        return userService.create(user);
    }
    @PreAuthorize("hasAuthority('USER_DELETE')")
    @DeleteMapping("/users/{id}")
    public void deleteUser(@PathVariable Long id) {
        userService.delete(id);
    }
    @PreAuthorize("hasAnyAuthority('USER_READ', 'USER_UPDATE')")
    @GetMapping("/users/{id}")
    public User getUser(@PathVariable Long id) {
        return userService.findById(id);
    }
    // 复杂条件表达式
    @PreAuthorize("hasRole('ADMIN') and hasAuthority('USER_CREATE')")
    @PutMapping("/users/{id}")
    public User updateUser(@PathVariable Long id, @RequestBody User user) {
        return userService.update(id, user);
    }
    // 基于对象属性的权限控制
    @PostAuthorize("returnObject.owner == authentication.name")
    @GetMapping("/documents/{id}")
    public Document getDocument(@PathVariable Long id) {
        return documentService.findById(id);
    }
}

动态权限控制

实现AccessDecisionManager

@Component
public class CustomAccessDecisionManager implements AccessDecisionManager {
    @Override
    public void decide(Authentication authentication, Object object, 
                      Collection<ConfigAttribute> configAttributes) 
                      throws AccessDeniedException, InsufficientAuthenticationException {
        if (configAttributes == null || configAttributes.isEmpty()) {
            return;
        }
        for (ConfigAttribute configAttribute : configAttributes) {
            String needAuthority = configAttribute.getAttribute();
            for (GrantedAuthority ga : authentication.getAuthorities()) {
                if (needAuthority.equals(ga.getAuthority())) {
                    return;
                }
            }
        }
        throw new AccessDeniedException("Access denied");
    }
    @Override
    public boolean supports(ConfigAttribute attribute) {
        return true;
    }
    @Override
    public boolean supports(Class<?> clazz) {
        return true;
    }
}

自定义权限评估

@Component
public class CustomPermissionEvaluator implements PermissionEvaluator {
    @Override
    public boolean hasPermission(Authentication authentication, 
                                Object targetDomainObject, 
                                Object permission) {
        if (authentication == null || !authentication.isAuthenticated()) {
            return false;
        }
        // 检查用户是否有特定权限
        if (targetDomainObject instanceof User) {
            User user = (User) targetDomainObject;
            String permissionStr = permission.toString();
            return authentication.getAuthorities()
                .stream()
                .anyMatch(a -> a.getAuthority().equals(permissionStr));
        }
        return false;
    }
    @Override
    public boolean hasPermission(Authentication authentication, 
                                Serializable targetId, 
                                String targetType, 
                                Object permission) {
        // 基于ID的权限检查
        if ("USER".equals(targetType)) {
            return hasPermission(authentication, 
                userService.findById((Long) targetId), permission);
        }
        return false;
    }
}

实际应用示例

基于RBAC的完整实现

@Service
public class RBACService {
    @PreAuthorize("hasRole('ADMIN')")
    public void assignRoleToUser(Long userId, String roleName) {
        // 分配角色给用户
    }
    @PreAuthorize("hasRole('ADMIN')")
    public void revokeRoleFromUser(Long userId, String roleName) {
        // 撤销用户角色
    }
    @PreAuthorize("hasRole('ADMIN')")
    public void grantPermissionToRole(String roleName, String permissionName) {
        // 给角色授予权限
    }
    // 检查用户是否有操作权限
    public boolean userHasPermission(Long userId, String permissionName) {
        User user = userRepository.findById(userId).orElseThrow();
        return user.getAuthorities().stream()
            .anyMatch(a -> a.getAuthority().equals(permissionName));
    }
    // 获取用户的所有角色
    public Set<String> getUserRoles(Long userId) {
        User user = userRepository.findById(userId).orElseThrow();
        return user.getRoles().stream()
            .map(Role::getName)
            .collect(Collectors.toSet());
    }
    // 获取用户的所有权限
    public Set<String> getUserPermissions(Long userId) {
        User user = userRepository.findById(userId).orElseThrow();
        Set<String> permissions = new HashSet<>();
        user.getRoles().forEach(role -> {
            role.getPermissions().forEach(permission -> {
                permissions.add(permission.getName());
            });
        });
        return permissions;
    }
}

API权限验证

@RestController
@RequestMapping("/api/v1")
public class DocumentController {
    @PreAuthorize("hasAnyRole('ADMIN', 'MANAGER')")
    @GetMapping("/documents")
    public ResponseEntity<List<Document>> getAllDocuments() {
        return ResponseEntity.ok(documentService.findAll());
    }
    @PreAuthorize("@customPermissionEvaluator.hasPermission(authentication, #id, 'DOCUMENT', 'READ')")
    @GetMapping("/documents/{id}")
    public ResponseEntity<Document> getDocument(@PathVariable Long id) {
        return ResponseEntity.ok(documentService.findById(id));
    }
    @PreAuthorize("hasAuthority('DOCUMENT_CREATE')")
    @PostMapping("/documents")
    public ResponseEntity<Document> createDocument(@RequestBody Document document) {
        return ResponseEntity.ok(documentService.create(document));
    }
    @PreAuthorize("hasAuthority('DOCUMENT_UPDATE')")
    @PutMapping("/documents/{id}")
    public ResponseEntity<Document> updateDocument(@PathVariable Long id, 
                                                  @RequestBody Document document) {
        return ResponseEntity.ok(documentService.update(id, document));
    }
    @PreAuthorize("hasAuthority('DOCUMENT_DELETE')")
    @DeleteMapping("/documents/{id}")
    public ResponseEntity<Void> deleteDocument(@PathVariable Long id) {
        documentService.delete(id);
        return ResponseEntity.ok().build();
    }
}

最佳实践建议

权限设计原则

@Component
public class PermissionConstants {
    // 命名规范:资源_操作
    public static final String USER_CREATE = "USER_CREATE";
    public static final String USER_READ = "USER_READ";
    public static final String USER_UPDATE = "USER_UPDATE";
    public static final String USER_DELETE = "USER_DELETE";
    public static final String ORDER_CREATE = "ORDER_CREATE";
    public static final String ORDER_READ = "ORDER_READ";
    public static final String ORDER_UPDATE = "ORDER_UPDATE";
    // 角色前缀
    public static final String ROLE_PREFIX = "ROLE_";
}

安全审计

@Aspect
@Component
public class SecurityAuditAspect {
    @AfterReturning("@annotation(preAuthorize)")
    public void auditAccess(JoinPoint joinPoint, PreAuthorize preAuthorize) {
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        AuditLog auditLog = new AuditLog();
        auditLog.setUsername(auth.getName());
        auditLog.setMethod(joinPoint.getSignature().getName());
        auditLog.setExpression(preAuthorize.value());
        auditLog.setTimestamp(LocalDateTime.now());
        auditLog.setSuccess(true);
        auditLogRepository.save(auditLog);
    }
}

基于角色和权限的授权机制是Spring Security的核心功能,通过合理的权限设计和配置,可以实现灵活、安全的访问控制。

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