| 对于最新的稳定版本,请使用 Spring Security 6.5.3! | 
使用 AuthorizationFilter 授权 HttpServletRequests
本节以 Servlet 架构和实现为基础,深入挖掘授权在基于 Servlet 的应用程序中的工作原理。
| AuthorizationFilter取代FilterSecurityInterceptor.
为了保持向后兼容,FilterSecurityInterceptor仍为默认值。
本节讨论如何AuthorizationFilter有效,以及如何覆盖默认配置。 | 
这AuthorizationFilter提供授权HttpServletRequests.
它作为安全过滤器之一插入到 FilterChainProxy 中。
您可以在声明SecurityFilterChain.
而不是使用authorizeRequests用authorizeHttpRequests这样:
- 
Java 
@Bean
SecurityFilterChain web(HttpSecurity http) throws AuthenticationException {
    http
        .authorizeHttpRequests((authorize) -> authorize
            .anyRequest().authenticated();
        )
        // ...
    return http.build();
}这改进了authorizeRequests以多种方式:
- 
使用简化的 AuthorizationManagerAPI 而不是元数据源、配置属性、决策管理器和投票者。 这简化了重用和定制。
- 
延误 Authentication查找。 它不需要为每个请求查找身份验证,而是只会在授权决策需要身份验证的请求中查找它。
- 
基于 Bean 的配置支持。 
什么时候authorizeHttpRequests代替authorizeRequests然后AuthorizationFilter代替FilterSecurityInterceptor.
 
- 
 首先, 首先,AuthorizationFilter从 SecurityContextHolder 获取身份验证。 它将其包装在一个Supplier以延迟查找。
- 
 其次,它传递 其次,它传递Supplier<Authentication>和HttpServletRequest到AuthorizationManager.- 
 如果授权被拒绝,则 如果授权被拒绝,则AccessDeniedException被抛出。 在本例中,ExceptionTranslationFilter处理AccessDeniedException.
- 
 如果授予访问权限, 如果授予访问权限,AuthorizationFilter继续使用 FilterChain,它允许应用程序正常处理。
 
