Spring Security 的授权机制与安全表达式
# Spring Security 的授权机制与安全表达式
前言
在 Spring Security 中,授权机制是核心功能之一,通过基于角色和权限的控制,可以确保只有经过认证的用户才能访问特定的资源。除了路径权限控制,Spring Security 还支持基于注解的授权控制、方法级别的安全控制以及自定义的权限判断逻辑。
# 1. 基于角色的权限控制
Spring Security 提供了多种注解用于基于角色和权限的控制,最常用的是 @PreAuthorize
和 @Secured
。
# 1.1 @PreAuthorize 注解
@PreAuthorize
是 Spring Security 提供的注解,用于在方法执行前进行权限检查。它支持使用 SpEL(Spring Expression Language)表达式进行权限判断。
代码示例:
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;
@Service
public class MyService {
@PreAuthorize("hasRole('ADMIN')") // 只有 ADMIN 角色的用户可以访问此方法
public void adminMethod() {
// 管理员权限才能执行的逻辑
}
@PreAuthorize("hasAnyRole('USER', 'ADMIN')") // USER 或 ADMIN 角色的用户可以访问此方法
public void userOrAdminMethod() {
// 用户或管理员都可以执行的逻辑
}
@PreAuthorize("hasAuthority('READ_PRIVILEGE')") // 具有特定权限的用户可以访问此方法
public void readPrivilegeMethod() {
// 具有读取权限的用户才能执行的逻辑
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
重要参数说明:
hasRole('ROLE_NAME')
:检查用户是否具有指定角色。Spring Security 会为角色自动添加ROLE_
前缀,所以hasRole('ADMIN')
实际上匹配的是ROLE_ADMIN
。hasAnyRole('ROLE1', 'ROLE2')
:检查用户是否具有多个角色中的任意一个。hasAuthority('AUTHORITY_NAME')
:检查用户是否具有指定权限,与角色不同,权限更细粒度。
使用场景: 当需要对服务层方法进行权限控制时,通过 @PreAuthorize
可以方便地实现基于角色和权限的访问控制。
# 1.2 @Secured 注解
@Secured
是另一个用于角色控制的注解,与 @PreAuthorize
类似,但它不支持 SpEL 表达式,只能用于简单的角色检查。
代码示例:
import org.springframework.security.access.annotation.Secured;
import org.springframework.stereotype.Service;
@Service
public class MyService {
@Secured("ROLE_ADMIN") // 只有 ADMIN 角色的用户可以访问此方法
public void adminMethod() {
// 管理员权限才能执行的逻辑
}
@Secured({"ROLE_USER", "ROLE_ADMIN"}) // USER 或 ADMIN 角色的用户可以访问此方法
public void userOrAdminMethod() {
// 用户或管理员都可以执行的逻辑
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
重要参数说明:
@Secured
:指定允许访问该方法的角色。支持单个角色或多个角色,多个角色时用数组表示。
使用场景: 适用于只需要简单角色控制的场景,尤其是无需复杂权限表达式的场合。
# 2. 方法级安全控制
除了在路径上进行权限控制外,Spring Security 还支持在方法级别进行权限控制,特别是在服务层或业务逻辑层,通过注解可以实现细粒度的访问控制。
# 2.1 启用方法级安全控制
要使用方法级别的安全控制,需要在配置类中启用相关功能:
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true) // 启用 @PreAuthorize 和 @Secured 注解
public class SecurityConfig extends WebSecurityConfigurerAdapter {
// 其他安全配置
}
2
3
4
5
6
7
8
9
重要参数说明:
prePostEnabled = true
:启用@PreAuthorize
和@PostAuthorize
注解,用于方法级别的权限控制。securedEnabled = true
:启用@Secured
注解,用于方法级别的角色控制。
# 2.2 方法级权限控制的使用场景
- 服务层安全:在服务层方法上使用注解可以确保只有具备特定角色或权限的用户才能执行关键业务逻辑。
- 业务逻辑权限细化:例如,一个用户只有在具备特定权限时才能访问某些功能,而不仅仅是基于路径的权限控制。
# 3. 自定义权限判断逻辑
在实际应用中,可能需要根据复杂的业务逻辑进行权限判断,这时可以自定义权限表达式。
# 3.1 使用 SpEL 表达式进行权限判断
@PreAuthorize
支持使用 SpEL 表达式来实现复杂的权限控制逻辑。
代码示例:
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;
@Service
public class MyService {
@PreAuthorize("#username == authentication.name") // 仅允许当前用户访问自己的信息
public void accessOwnData(String username) {
// 只有当前用户可以访问自己的数据
}
@PreAuthorize("#user.id == principal.id or hasRole('ADMIN')") // 用户可以访问自己的数据,管理员可以访问所有数据
public void accessData(User user) {
// 复杂的权限控制逻辑
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
重要参数说明:
#username == authentication.name
:比较方法参数和当前认证对象的用户名,确保用户只能访问自己的信息。principal.id
:principal
是当前认证对象,可以通过它获取用户的详细信息。
# 3.2 自定义权限表达式
可以通过自定义 PermissionEvaluator
或 MethodSecurityExpressionHandler
实现更复杂的权限控制。
自定义 PermissionEvaluator
示例:
import org.springframework.security.access.PermissionEvaluator;
import org.springframework.security.core.Authentication;
import java.io.Serializable;
public class CustomPermissionEvaluator implements PermissionEvaluator {
@Override
public boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) {
// 实现自定义权限判断逻辑
if (targetDomainObject instanceof Document) {
Document document = (Document) targetDomainObject;
return document.getOwner().equals(authentication.getName()) || authentication.getAuthorities().contains(new SimpleGrantedAuthority("ROLE_ADMIN"));
}
return false;
}
@Override
public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission) {
// 另一种方式的权限判断
return false;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
配置自定义权限表达式:
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
expressionHandler.setPermissionEvaluator(new CustomPermissionEvaluator());
return expressionHandler;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
使用场景: 当权限控制逻辑涉及复杂的业务规则(如多重条件判断)时,可以通过自定义权限表达式实现。
总结
- 基于角色的权限控制:通过
@PreAuthorize
和@Secured
注解可以实现路径和方法级别的角色和权限控制,适用于简单到中等复杂度的权限需求。 - 方法级安全控制:启用方法级安全后,可以对服务层方法进行精细化权限控制,确保业务逻辑的安全性。
- 自定义权限判断逻辑:对于复杂的业务权限需求,可以通过 SpEL 表达式和自定义权限表达式实现灵活的权限控制。