http.authorizeHttpRequests()一行配置,如何在运行时变成权限校验逻辑?本文深入拆解 AuthorizeHttpRequestsConfigurer 的配置收集、AuthorizationManager 的映射注册,以及 AuthorizationFilter 的运行时校验全流程。
前言
认证回答了"你是谁",授权回答了"你能做什么"。在 Spring Security 中,URL 授权是最基础也最常用的授权方式——通过配置 URL 路径与权限的映射关系,控制哪些请求需要认证、哪些需要特定角色。
本文将追踪 http.authorizeHttpRequests(auth -> auth.requestMatchers("/admin/**").hasRole("ADMIN")) 这一行配置,从配置阶段到运行时校验的完整链路。
一、配置阶段:AuthorizeHttpRequestsConfigurer
1.1 入口方法
// HttpSecurity.authorizeHttpRequests()
public HttpSecurity authorizeHttpRequests(
Customizer<AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry> authorizeHttpRequestsCustomizer)
throws Exception {
ApplicationContext context = getContext();
// 获取或创建 AuthorizeHttpRequestsConfigurer,应用 Lambda 配置
authorizeHttpRequestsCustomizer
.customize(getOrApply(new AuthorizeHttpRequestsConfigurer<>(context)).getRegistry());
return HttpSecurity.this;
}
1.2 AuthorizationManagerRequestMatcherRegistry:配置收集器
这是 AuthorizeHttpRequestsConfigurer 的内部类,负责收集 URL 路径与授权管理器的映射关系。它继承自 AbstractRequestMatcherRegistry(提供 requestMatchers() / anyRequest() 等路径匹配入口),通过内部持有的 RequestMatcherDelegatingAuthorizationManager.Builder 逐步收集映射:
public final class AuthorizationManagerRequestMatcherRegistry
extends AbstractRequestMatcherRegistry<AuthorizedUrl> {
// 核心:使用 Builder 模式收集所有映射
private final RequestMatcherDelegatingAuthorizationManager.Builder managerBuilder =
RequestMatcherDelegatingAuthorizationManager.builder();
// requestMatchers(String...) 由父类 AbstractRequestMatcherRegistry 实现
// 它会创建 AntPathRequestMatcher/MvcRequestMatcher,最终回调本方法
@Override
protected AuthorizedUrl chainRequestMatchers(List<RequestMatcher> requestMatchers) {
return new AuthorizedUrl(requestMatchers);
}
// 添加映射(由 AuthorizedUrl.access() 最终调用)
private void addMapping(RequestMatcher matcher,
AuthorizationManager<RequestAuthorizationContext> manager) {
this.managerBuilder.add(matcher, manager);
}
// 构建最终的 AuthorizationManager
private AuthorizationManager<HttpServletRequest> createAuthorizationManager() {
return (AuthorizationManager<HttpServletRequest>) this.managerBuilder.build();
}
}
1.3 AuthorizedUrl:权限配置的链式 API
AuthorizedUrl 提供了各种权限配置方法:
public class AuthorizedUrl {
private final List<? extends RequestMatcher> matchers;
// 允许所有人访问 → SingleResultAuthorizationManager.permitAll()
public AuthorizationManagerRequestMatcherRegistry permitAll() {
return access(SingleResultAuthorizationManager.permitAll());
}
// 拒绝所有访问 → SingleResultAuthorizationManager.denyAll()
public AuthorizationManagerRequestMatcherRegistry denyAll() {
return access(SingleResultAuthorizationManager.denyAll());
}
// 需要认证(检查 Authentication 非 null 且已认证)
public AuthorizationManagerRequestMatcherRegistry authenticated() {
return access(AuthenticatedAuthorizationManager.authenticated());
}
// 需要特定角色(自动拼接 ROLE_ 前缀,支持 RoleHierarchy)
public AuthorizationManagerRequestMatcherRegistry hasRole(String role) {
return access(AuthorityAuthorizationManager.hasAnyRole(rolePrefix, new String[]{role}));
}
// 需要特定权限(不做前缀处理)
public AuthorizationManagerRequestMatcherRegistry hasAuthority(String authority) {
return access(AuthorityAuthorizationManager.hasAuthority(authority));
}
// 所有权限方法最终汇聚于此:遍历 matchers 逐个注册到 configurer
public AuthorizationManagerRequestMatcherRegistry access(
AuthorizationManager<RequestAuthorizationContext> manager) {
return AuthorizeHttpRequestsConfigurer.this.addMapping(this.matchers, manager);
}
}
1.4 配置示例解析
http.authorizeHttpRequests(auth -> auth
.requestMatchers("/public/**").permitAll() // 映射1
.requestMatchers("/admin/**").hasRole("ADMIN") // 映射2
.requestMatchers("/api/**").authenticated() // 映射3
.anyRequest().denyAll() // 映射4
);
配置收集结果:
| 映射 | RequestMatcher | AuthorizationManager |
|---|---|---|
| 1 | /public/** | permitAll → 始终授权 |
| 2 | /admin/** | hasRole("ADMIN") → 检查 ROLE_ADMIN |
| 3 | /api/** | authenticated → 检查是否已认证 |
| 4 | anyRequest | denyAll → 始终拒绝 |
配置收集流程:
requestMatchers()→ 创建 RequestMatcher →AuthorizedUrl链式调用 →access()→addMapping()→managerBuilder.add()。最终所有映射存储在RequestMatcherDelegatingAuthorizationManager.Builder中。
二、构建阶段:注册 AuthorizationFilter
2.1 AuthorizeHttpRequestsConfigurer.configure()
@Override
public void configure(H http) {
// 1. 创建 AuthorizationManager
AuthorizationManager<HttpServletRequest> authorizationManager =
this.registry.createAuthorizationManager();
// 2. 创建 AuthorizationFilter
AuthorizationFilter authorizationFilter =
new AuthorizationFilter(authorizationManager);
// 3. 设置事件发布器
authorizationFilter.setAuthorizationEventPublisher(this.publisher);
// 4. 添加到 HttpSecurity
http.addFilter(postProcess(authorizationFilter));
}
2.2 createAuthorizationManager:合并映射
// AuthorizeHttpRequestsConfigurer.AuthorizationManagerRequestMatcherRegistry
private AuthorizationManager<HttpServletRequest> createAuthorizationManager() {
// 确保没有未完成的映射(例如只写了 requestMatchers 但忘了写权限规则)
Assert.state(this.unmappedMatchers == null,
"An incomplete mapping was found...");
// 确保至少有一条映射
Assert.state(this.mappingCount > 0,
"At least one mapping is required...");
// 通过 Builder 构建最终的 RequestMatcherDelegatingAuthorizationManager
return (AuthorizationManager<HttpServletRequest>) this.managerBuilder.build();
}
RequestMatcherDelegatingAuthorizationManager 的核心逻辑:
public class RequestMatcherDelegatingAuthorizationManager
implements AuthorizationManager<HttpServletRequest> {
private final List<RequestMatcherEntry<AuthorizationManager<HttpServletRequest>>> mappings;
@Override
public AuthorizationDecision check(
Supplier<Authentication> authentication, HttpServletRequest request) {
// 遍历所有映射
for (RequestMatcherEntry<AuthorizationManager<HttpServletRequest>> entry : this.mappings) {
// 1. 检查请求是否匹配
if (entry.getRequestMatcher().matches(request)) {
// 2. 委托给对应的 AuthorizationManager
return entry.getEntry().check(authentication, request);
}
}
// 没有匹配的映射,默认拒绝
return new AuthorizationDecision(false);
}
}
三、运行时阶段:AuthorizationFilter 校验
3.1 AuthorizationFilter.doFilter() 完整源码
public class AuthorizationFilter extends GenericFilterBean {
private SecurityContextHolderStrategy securityContextHolderStrategy =
SecurityContextHolder.getContextHolderStrategy();
private final AuthorizationManager<HttpServletRequest> authorizationManager;
private AuthorizationEventPublisher eventPublisher;
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
FilterChain chain) throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
// ① 防止同一请求被重复过滤
if (this.observeOncePerRequest && isApplied(request)) {
chain.doFilter(request, response);
return;
}
// ② 跳过某些特殊 dispatcher 类型(如 ERROR dispatch)
if (skipDispatch(request)) {
chain.doFilter(request, response);
return;
}
// ③ 标记已过滤
String alreadyFilteredAttributeName = getAlreadyFilteredAttributeName();
request.setAttribute(alreadyFilteredAttributeName, Boolean.TRUE);
try {
// ④ 核心:调用授权管理器进行校验
AuthorizationResult result = this.authorizationManager.authorize(
this::getAuthentication, request);
// ⑤ 发布授权事件(可被监控系统采集)
this.eventPublisher.publishAuthorizationEvent(
this::getAuthentication, request, result);
// ⑥ 授权不通过 → 抛出异常,由 ExceptionTranslationFilter 捕获
if (result != null && !result.isGranted()) {
throw new AuthorizationDeniedException("Access Denied", result);
}
// ⑦ 授权通过 → 继续过滤器链
chain.doFilter(request, response);
} finally {
// ⑧ 清除标记(避免同一线程复用时误判)
request.removeAttribute(alreadyFilteredAttributeName);
}
}
private Authentication getAuthentication() {
Authentication authentication = this.securityContextHolderStrategy.getContext().getAuthentication();
if (authentication == null) {
throw new AuthenticationCredentialsNotFoundException(
"An Authentication object was not found in the SecurityContext");
}
return authentication;
}
}
关键点:注意第⑥步,AuthorizationFilter 不直接返回 403,而是抛出 AuthorizationDeniedException(继承自 AccessDeniedException)。这个异常会被后面的 ExceptionTranslationFilter 捕获,并根据用户是否已登录来决定返回 401(未登录)还是 403(已登录但无权限)。
3.2 校验流程图
四、AuthorizationManager 体系完整拆解
4.1 RequestMatcherDelegatingAuthorizationManager:按 URL 匹配委托
这是 URL 授权的核心路由器,内部维护了一个 mappings 列表,按顺序匹配 URL:
public final class RequestMatcherDelegatingAuthorizationManager
implements AuthorizationManager<HttpServletRequest> {
private static final AuthorizationDecision DENY = new AuthorizationDecision(false);
private final List<RequestMatcherEntry<AuthorizationManager<? super RequestAuthorizationContext>>> mappings;
@Override
public AuthorizationResult authorize(
Supplier<? extends Authentication> authentication,
HttpServletRequest request) {
// 遍历所有映射
for (RequestMatcherEntry<AuthorizationManager<? super RequestAuthorizationContext>> mapping : this.mappings) {
RequestMatcher matcher = mapping.getRequestMatcher();
MatchResult matchResult = matcher.matcher(request);
if (matchResult.isMatch()) {
// 找到匹配的 URL 规则 → 委托给对应的 AuthorizationManager
AuthorizationManager<? super RequestAuthorizationContext> manager = mapping.getEntry();
return manager.authorize(authentication,
new RequestAuthorizationContext(request, matchResult.getVariables()));
}
}
// ★ 没有任何规则匹配 → 默认拒绝(Security by Default)
return DENY;
}
}
关键设计:mappings 的顺序就是你在 authorizeHttpRequests() 中配置的顺序——先配置的规则优先匹配。
4.2 SingleResultAuthorizationManager:最简授权管理器
permitAll() 和 denyAll() 背后的实现非常简单——直接返回构造时传入的结果:
public final class SingleResultAuthorizationManager<C> implements AuthorizationManager<C> {
private static final SingleResultAuthorizationManager<?> DENY_MANAGER =
new SingleResultAuthorizationManager<>(new AuthorizationDecision(false));
private static final SingleResultAuthorizationManager<?> PERMIT_MANAGER =
new SingleResultAuthorizationManager<>(new AuthorizationDecision(true));
private final AuthorizationResult result;
@Override
public AuthorizationResult authorize(
Supplier<? extends Authentication> authentication, C object) {
return this.result; // 不检查任何东西,直接返回预设结果
}
}
4.3 常用 AuthorizationManager 对照表
| Manager | 对应配置 | 核心逻辑 |
|---|---|---|
SingleResultAuthorizationManager | permitAll() / denyAll() | 直接返回预设结果,不做任何检查 |
AuthenticatedAuthorizationManager | authenticated() | 检查 Authentication 是否非 null 且已认证 |
AuthorityAuthorizationManager | hasRole() / hasAuthority() | 检查用户权限列表中是否包含指定权限 |
RequestMatcherDelegatingAuthorizationManager | 多条规则 | URL 匹配 + 委托给对应的 Manager |
五、URL 授权 vs 方法授权
| 维度 | URL 授权 | 方法授权 |
|---|---|---|
| 配置方式 | http.authorizeHttpRequests() | @PreAuthorize / @Secured |
| 粒度 | URL 路径级别 | 方法级别 |
| 灵活性 | 较低(仅 URL) | 高(SpEL 表达式) |
| 执行时机 | AuthorizationFilter | AOP 代理 |
| 适用场景 | 粗粒度访问控制 | 细粒度业务权限 |
推荐实践:URL 授权做粗粒度控制(哪些路径需要认证),方法授权做细粒度控制(具体业务操作权限)。
六、实战:自定义 AuthorizationManager
// 自定义 IP 白名单授权管理器
// 注意:access() 接受的参数类型是 AuthorizationManager<RequestAuthorizationContext>
public class IpWhitelistAuthorizationManager
implements AuthorizationManager<RequestAuthorizationContext> {
private final List<String> allowedIps;
public IpWhitelistAuthorizationManager(List<String> allowedIps) {
this.allowedIps = allowedIps;
}
@Override
public AuthorizationResult authorize(
Supplier<Authentication> authentication, RequestAuthorizationContext context) {
String remoteAddr = context.getRequest().getRemoteAddr();
boolean allowed = allowedIps.contains(remoteAddr);
return new AuthorizationDecision(allowed);
}
// 兼容旧版 API(6.4 前),同时覆盖 check()
@Override
@Deprecated
public AuthorizationDecision check(
Supplier<Authentication> authentication, RequestAuthorizationContext context) {
return (AuthorizationDecision) authorize(authentication, context);
}
}
// 使用
http.authorizeHttpRequests(auth -> auth
.requestMatchers("/internal/**")
.access(new IpWhitelistAuthorizationManager(
List.of("192.168.1.0/24", "10.0.0.0/8")))
);
七、总结
| 阶段 | 组件 | 核心操作 |
|---|---|---|
| 配置阶段 | AuthorizeHttpRequestsConfigurer | 收集 URL → AuthorizationManager 映射 |
| 构建阶段 | AuthorizeHttpRequestsConfigurer.configure() | 创建 AuthorizationFilter 并注册 |
| 运行时 | AuthorizationFilter.doFilter() | 遍历映射,委托给 AuthorizationManager 校验 |
| 校验失败 | ExceptionTranslationFilter | 将 AuthorizationDeniedException 转为 401/403 |
| AuthorizationManager | 配置方法 | 校验逻辑 |
|---|---|---|
| SingleResultAuthorizationManager | permitAll() / denyAll() | 始终授权 / 始终拒绝 |
| AuthenticatedAuthorizationManager | authenticated() | 检查已认证 |
| AuthorityAuthorizationManager | hasRole() / hasAuthority() | 检查权限 |
下一篇预告:《Spring Security 方法安全授权与自定义扩展:@EnableMethodSecurity、AOP、SpEL 全链路》将深入拆解方法级授权的启用原理、@PreAuthorize 的 AOP 拦截机制,以及基于 SpEL 和自定义 AuthorizationManager 的两种生产级扩展方案。