- 
我们可以通过按优先级顺序添加更多规则来将 Spring Security 配置为具有不同的规则。
- 
Java 
@Bean
SecurityFilterChain web(HttpSecurity http) throws Exception {
	http
		// ...
		.authorizeHttpRequests(authorize -> authorize                                  (1)
			.requestMatchers("/resources/**", "/signup", "/about").permitAll()         (2)
			.requestMatchers("/admin/**").hasRole("ADMIN")                             (3)
			.requestMatchers("/db/**").access(new WebExpressionAuthorizationManager("hasRole('ADMIN') and hasRole('DBA')"))   (4)
			// .requestMatchers("/db/**").access(AuthorizationManagers.allOf(AuthorityAuthorizationManager.hasRole("ADMIN"), AuthorityAuthorizationManager.hasRole("DBA")))   (5)
			.anyRequest().denyAll()                                                (6)
		);
	return http.build();
}| 1 | 指定了多个授权规则。 每个规则都按声明的顺序进行考虑。 | 
| 2 | 我们指定了任何用户都可以访问的多个 URL 模式。 具体来说,如果 URL 以“/resources/”开头、等于“/signup”或等于“/about”,则任何用户都可以访问请求。 | 
| 3 | 任何以“/admin/”开头的 URL 将仅限于具有角色“ROLE_ADMIN”的用户。
您会注意到,由于我们调用了 hasRole方法,我们不需要指定“ROLE_”前缀。 | 
| 4 | 任何以“/db/”开头的 URL 都要求用户同时拥有“ROLE_ADMIN”和“ROLE_DBA”。
您会注意到,由于我们使用的是 hasRoleexpression 我们不需要指定“ROLE_”前缀。 | 
| 5 | 4 中的相同规则,可以通过组合多个来编写 AuthorizationManager. | 
| 6 | 任何尚未匹配的 URL 都将被拒绝访问。 如果您不想意外忘记更新授权规则,这是一个很好的策略。 | 
您可以通过构建自己的 Bean 来采用基于 Bean 的方法RequestMatcherDelegatingAuthorizationManager这样:
- 
Java 
@Bean
SecurityFilterChain web(HttpSecurity http, AuthorizationManager<RequestAuthorizationContext> access)
        throws AuthenticationException {
    http
        .authorizeHttpRequests((authorize) -> authorize
            .anyRequest().access(access)
        )
        // ...
    return http.build();
}
@Bean
AuthorizationManager<RequestAuthorizationContext> requestMatcherAuthorizationManager(HandlerMappingIntrospector introspector) {
    MvcRequestMatcher.Builder mvcMatcherBuilder = new MvcRequestMatcher.Builder(introspector);
    RequestMatcher permitAll =
            new AndRequestMatcher(
                    mvcMatcherBuilder.pattern("/resources/**"),
                    mvcMatcherBuilder.pattern("/signup"),
                    mvcMatcherBuilder.pattern("/about"));
    RequestMatcher admin = mvcMatcherBuilder.pattern("/admin/**");
    RequestMatcher db = mvcMatcherBuilder.pattern("/db/**");
    RequestMatcher any = AnyRequestMatcher.INSTANCE;
    AuthorizationManager<HttpServletRequest> manager = RequestMatcherDelegatingAuthorizationManager.builder()
            .add(permitAll, (context) -> new AuthorizationDecision(true))
            .add(admin, AuthorityAuthorizationManager.hasRole("ADMIN"))
            .add(db, AuthorityAuthorizationManager.hasRole("DBA"))
            .add(any, new AuthenticatedAuthorizationManager())
            .build();
    return (context) -> manager.check(context.getRequest());
}您还可以为任何请求匹配器连接自己的自定义授权管理器。
- 
Java 
@Bean
SecurityFilterChain web(HttpSecurity http) throws Exception {
    http
        .authorizeHttpRequests((authorize) -> authorize
            .requestMatchers("/my/authorized/endpoint").access(new CustomAuthorizationManager());
        )
        // ...
    return http.build();
}或者,您可以为所有请求提供它,如下所示:
- 
Java 
@Bean
SecurityFilterChain web(HttpSecurity http) throws Exception {
    http
        .authorizeHttpRequests((authorize) -> authorize
            .anyRequest().access(new CustomAuthorizationManager());
        )
        // ...
    return http.build();
}默认情况下,AuthorizationFilter不适用于DispatcherType.ERROR和DispatcherType.ASYNC.
我们可以配置 Spring Security 以将授权规则应用于所有调度器类型,方法是使用shouldFilterAllDispatcherTypes方法:
- 
Java 
- 
Kotlin 
@Bean
SecurityFilterChain web(HttpSecurity http) throws Exception {
    http
        .authorizeHttpRequests((authorize) -> authorize
            .shouldFilterAllDispatcherTypes(true)
            .anyRequest.authenticated()
        )
        // ...
    return http.build();
}@Bean
open fun web(http: HttpSecurity): SecurityFilterChain {
    http {
        authorizeHttpRequests {
            shouldFilterAllDispatcherTypes = true
            authorize(anyRequest, authenticated)
        }
    }
    return http.build()
}现在,授权规则适用于所有调度程序类型,您可以更好地控制这些类型的授权。
例如,您可能想要配置shouldFilterAllDispatcherTypes自true但不对具有调度程序类型的请求应用授权ASYNC或FORWARD.
- 
Java 
- 
Kotlin 
@Bean
SecurityFilterChain web(HttpSecurity http) throws Exception {
    http
        .authorizeHttpRequests((authorize) -> authorize
            .shouldFilterAllDispatcherTypes(true)
            .dispatcherTypeMatchers(DispatcherType.ASYNC, DispatcherType.FORWARD).permitAll()
            .anyRequest().authenticated()
        )
        // ...
    return http.build();
}@Bean
open fun web(http: HttpSecurity): SecurityFilterChain {
    http {
        authorizeHttpRequests {
            shouldFilterAllDispatcherTypes = true
            authorize(DispatcherTypeRequestMatcher(DispatcherType.ASYNC, DispatcherType.FORWARD), permitAll)
            authorize(anyRequest, authenticated)
        }
    }
    return http.build()
}您还可以对其进行自定义,以要求调度员类型的特定角色:
- 
Java 
- 
Kotlin 
@Bean
SecurityFilterChain web(HttpSecurity http) throws Exception {
    http
        .authorizeHttpRequests((authorize) -> authorize
            .shouldFilterAllDispatcherTypes(true)
            .dispatcherTypeMatchers(DispatcherType.ERROR).hasRole("ADMIN")
            .anyRequest().authenticated()
        )
        // ...
    return http.build();
}@Bean
open fun web(http: HttpSecurity): SecurityFilterChain {
    http {
        authorizeHttpRequests {
            shouldFilterAllDispatcherTypes = true
            authorize(DispatcherTypeRequestMatcher(DispatcherType.ERROR), hasRole("ADMIN"))
            authorize(anyRequest, authenticated)
        }
    }
    return http.build()
}请求匹配器
这RequestMatcher接口用于确定请求是否与给定规则匹配。我们使用securityMatchers以确定给定的HttpSecurity应该应用于给定的请求。同样,我们可以使用requestMatchers以确定我们应该应用于给定请求的授权规则。请看以下示例:
- 
Java 
- 
Kotlin 
@Configuration
@EnableWebSecurity
public class SecurityConfig {
	@Bean
	public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
		http
			.securityMatcher("/api/**")                            (1)
			.authorizeHttpRequests(authorize -> authorize
				.requestMatchers("/user/**").hasRole("USER")       (2)
				.requestMatchers("/admin/**").hasRole("ADMIN")     (3)
				.anyRequest().authenticated()                      (4)
			)
			.formLogin(withDefaults());
		return http.build();
	}
}@Configuration
@EnableWebSecurity
open class SecurityConfig {
    @Bean
    open fun web(http: HttpSecurity): SecurityFilterChain {
        http {
            securityMatcher("/api/**")                                           (1)
            authorizeHttpRequests {
                authorize("/user/**", hasRole("USER"))                           (2)
                authorize("/admin/**", hasRole("ADMIN"))                         (3)
                authorize(anyRequest, authenticated)                             (4)
            }
        }
        return http.build()
    }
}| 1 | 配置 HttpSecurity仅应用于以/api/ | 
| 2 | 允许访问以 /user/对使用USER角色 | 
| 3 | 允许访问以 /admin/对使用ADMIN角色 | 
| 4 | 与上述规则不匹配的任何其他请求都需要身份验证 | 
这securityMatcher(s)和requestMatcher(s)方法将决定哪个RequestMatcher实现最适合您的应用程序:如果 Spring MVC 在类路径中,则MvcRequestMatcher将被使用,否则,AntPathRequestMatcher将被使用。
您可以在此处阅读有关 Spring MVC 集成的更多信息。
如果要使用特定的RequestMatcher,只需将实现传递给securityMatcher和/或requestMatcher方法:
- 
Java 
- 
Kotlin 
import static org.springframework.security.web.util.matcher.AntPathRequestMatcher.antMatcher; (1)
import static org.springframework.security.web.util.matcher.RegexRequestMatcher.regexMatcher;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
	@Bean
	public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
		http
			.securityMatcher(antMatcher("/api/**"))                              (2)
			.authorizeHttpRequests(authorize -> authorize
				.requestMatchers(antMatcher("/user/**")).hasRole("USER")         (3)
				.requestMatchers(regexMatcher("/admin/.*")).hasRole("ADMIN")     (4)
				.requestMatchers(new MyCustomRequestMatcher()).hasRole("SUPERVISOR")     (5)
				.anyRequest().authenticated()
			)
			.formLogin(withDefaults());
		return http.build();
	}
}
public class MyCustomRequestMatcher implements RequestMatcher {
    @Override
    public boolean matches(HttpServletRequest request) {
        // ...
    }
}import org.springframework.security.web.util.matcher.AntPathRequestMatcher.antMatcher (1)
import org.springframework.security.web.util.matcher.RegexRequestMatcher.regexMatcher
@Configuration
@EnableWebSecurity
open class SecurityConfig {
    @Bean
    open fun web(http: HttpSecurity): SecurityFilterChain {
        http {
            securityMatcher(antMatcher("/api/**"))                               (2)
            authorizeHttpRequests {
                authorize(antMatcher("/user/**"), hasRole("USER"))               (3)
                authorize(regexMatcher("/admin/**"), hasRole("ADMIN"))           (4)
                authorize(MyCustomRequestMatcher(), hasRole("SUPERVISOR"))       (5)
                authorize(anyRequest, authenticated)
            }
        }
        return http.build()
    }
}| 1 | 从 AntPathRequestMatcher和RegexRequestMatcher创建RequestMatcher实例。 | 
| 2 | 配置 HttpSecurity仅应用于以/api/用AntPathRequestMatcher | 
| 3 | 允许访问以 /user/对使用USER角色, 使用AntPathRequestMatcher | 
| 4 | 允许访问以 /admin/对使用ADMIN角色, 使用RegexRequestMatcher | 
| 5 | 允许访问与 MyCustomRequestMatcher对使用SUPERVISOR角色,使用自定义RequestMatcher | 
表达 式
建议您使用类型安全的授权管理器而不是 SpEL。 然而WebExpressionAuthorizationManager可用于帮助迁移旧版 SpEL。
使用WebExpressionAuthorizationManager,您可以使用尝试迁移的表达式构造一个,如下所示:
- 
Java 
- 
Kotlin 
.requestMatchers("/test/**").access(new WebExpressionAuthorizationManager("hasRole('ADMIN') && hasRole('USER')")).requestMatchers("/test/**").access(WebExpressionAuthorizationManager("hasRole('ADMIN') && hasRole('USER')"))如果你在表达式中引用一个 bean,如下所示:@webSecurity.check(authentication, request),建议你直接调用 bean,它看起来如下:
- 
Java 
- 
Kotlin 
.requestMatchers("/test/**").access((authentication, context) ->
    new AuthorizationDecision(webSecurity.check(authentication.get(), context.getRequest()))).requestMatchers("/test/**").access((authentication, context): AuthorizationManager<RequestAuthorizationContext> ->
    AuthorizationDecision(webSecurity.check(authentication.get(), context.getRequest())))对于包含 bean 引用和其他表达式的复杂指令,建议您将这些指令更改为实现AuthorizationManager并通过调用.access(AuthorizationManager).
如果您无法执行此作,则可以配置DefaultHttpSecurityExpressionHandler使用 bean 解析器并将其提供给WebExpressionAuthorizationManager#setExpressionhandler.