前面章节介绍的基础权限管理方法(如 permitAll()、denyAll() 等)和基于角色、权限管理方法(如 hasRole()、hasAnyRole() 等)都是通过调用 access() 方法来实现的。
access() 方法是配置请求授权规则的核心方式之一,它允许你使用自定义表达式或授权管理器来定义复杂的访问控制逻辑。
方法定义如下:
public AuthorizeHttpRequestsConfigurer<H>.AuthorizationManagerRequestMatcherRegistry access(AuthorizationManager<RequestAuthorizationContext> manager) { Assert.notNull(manager, "manager cannot be null"); return this.not ? AuthorizeHttpRequestsConfigurer.this.addMapping( this.matchers, AuthorizationManagers.not(manager)) : AuthorizeHttpRequestsConfigurer.this.addMapping(this.matchers, manager); }
参数说明:
manager:实现 AuthorizationManager<RequestAuthorizationContext> 接口的自定义授权管理器,负责判断请求是否被允许。
上述代码,首先检查 manager 是否为 null,若为 null 则抛出异常。然后,根据 this.not 标志决定如何应用授权管理器:
若 this.not 为 true,使用 AuthorizationManagers.not(manager) 取反授权决策。
若 this.not 为 false,直接使用传入的 manager 进行授权决策。
最终调用 addMapping() 方法将请求匹配器(this.matchers)与授权管理器(manager)关联。
下面是一个简单示例:
http.authorizeHttpRequests(authorize -> authorize // 需要具有 admin 角色权限才能访问 /sys 路径的请求 .requestMatchers("/sys/**").hasRole("admin") .anyRequest().authenticated() // 其他请求需认证 )
使用 access() 方法改写:
http.authorizeHttpRequests(authorize -> authorize // 需要具有 admin 角色权限才能访问 /sys 路径的请求 .requestMatchers("/sys/**").access(AuthorityAuthorizationManager.hasRole("admin")) .anyRequest().authenticated() // 其他请求需认证 )
更多 access() 方法的使用方法如下:
// AuthenticatedAuthorizationManager http.authorizeHttpRequests(authorize -> authorize // 允许匿名访问 /sys 路径 .requestMatchers("/sys/**").access(AuthenticatedAuthorizationManager.anonymous()) .anyRequest().authenticated() // 其他请求需认证 ) // AuthorityAuthorizationManager http.authorizeHttpRequests(authorize -> authorize // 需要具有 admin 角色权限才能访问 /sys 路径的请求 .requestMatchers("/sys/**").access(AuthorityAuthorizationManager.hasRole("admin")) .anyRequest().authenticated() // 其他请求需认证 ) // WebExpressionAuthorizationManager http.authorizeHttpRequests(authorize -> authorize // 需要具有 admin 角色权限才能访问 /sys 路径的请求 .requestMatchers("/sys/**").access(new WebExpressionAuthorizationManager("hasRole('admin')")) .anyRequest().authenticated() // 其他请求需认证 ) // IpAddressAuthorizationManager http.authorizeHttpRequests(authorize -> authorize // 需要客户端 IP 为 192.168.1.15 或位于 192.168.1.1 ~ 192.168.1.224 之间,才能访问 /sys 路径的请求 .requestMatchers("/sys/**").access(AuthorizationManagers.anyOf( IpAddressAuthorizationManager.hasIpAddress("192.168.1.15"), IpAddressAuthorizationManager.hasIpAddress("192.168.1.0/24") )) .anyRequest().authenticated() // 其他请求需认证 )
接下来将详细介绍授权管理器接口 AuthorizationManager。
AuthorizationManager 是实现自定义授权逻辑的核心接口,它允许你基于请求上下文(如路径变量、请求参数、用户认证信息)进行灵活的访问控制。
接口定义如下:
package org.springframework.security.authorization; import java.util.function.Supplier; import org.springframework.lang.Nullable; import org.springframework.security.access.AccessDeniedException; import org.springframework.security.core.Authentication; /** * 一个授权管理器,它可以确定一个 Authentication(身份验证对象)是否有权访问特定对象。 * * @param <T> 进行授权检查所针对的对象类型。 * @author Evgeniy Cheban */ @FunctionalInterface public interface AuthorizationManager<T> { /** * 确定对于特定的身份验证和对象是否应授予访问权限。 * @param authentication 要检查的 Authentication 的 Supplier。 * @param object 要检查的对象 T * @throws 如果未授予访问权限,则抛出 AccessDeniedException 异常。 */ default void verify(Supplier<Authentication> authentication, T object) { // 调用 check 方法检查是否拥有访问权限 AuthorizationDecision decision = check(authentication, object); // 如果返回为 null,什么也不做 // 如果返回不为 null,且 decision.isGranted() 为 false,则抛出 AccessDeniedException 异常 if (decision != null && !decision.isGranted()) { throw new AccessDeniedException("Access Denied"); } } /** * 判断某个特定的身份验证信息以及对应的对象,是否被赋予访问权限。 * @param authentication 要检查的 Authentication 的 Supplier。 * @param object 要检查的对象 T * @return 一个 AuthorizationDecision(表示授权结果,同意还是不同意)。 * 如果无法做出决定,则返回null。 */ @Nullable AuthorizationDecision check(Supplier<Authentication> authentication, T object); }
上面源码中,check() 方法返回的是 AuthorizationDecision,我们看看它的源码:
package org.springframework.security.authorization; /** * @author Rob Winch * @since 5.0 */ public class AuthorizationDecision implements AuthorizationResult { // 授权决策结果:true表示允许访问,false表示拒绝访问 private final boolean granted; public AuthorizationDecision(boolean granted) { this.granted = granted; } @Override public boolean isGranted() { return this.granted; } @Override public String toString() { return getClass().getSimpleName() + " [granted=" + this.granted + "]"; } }
再看看 AuthorizationResult 接口,定义如下:
package org.springframework.security.authorization; /** * Represents an authorization result * 表示一个授权结果,定义了检查访问权限的基本契约。 * * @author Marcus da Coregio * @since 6.3 */ public interface AuthorizationResult { /** * 检查访问权限是否被授予。 * @return 如果访问被允许则返回true,否则返回false。 */ boolean isGranted(); }
AuthorizationResult 接口仅仅定义了一个 isGranted() 方法,表示是否被授权。
通常情况下,我们是不需要自己去实现 AuthorizationManager 接口的,因为 Spring Security 内置了很多 AuthorizationManager 接口的实现类,如下图:
上图中,已经内置了十几个实现,各个实现如下:
AuthorityAuthorizationManager 基于用户权限(如角色、菜单权限)进行授权决策,检查当前用户是否拥有指定的权限。
AuthenticatedAuthorizationManager 基于用户认证状态进行授权决策。如当前用户是否匿名、是否通过完整认证(即用户名/密码登陆)、是否通过“记住我”认证),适用于需要区分不同登录级别访问权限的场景。
WebExpressionAuthorizationManager 用于基于表达式语言实现复杂的访问控制决策。它允许开发者使用 SpEL(Spring Expression Language)定义灵活的授权规则,适用于 Web 请求的权限验证。
IpAddressAuthorizationManager 用于基于 IP 地址进行访问控制的授权管理器。它允许开发者根据客户端的 IP 地址或 IP 网段(CIDR 格式)来限制或允许请求。
AuthoritiesAuthorizationManager 基于角色或权限(authorities)进行授权决策。检查用户是否具有指定的角色或权限才能访问受保护的资源。
DeferringObservationAuthorizationManager 用于在授权决策过程中收集观测数据(observability data),特别是针对需要延迟执行的授权决策。
Jsr250AuthorizationManager 实现 JSR-250 标准的授权注解(如 @RolesAllowed)的管理器,用于基于 Java 安全注解进行授权决策。
MethodExpressionAuthorizationManager 支持基于表达式的方法级授权,允许使用 SpEL 表达式定义复杂的授权规则,例如基于方法参数或返回值的条件授权。
ObservationAuthorizationManager 用于收集授权过程的观察数据,如执行时间、成功 / 失败率等,帮助监控和分析授权行为。用于收集授权过程观察数据的装饰器类,它包装了另一个 AuthorizationManager 并添加了观测能力。
RequestMatcherDelegatingAuthorizationManager 基于请求匹配器(RequestMatcher)的授权管理器,允许为不同的 HTTP 请求路径或方法配置不同的授权策略。
关于上述内置 AuthorizationManager 实现类如何使用将在后续章节逐一展开。