Authentication
Authentication
讨论了所有 Authentication
实现如何存储 GrantedAuthority
对象的列表。这些对象代表已经授予委托人(principal)的权限。GrantedAuthority
对象由 AuthenticationManager
插入到 Authentication
对象中,随后由 AccessDecisionManager
实例在做出授权决定时读取。
GrantedAuthority
接口只有一个方法。
String getAuthority();
Copied!
这个方法被 AuthorizationManager
实例用来获取 GrantedAuthority
的一个精确的 String
表示。通过返回一个 String
表示,一个 GrantedAuthority
可以被大多数 AuthorizationManager
实现轻松 "读取"。如果 GrantedAuthority
不能被精确地表示为一个 String
,那么该 GrantedAuthority
被认为是 "复杂的",getAuthority()
必须返回 null
。
一个复杂的 GrantedAuthority
的例子是一个实现,它存储了一个适用于不同客户账号的操作和权限阈值的列表。将这种复杂的 GrantedAuthority
表示为一个 String
将是相当困难的。因此,getAuthority()
方法应该返回 null
。这向任何 AuthorizationManager
表明,它需要支持特定的 GrantedAuthority
实现来理解其内容。
Spring Security 包括一个具体的 GrantedAuthority
实现。SimpleGrantedAuthority
。这个实现允许任何用户指定的字符串被转换为 GrantedAuthority
。安全架构中包含的所有 AuthenticationProvider
实例都使用 SimpleGrantedAuthority
来填充 Authentication
对象。
默认情况下,基于角色的授权规则包括 ROLE_
作为前缀。这意味着,如果有一个授权规则要求 security context 的角色是 "USER",Spring Security 将默认寻找返回 "ROLE_USER" 的 GrantedAuthority#getAuthority
。
你可以用 GrantedAuthorityDefaults 来定制这个。GrantedAuthorityDefaults 的存在是为了允许自定义基于角色的授权规则所使用的前缀。
你可以通过暴露一个 GrantedAuthorityDefaults Bean 来配置授权规则以使用不同的前缀,像这样:
@Bean
static GrantedAuthorityDefaults grantedAuthorityDefaults() {
return new GrantedAuthorityDefaults("MYPREFIX_");
}
调用处理
Spring Security 提供的拦截器可以控制对安全对象的访问,例如方法调用或Web请求。关于调用是否被允许进行的预调用决定是由 AuthorizationManager 实例做出的。同样,关于是否可以返回给定值的调用后决策也是由 AuthorizationManager 实例做出的。
AuthorizationManager
AuthorizationManager
同时取代了 AccessDecisionManager
和 AccessDecisionVoter
。
我们鼓励定制 AccessDecisionManager
或 AccessDecisionVoter
的应用程序 改为使用 AuthorizationManager
。
AuthorizationManager
被 Spring Security 的 基于请求、 基于方法 和 基于消息 的授权组件所调用,并负责做出最终的访问控制决定。AuthorizationManager
接口包含两个方法:
AuthorizationDecision check(Supplier<Authentication> authentication, Object secureObject);
default AuthorizationDecision verify(Supplier<Authentication> authentication, Object secureObject)
throws AccessDeniedException {
// ...
}
Copied!
AuthorizationManager 的检查方法被传递给它所需要的所有相关信息,以便做出授权决定。特别是,传递安全对象(secure Object)使那些包含在实际安全对象调用中的参数能够被检查到。例如,让我们假设安全对象是一个 MethodInvocation。查询 MethodInvocation 的任何客户参数是很容易的,然后在 AuthorizationManager 中实现某种安全逻辑以确保委托人(principal)被允许对该客户进行操作。如果访问被授予,实现应返回 "negative" 的 AuthorizationDecision,如果访问被拒绝,应返回 "negative" 的 AuthorizationDecision,如果不作出决定,则返回空的 AuthorizationDecision。
verify 调用 check,然后在出现 "negative" 的 AuthorizationDecision 决定时抛出一个 AccessDeniedException。
基于授权的AuthorizationManager实现
虽然用户可以实现他们自己的 AuthorizationManager
来控制授权的所有方面(aspect),但Spring Security提供了一个委托的 AuthorizationManager
,可以与个别的 AuthorizationManager
协作。
RequestMatcherDelegatingAuthorizationManager
将把请求与最合适的委托(delegate) AuthorizationManager
相匹配。 对于方法安全,你可以使用 AuthorizationManagerBeforeMethodInterceptor
和 AuthorizationManagerAfterMethodInterceptor
。
使用这种方法,AuthorizationManager
实现的组合可以在授权决定上被轮询。
AuthorityAuthorizationManager
Spring Security提供的最常见的 AuthorizationManager
是 AuthorityAuthorizationManager
。它被配置为在当前 Authentication
中寻找一组给定的授权。如果 Authentication
包含任何配置的授权,它将返回 positive 的 AuthorizationDecision
。否则,它将返回一个 negative 的 AuthorizationDecision
AuthenticatedAuthorizationManager
另一个管理器是 AuthenticatedAuthorizationManager
。它可以用来区分匿名、完全认证和记住我认证的用户。许多网站在Remember-me认证下允许某些有限的访问,但要求用户通过登录来确认他们的身份以获得完整的访问。
AuthenticationManagers
在 AuthenticationManagers
中还有一些有用的静态工厂,用于将单个 AuthenticationManagers
组合成更复杂的表达式。
自定义授权管理器(Authorization Manager)
很明显,你也可以实现一个自定义的 AuthorizationManager
,你可以把你想要的任何访问控制逻辑放在里面。它可能是针对你的应用程序的(与业务逻辑有关),也可能实现一些安全管理逻辑。例如,你可以创建一个可以查询Open Policy Agent或你自己的授权数据库的实现。
package com.example.eachadmin.config.Authorization;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.access.intercept.RequestAuthorizationContext;
import org.springframework.stereotype.Component;
import java.util.Set;
import java.util.function.Supplier;
@Component
public class RBACAuthorizationManager implements AuthorizationManager<RequestAuthorizationContext> {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Override
public AuthorizationDecision check(Supplier<Authentication> authentication, RequestAuthorizationContext object) {
Authentication auth = authentication.get();
boolean authenticated = auth.isAuthenticated();
Set<Object> urls = redisTemplate.opsForSet().members("mySet");
if (!authenticated) return new AuthorizationDecision(false);
if (urls != null) {
return new AuthorizationDecision(urls.contains(object.getRequest().getRequestURI()));
}
return new AuthorizationDecision(false);
}
}
@Component
public class CustomizeAuthorizeHttpRequestsConfig implements Customizer<AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry> {
@Autowired
private RBACAuthorizationManager rbacAuthorizationManager;
@Override
public void customize(AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry authorizationManagerRequestMatcherRegistry) {
authorizationManagerRequestMatcherRegistry
.requestMatchers("/login", "/image")
.permitAll()
.anyRequest()
.access(rbacAuthorizationManager);
}
}