对于最新的稳定版本,请使用 Spring Security 6.3.1Spring中文文档

对于最新的稳定版本,请使用 Spring Security 6.3.1Spring中文文档

本节以 Servlet 体系结构和实现为基础,深入探讨授权在基于 Servlet 的应用程序中的工作方式。Spring中文文档

AuthorizationFilter取代 FilterSecurityInterceptor。 要保持向后兼容,则仍然是默认值。 本节讨论工作原理以及如何覆盖默认配置。FilterSecurityInterceptorAuthorizationFilter

AuthorizationFilter 为 s 提供授权。 它作为安全过滤器之一插入到 FilterChainProxy 中。HttpServletRequestSpring中文文档

您可以在声明 . 不要使用 authorizeRequests,而是使用 ,如下所示:SecurityFilterChainauthorizeHttpRequestsSpring中文文档

使用 authorizeHttpRequests
@Bean
SecurityFilterChain web(HttpSecurity http) throws AuthenticationException {
    http
        .authorizeHttpRequests((authorize) -> authorize
            .anyRequest().authenticated();
        )
        // ...

    return http.build();
}

这在许多方面得到了改进:authorizeRequestsSpring中文文档

  1. 使用简化的 API,而不是元数据源、配置属性、决策管理器和投票者。 这简化了重用和自定义。AuthorizationManagerSpring中文文档

  2. 延迟查找。 它不需要为每个请求查找身份验证,而只会在授权决策需要身份验证的请求中查找身份验证。AuthenticationSpring中文文档

  3. 基于 Bean 的配置支持。Spring中文文档

当使用 而不是 时,则使用 AuthorizationFilter 而不是 FilterSecurityInterceptorauthorizeHttpRequestsauthorizeRequestsSpring中文文档

授权过滤器
图 1.授权 HttpServletRequest

我们可以通过按优先级顺序添加更多规则来配置 Spring Security 以具有不同的规则。Spring中文文档

授权请求
@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”的用户。 您会注意到,由于我们正在调用该方法,因此我们不需要指定“ROLE_”前缀。hasRole
4 任何以“/db/”开头的 URL 都要求用户同时具有“ROLE_ADMIN”和“ROLE_DBA”。 您会注意到,由于我们使用的是表达式,因此我们不需要指定“ROLE_”前缀。hasRole
5 来自 4 的相同规则,可以通过组合多个 .AuthorizationManager
6 任何尚未匹配的 URL 都将被拒绝访问。 如果您不想意外忘记更新授权规则,这是一个很好的策略。

您可以通过构造自己的 RequestMatcherDelegatingAuthorizationManager 来采用基于 Bean 的方法,如下所示:Spring中文文档

配置 RequestMatcherDelegatingAuthorizationManager
@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());
}

您还可以为任何请求匹配器连接自己的自定义授权管理器Spring中文文档

下面是将自定义授权管理器映射到 的示例:my/authorized/endpointSpring中文文档

自定义授权管理器
@Bean
SecurityFilterChain web(HttpSecurity http) throws Exception {
    http
        .authorizeHttpRequests((authorize) -> authorize
            .requestMatchers("/my/authorized/endpoint").access(new CustomAuthorizationManager());
        )
        // ...

    return http.build();
}

或者,您可以为所有请求提供它,如下所示:Spring中文文档

所有请求的自定义授权管理器
@Bean
SecurityFilterChain web(HttpSecurity http) throws Exception {
    http
        .authorizeHttpRequests((authorize) -> authorize
            .anyRequest().access(new CustomAuthorizationManager());
        )
        // ...

    return http.build();
}

默认情况下,不适用于 和 。 我们可以使用以下方法将 Spring Security 配置为将授权规则应用于所有调度程序类型:AuthorizationFilterDispatcherType.ERRORDispatcherType.ASYNCshouldFilterAllDispatcherTypesSpring中文文档

将 shouldFilterAllDispatcherTypes 设置为 true
@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()
}

现在,将授权规则应用于所有调度程序类型后,您可以更好地控制对它们的授权。 例如,您可能希望配置为对具有调度程序类型或 .shouldFilterAllDispatcherTypestrueASYNCFORWARDSpring中文文档

允许 ASYNC 和 FORWARD 调度程序类型
@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()
}

您还可以自定义它以要求调度程序类型的特定角色:Spring中文文档

