本文目录导读:

我来详细讲解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的核心功能,通过合理的权限设计和配置,可以实现灵活、安全的访问控制。