// 注解作用域
@Retention(RetentionPolicy.RUNTIME)
// 使用位置
@Target(ElementType.METHOD)
// 权限校验注解
@PreAuthorize("hasAuthority('SCOPE_message:read')")
public @interface HasMessageRead {} // 定义一个注解
@PreAuthorize("hasAuthority('SCOPE_{scope}')")
X.509 身份验证过滤器用于基于 X.509 证书对客户端进行身份验证。X.509 是一种广泛使用的公钥基础设施(PKI)标准,用于创建和管理数字证书
Servlet 身份验证架构
SecurityContextHolder——是Spring Security 存储经过身份验证的SecurityContextHolder人员的详细信息的地方。
SecurityContext - 从中获得SecurityContextHolder并包含Authentication当前经过身份验证的用户的。
身份验证——可以是输入,以AuthenticationManager提供用户提供的用于身份验证的凭据或当前用户的凭据SecurityContext。
GrantedAuthority-授予主体的权限Authentication(即角色、范围等)
AuthenticationManager——定义 Spring Security 的过滤器如何执行身份验证的API 。
ProviderManager——最常见的实现AuthenticationManager。
AuthenticationProvider - 用于ProviderManager执行特定类型的身份验证。
请求凭证AuthenticationEntryPoint- 用于从客户端请求凭证(即重定向到登录页面、发送响应WWW-Authenticate等)
AbstractAuthenticationProcessingFilter - 用于身份验证的基础Filter。这也很好地说明了身份验证的高级流程以及各部分如何协同工作。
核心逻辑使用
SecurityContext context = SecurityContextHolder.createEmptyContext();
Authentication authentication =
new TestingAuthenticationToken("username", "password", "ROLE_USER");
context.setAuthentication(authentication);
SecurityContextHolder.setContext(context);
Authentication接口在 Spring Security 中有两个主要用途:
AuthenticationManager提供用户提供的用于验证身份的凭据的输入。在此场景中使用时,isAuthenticated()返回false。
表示当前已认证的用户。你可以Authentication从SecurityContext获取当前用户。
包含Authentication:
principal:标识用户。使用用户名/密码进行身份验证时,这通常是 的一个实例UserDetails。
credentials:通常是密码。在很多情况下,用户认证后会清除密码,以确保不泄露。
authorities:GrantedAuthority实例是授予用户的高级权限。两个示例是角色和范围。
GrantedAuthority实例是授予用户的高级权限。两个示例是角色和范围。
您可以GrantedAuthority从Authentication.getAuthorities()方法中获取实例。此方法提供Collection对象GrantedAuthority。GrantedAuthority毫无疑问,是授予主体的权限。此类权限通常是“角色”,例如ROLE_ADMINISTRATOR或ROLE_HR_SUPERVISOR。这些角色稍后将配置为 Web 授权、方法授权和域对象授权。Spring Security 的其他部分将解释这些权限并期望它们存在。当使用基于用户名/密码的身份验证时,GrantedAuthority实例通常由 加载UserDetailsService。
通常,GrantedAuthority对象是应用程序范围的权限。它们并不特定于给定的域对象。因此,您不太可能拥有GrantedAuthority表示对对象编号 54 的权限Employee,因为如果有数千个这样的权限,您将很快耗尽内存(或者,至少会导致应用程序花费很长时间来验证用户身份)。当然,Spring Security 专门设计用于处理这种常见需求,但您应该使用项目的域对象安全功能来实现此目的。
AuthenticationManager是定义 Spring Security 的过滤器如何执行身份验证的 API 。Authentication返回的 随后由调用 的控制器(即Spring Security 的实例)在SecurityContextHolder上设置。如果您未与 Spring Security 的实例集成,则可以直接设置,而不必使用。FiltersAuthenticationManagerFiltersSecurityContextHolderAuthenticationManager
虽然的实现AuthenticationManager可以是任何东西,但最常见的实现是ProviderManager。
权限
Spring Security 提供了强大的权限和访问控制机制,可以很好地处理应用程序范围的权限。对于具体的域对象(如对特定 Employee 的访问权限),Spring Security 推荐使用域对象安全功能,而不是直接使用 GrantedAuthority。这是因为 GrantedAuthority 通常用于定义全局的角色和权限,而不是特定对象的权限。
Spring Security 的处理机制
全局权限控制(GrantedAuthority):
GrantedAuthority 对象用于定义应用程序范围内的权限和角色。通常,这些权限不针对特定域对象,而是针对整个应用的功能进行控制。
例如,ROLE_ADMIN 和 ROLE_USER 是典型的 GrantedAuthority,用于控制用户是否有管理权限或普通用户权限。
域对象安全(Domain Object Security):
对于需要细粒度控制的场景,比如对特定资源(如某个特定的 Employee)的访问权限,Spring Security 提供了域对象安全的功能。
通过使用方法安全性和 ACL(Access Control List),可以实现对具体对象的权限控制。
域对象安全的实现
- 方法级别安全性
通过方法级别的安全注解(如 @PreAuthorize 和 @PostAuthorize),可以基于业务逻辑进行权限控制。例如,检查用户是否有权访问特定对象。
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;
@Service
public class EmployeeService {
@PreAuthorize("hasPermission(#id, 'com.example.Employee', 'read')")
public Employee getEmployeeById(Long id) {
// 获取并返回 Employee 对象
}
}
在上面的例子中,@PreAuthorize 注解用于在方法执行前检查用户是否有权限读取特定的 Employee 对象。
- 使用 ACL(访问控制列表)
Spring Security 提供了一个 ACL 模块,可以用于细粒度的域对象访问控制。ACL 允许你为每个域对象指定权限,并将这些权限与用户或角色关联。
配置 ACL:
引入依赖:
xml
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-acl</artifactId>
</dependency>
配置 ACL 数据源:
配置 DataSource、JdbcMutableAclService 等必要的 Bean。
@Bean
public LookupStrategy lookupStrategy(DataSource dataSource) {
return new BasicLookupStrategy(dataSource, aclCache(), aclAuthorizationStrategy(), new ConsoleAuditLogger());
}
@Bean
public JdbcMutableAclService aclService(DataSource dataSource) {
return new JdbcMutableAclService(dataSource, lookupStrategy(dataSource), aclCache());
}
@Bean
public AclCache aclCache() {
return new EhCacheBasedAclCache(ehCacheFactoryBean().getObject(), permissionGrantingStrategy(), aclAuthorizationStrategy());
}
使用 ACL:
通过 AclService 来管理和查询域对象的权限。
import org.springframework.security.acls.domain.BasePermission;
import org.springframework.security.acls.model.MutableAcl;
import org.springframework.security.acls.model.ObjectIdentity;
import org.springframework.security.acls.model.ObjectIdentityImpl;
import org.springframework.security.acls.model.Sid;
import org.springframework.security.acls.model.PrincipalSid;
import org.springframework.stereotype.Service;
@Service
public class AclManager {
private final JdbcMutableAclService aclService;
public AclManager(JdbcMutableAclService aclService) {
this.aclService = aclService;
}
public void grantPermission(Long employeeId, String username, Permission permission) {
ObjectIdentity oi = new ObjectIdentityImpl(Employee.class, employeeId);
MutableAcl acl = aclService.createAcl(oi);
Sid sid = new PrincipalSid(username);
acl.insertAce(acl.getEntries().size(), permission, sid, true);
aclService.updateAcl(acl);
}
}
总结
Spring Security 提供了两种主要机制来处理权限控制:
GrantedAuthority 用于全局权限和角色管理。
域对象安全(Domain Object Security),通过方法安全性和 ACL,实现对特定域对象的细粒度权限控制。
通过这种方式,Spring Security 能够高效地处理大规模应用中的权限控制需求,同时避免在每个域对象上使用 GrantedAuthority 造成的内存和性能问题。
AuthenticationProvider身份验证提供者
可以将多个AuthenticationProviders实例注入ProviderManager。每个实例AuthenticationProvider执行特定类型的身份验证。例如,DaoAuthenticationProvider支持基于用户名/密码的身份验证,而JwtAuthenticationProvider支持对 JWT 令牌进行身份验证
请求凭证AuthenticationEntryPoin
AuthenticationEntryPoint用于发送向客户端请求凭据的 HTTP 响应。
有时,客户端会主动包含凭据(例如用户名和密码)来请求资源。在这些情况下,Spring Security 不需要提供向客户端请求凭据的 HTTP 响应,因为它们已经包含在内。
在其他情况下,客户端会向其无权访问的资源发出未经身份验证的请求。在这种情况下, 的实现AuthenticationEntryPoint用于向客户端请求凭据。该AuthenticationEntryPoint实现可能会重定向到登录页面、使用WWW-Authenticate标头进行响应或采取其他操作
流程
-
号当用户提交其凭证时, 会从AbstractAuthenticationProcessingFilter创建一个要进行身份验证的 。 创建的 类型取决于 的子类。 例如,会从中提交的用户名和密码创建一个。AuthenticationHttpServletRequestAuthenticationAbstractAuthenticationProcessingFilterUsernamePasswordAuthenticationFilterUsernamePasswordAuthenticationTokenHttpServletRequest
-
接下来,Authentication将传递到AuthenticationManager进行身份验证。
-
如果身份验证失败,则失败。
-
SecurityContextHolder已被清除。
-
RememberMeServices.loginFail被调用。如果未配置记住我,则此操作无效。请参阅rememberme包。
-
AuthenticationFailureHandler被调用。请参阅AuthenticationFailureHandler接口。
- 如果身份验证成功,则为Success。
-
SessionAuthenticationStrategy收到新登录通知。查看SessionAuthenticationStrategy界面。
-
Authentication在SecurityContextHolder上设置。稍后,如果您需要保存,以便在将来的请求中自动设置,则必须显式调用。请参阅类。SecurityContextSecurityContextRepository#saveContextSecurityContextHolderFilter
-
RememberMeServices.loginSuccess被调用。如果未配置记住我,则此操作无效。请参阅rememberme包。
-
ApplicationEventPublisher发布了一份InteractiveAuthenticationSuccessEvent。
-
AuthenticationSuccessHandler被调用。请参阅AuthenticationSuccessHandler接口。