需要 ADMIN 才能进行调度程序类型错误
@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()
}
AuthorizationFilter取代 FilterSecurityInterceptor。 要保持向后兼容,则仍然是默认值。 本节讨论工作原理以及如何覆盖默认配置。FilterSecurityInterceptorAuthorizationFilter
1 指定了多个授权规则。 每条规则都按其声明的顺序进行考虑。
2 我们指定了任何用户都可以访问的多个 URL 模式。 具体而言,如果 URL 以“/resources/”开头、等于“/signup”或等于“/about”,则任何用户都可以访问请求。
3 任何以“/admin/”开头的 URL 都将仅限于角色为“ROLE_ADMIN”的用户。 您会注意到,由于我们正在调用该方法,因此我们不需要指定“ROLE_”前缀。hasRole
4 任何以“/db/”开头的 URL 都要求用户同时具有“ROLE_ADMIN”和“ROLE_DBA”。 您会注意到,由于我们使用的是表达式,因此我们不需要指定“ROLE_”前缀。hasRole
5 来自 4 的相同规则,可以通过组合多个 .AuthorizationManager
6 任何尚未匹配的 URL 都将被拒绝访问。 如果您不想意外忘记更新授权规则,这是一个很好的策略。

请求匹配器

该接口用于确定请求是否与给定规则匹配。 我们用来确定是否应该将给定的请求应用于给定的请求。 同样,我们可以用来确定应应用于给定请求的授权规则。 请看以下示例:RequestMatchersecurityMatchersHttpSecurityrequestMatchersSpring中文文档

@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 允许访问以具有该角色的用户开头的 URL/user/USER
3 允许访问以具有该角色的用户开头的 URL/admin/ADMIN
4 任何其他与上述规则不匹配的请求都需要身份验证

和 方法将决定哪种实现最适合您的应用程序:如果 Spring MVC 在类路径中,则将使用 then,否则将使用。 您可以在此处阅读有关 Spring MVC 集成的更多信息。securityMatcher(s)requestMatcher(s)RequestMatcherMvcRequestMatcherAntPathRequestMatcherSpring中文文档

如果要使用特定的 ,只需将实现传递给 and/or 方法:RequestMatchersecurityMatcherrequestMatcherSpring中文文档

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 从实例导入静态工厂方法,并创建实例。AntPathRequestMatcherRegexRequestMatcherRequestMatcher
2 配置为仅应用于以 开头的 URL,使用HttpSecurity/api/AntPathRequestMatcher
3 允许访问以具有角色的用户开头的 URL,使用/user/USERAntPathRequestMatcher
4 允许访问以具有角色的用户开头的 URL,使用/admin/ADMINRegexRequestMatcher
5 允许访问与具有角色的用户匹配的 URL,使用自定义MyCustomRequestMatcherSUPERVISORRequestMatcher
1 配置为仅应用于以HttpSecurity/api/
2 允许访问以具有该角色的用户开头的 URL/user/USER
3 允许访问以具有该角色的用户开头的 URL/admin/ADMIN
4 任何其他与上述规则不匹配的请求都需要身份验证
1 从实例导入静态工厂方法,并创建实例。AntPathRequestMatcherRegexRequestMatcherRequestMatcher
2 配置为仅应用于以 开头的 URL,使用HttpSecurity/api/AntPathRequestMatcher
3 允许访问以具有角色的用户开头的 URL,使用/user/USERAntPathRequestMatcher
4 允许访问以具有角色的用户开头的 URL,使用/admin/ADMINRegexRequestMatcher
5 允许访问与具有角色的用户匹配的 URL,使用自定义MyCustomRequestMatcherSUPERVISORRequestMatcher

表达 式

建议您使用类型安全授权管理器而不是 SpEL。 但是,可用于帮助迁移旧版 SpEL。WebExpressionAuthorizationManagerSpring中文文档

要使用 ,您可以使用您尝试迁移的表达式构造一个,如下所示:WebExpressionAuthorizationManagerSpring中文文档

.requestMatchers("/test/**").access(new WebExpressionAuthorizationManager("hasRole('ADMIN') && hasRole('USER')"))
.requestMatchers("/test/**").access(WebExpressionAuthorizationManager("hasRole('ADMIN') && hasRole('USER')"))

如果您在表达式中引用 bean,如下所示: ,建议您直接调用该 bean,如下所示:@webSecurity.check(authentication, request)Spring中文文档

.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)Spring中文文档

如果无法执行此操作,则可以使用 Bean 解析器配置 a 并将其提供给 .DefaultHttpSecurityExpressionHandlerWebExpressionAuthorizationManager#setExpressionhandlerSpring中文文档