Spring Security 7.0 迁移指南
重要说明: Spring Security 7.0 包含重大更改。在升级到 7.0 之前,请使用 Spring Security 6.5 作为准备版本。Spring Security 6.5 允许你逐步迁移单个功能,使升级风险降至最低。
有关新功能的详细信息,请参阅 What's New。
目录
- 概述
- 版本要求
- 依赖更新
- 核心API变更
- XML配置更新
- Java配置变更
- 授权配置
- 请求匹配器
- Jackson迁移
- [OAuth 2.0变更](#OAuth 2.0变更)
- [SAML 2.0变更](#SAML 2.0变更)
- Web安全变更
- 模块特定变更
- 测试更新
- 验证和测试
- 常见问题
- 总结
概述
本指南涵盖了从 Spring Security 6.x 迁移到 7.0 的步骤。Spring Security 7.0 代表了框架的重大现代化,移除了长期弃用的 API 并提高了一致性。
主要迁移主题:
- 授权API: 从 AccessDecisionManager 完全迁移到 AuthorizationManager
- 配置 : 移除
and()方法,XML schema 版本要求 - 请求匹配: 基于 PathPatternRequestMatcher 的标准化
- Jackson: 迁移到 Jackson 3
- OAuth 2.0: 默认启用 PKCE,移除密码授权
- SAML 2.0: 改进的规范合规性,要求 OpenSAML 5
- 模块架构: 集成以前独立的项目
版本要求
在迁移到 Spring Security 7.0 之前,请确保你的项目满足以下版本要求:
- Spring Framework: 6.x
- Spring Boot: 4.0.x
- Java: 17+
- Jakarta EE: 9+
版本兼容性矩阵
| Spring Security | Spring Boot | Java |
|---|---|---|
| 7.0.x | 4.0.x | 17+ |
| 6.5.x | 3.2.x | 17+ |
| 6.4.x | 3.1.x | 17+ |
依赖更新
Maven 配置
在 pom.xml 中更新以下依赖:
xml
<!-- Spring Boot 4.0 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>4.0.0</version>
<relativePath/>
</parent>
<!-- Spring Security 7.0 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- 如果使用 OAuth 2.0 授权服务器 -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-authorization-server</artifactId>
</dependency>
<!-- 如果使用 SAML 2.0 -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-saml2-service-provider</artifactId>
</dependency>
<!-- 如果使用 LDAP -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-ldap</artifactId>
</dependency>
Gradle 配置
在 build.gradle 中更新依赖:
gradle
// Spring Boot 4.0
plugins {
id 'org.springframework.boot' version '4.0.0'
id 'io.spring.dependency-management' version '1.1.4'
}
// Spring Security 7.0
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-security'
// 可选依赖
implementation 'org.springframework.security:spring-security-oauth2-authorization-server'
implementation 'org.springframework.security:spring-security-saml2-service-provider'
implementation 'org.springframework.boot:spring-boot-starter-ldap'
}
核心API变更
AuthorizationManager#check 移除
AuthorizationManager 接口中的 check 方法已被移除。使用 authorize 方法替代。
迁移前 (6.x):
java
AuthorizationManager<RequestAuthorizationContext> authorizationManager = (authentication, object) -> {
// ... 授权逻辑
authorizationDecision = check(authentication, object);
};
迁移后 (7.0):
java
AuthorizationManager<RequestAuthorizationContext> authorizationManager = (authentication, object) -> {
// ... 授权逻辑
authorizationDecision = AuthorizationDecision.from(hasRole("ADMIN"));
};
Access Decision API
如果你使用 Access Decision API,需要添加 spring-security-access 模块。
Maven:
xml
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-access</artifactId>
</dependency>
Gradle:
gradle
implementation 'org.springframework.security:spring-security-access'
新增 Authentication.Builder API
Spring Security 7.0 引入了新的 Authentication.Builder API 用于构建身份验证对象:
java
// 构建用户名密码认证
Authentication authentication = UsernamePasswordAuthenticationToken.builder()
.principal("user")
.credentials("password")
.authorities("ROLE_USER")
.build();
// 构建 OAuth2 认证
OAuth2AuthenticationToken oauth2Token = OAuth2AuthenticationToken.builder()
.principal(oauth2User)
.clientRegistrationId("google")
.authorizedScopes(Arrays.asList("openid", "profile"))
.build();
XML配置更新
命名空间Schema要求
Spring Security 7.0 要求 XML 配置使用 7.0 schema。更新你的 XML 配置:
迁移前 (6.x):
xml
<beans xmlns:security="http://www.springframework.org/schema/security"
xsi:schemaLocation="
http://www.springframework.org/schema/security
https://www.springframework.org/schema/security/spring-security-6.0.xsd">
迁移后 (7.0) - 选项1:显式版本
xml
<beans xmlns:security="http://www.springframework.org/schema/security"
xsi:schemaLocation="
http://www.springframework.org/schema/security
https://www.springframework.org/schema/security/spring-security-7.0.xsd">
迁移后 (7.0) - 选项2:无版本(推荐)
xml
<beans xmlns:security="http://www.springframework.org/schema/security"
xsi:schemaLocation="
http://www.springframework.org/schema/security
https://www.springframework.org/schema/security/spring-security.xsd">
SecurityNamespaceHandler 在解析时强制执行此要求。
Java配置变更
移除 and() 方法
HttpSecurity DSL 中的 and() 方法已被移除。使用基于lambda的配置替代。
迁移前 (6.x):
java
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authz -> authz
.requestMatchers("/public/**").permitAll()
.anyRequest().authenticated()
)
.and()
.formLogin(form -> form
.loginPage("/login")
.permitAll()
)
.and()
.logout(logout -> logout.permitAll());
return http.build();
}
迁移后 (7.0):
java
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authz -> authz
.requestMatchers("/public/**").permitAll()
.anyRequest().authenticated()
)
.formLogin(form -> form
.loginPage("/login")
.permitAll()
)
.logout(logout -> logout.permitAll());
return http.build();
}
授权配置
移除 authorizeRequests()
authorizeRequests() 方法已被移除。使用 authorizeHttpRequests() 替代。
迁移:
java
// 迁移前 (6.x)
http.authorizeRequests(authz -> authz
.requestMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
);
// 迁移后 (7.0)
http.authorizeHttpRequests(authz -> authz
.requestMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
);
authorizeHttpRequests() 使用 AuthorizationManager API 而不是传统的 AccessDecisionManager。
请求匹配器
移除 MvcRequestMatcher 和 AntPathRequestMatcher
MvcRequestMatcher 和 AntPathRequestMatcher 已被移除。使用 PathPatternRequestMatcher 替代。
迁移:
java
// 迁移前 (6.x)
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
http.authorizeHttpRequests(authz -> authz
.requestMatchers(new AntPathRequestMatcher("/api/**")).hasRole("API")
);
// 迁移后 (7.0)
http.authorizeHttpRequests(authz -> authz
.requestMatchers("/api/**").hasRole("API") // 默认使用 PathPattern
);
默认的请求匹配策略现在基于 Spring 的 PathPattern,它比 Ant 样式的模式提供更好的性能和更多功能。
Jackson迁移
Spring Security 7.0 默认使用 Jackson 3。Jackson 2 支持已弃用。
更新 ObjectMapper 配置
迁移前 (Jackson 2):
java
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.security.jackson2.SecurityJackson2Modules;
ObjectMapper mapper = new ObjectMapper();
mapper.registerModules(SecurityJackson2Modules.getModules(
getClass().getClassLoader()));
迁移后 (Jackson 3):
java
import tools.jackson.databind.json.JsonMapper;
import org.springframework.security.jackson3.SecurityJacksonModules;
JsonMapper.Builder builder = JsonMapper.builder();
SecurityJacksonModules.getModules(getClass().getClassLoader())
.forEach(builder::addModule);
JsonMapper mapper = builder.build();
替换单个模块
迁移前 (Jackson 2):
java
import org.springframework.security.jackson2.CoreJackson2Module;
mapper.registerModule(new CoreJackson2Module());
迁移后 (Jackson 3):
java
import org.springframework.security.jackson3.SecurityJacksonModules;
// 使用自动模块检测
SecurityJacksonModules.getModules(getClass().getClassLoader())
.forEach(builder::addModule);
授权服务器配置
如果使用 spring-security-oauth2-authorization-server,它现在默认使用 Jackson 3。继续使用 Jackson 2(不推荐):
Maven:
xml
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-authorization-server</artifactId>
<exclusions>
<exclusion>
<groupId>tools.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
OAuth 2.0变更
JWT类型验证
Spring Security 7.0 默认验证 typ 头部。如果你在 6.5 中禁用了此功能,可以移除这些配置。
Servlet实现:
java
// 6.5 准备 (7.0中可以移除)
@Bean
JwtDecoder jwtDecoder() {
NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withIssuerLocation(location)
.validateTypes(false) // ← 移除这行
.build();
jwtDecoder.setJwtValidator(JwtValidators.createDefaultWithValidators(
new JwtIssuerValidator(location),
JwtTypeValidator.jwt())); // ← 移除显式 JwtTypeValidator
return jwtDecoder;
}
// 7.0 (简化)
@Bean
JwtDecoder jwtDecoder() {
NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withIssuerLocation(location)
.build(); // validateTypes 默认为 false,包含 JwtTypeValidator
jwtDecoder.setJwtValidator(JwtValidators.createDefaultWithIssuer(location));
return jwtDecoder;
}
Reactive实现:
java
// 6.5 准备 (7.0中可以移除)
@Bean
NimbusReactiveJwtDecoder jwtDecoder() {
NimbusReactiveJwtDecoder jwtDecoder =
NimbusReactiveJwtDecoder.withIssuerLocation(location)
.validateTypes(false) // ← 移除这行
.build();
jwtDecoder.setJwtValidator(JwtValidators.createDefaultWithValidators(
new JwtIssuerValidator(location),
JwtTypeValidator.jwt())); // ← 移除显式 JwtTypeValidator
return jwtDecoder;
}
// 7.0 (简化)
@Bean
NimbusReactiveJwtDecoder jwtDecoder() {
NimbusReactiveJwtDecoder jwtDecoder =
NimbusReactiveJwtDecoder.withIssuerLocation(location)
.build(); // validateTypes 默认为 false,包含 JwtTypeValidator
jwtDecoder.setJwtValidator(JwtValidators.createDefaultWithIssuer(location));
return jwtDecoder;
}
BearerTokenAuthenticationFilter配置
BearerTokenAuthenticationFilter 上的 setBearerTokenResolver() 和 setAuthenticationDetailsSource() 方法已弃用。在 BearerTokenAuthenticationConverter 上配置这些。
迁移:
java
// 迁移前 (6.x)
BearerTokenAuthenticationFilter filter =
new BearerTokenAuthenticationFilter(authenticationManager);
filter.setBearerTokenResolver(myBearerTokenResolver);
filter.setAuthenticationDetailsSource(myAuthenticationDetailsSource);
// 迁移后 (7.0)
BearerTokenAuthenticationConverter authenticationConverter =
new BearerTokenAuthenticationConverter();
authenticationConverter.setBearerTokenResolver(myBearerTokenResolver);
authenticationConverter.setAuthenticationDetailsSource(myAuthenticationDetailsSource);
BearerTokenAuthenticationFilter filter =
new BearerTokenAuthenticationFilter(authenticationManager, authenticationConverter);
移除密码授权
OAuth 2.0 密码授权已被移除,因为它不再被 OAuth 2.0 规范推荐。适当地迁移到授权码流与 PKCE 或客户端凭证流。
PKCE默认启用
使用 spring-security-oauth2-authorization-server 时,PKCE(授权码交换证明密钥)现在对所有授权码流默认启用。这提高了安全性,无需配置更改。
SAML 2.0变更
LogoutResponse行为
在 Spring Security 7.0 中,当 <saml2:LogoutRequest> 验证失败时,Spring Security 现在返回错误 <saml2:LogoutResponse> 而不是返回 HTTP 401。这符合 SAML 2.0 规范要求。
选择退出(如果需要):
java
@Bean
Saml2LogoutResponseResolver logoutResponseResolver(
RelyingPartyRegistrationRepository registrations) {
OpenSaml5LogoutResponseResolver delegate =
new OpenSaml5LogoutResponseResolver(registrations);
return new Saml2LogoutResponseResolver() {
@Override
public Saml2LogoutResponse resolve(HttpServletRequest request,
Authentication authentication) {
return delegate.resolve(request, authentication);
}
@Override
public Saml2LogoutResponse resolve(HttpServletRequest request,
Authentication authentication,
Saml2AuthenticationException error) {
return null; // 恢复到 6.x 行为
}
};
}
AssertingPartyDetails移除
AssertingPartyDetails 类已被移除。使用 AssertingPartyMetadata 接口替代。
迁移通常涉及更新方法签名:
java
// 迁移前 (6.x)
public void processAssertion(AssertingPartyDetails details) {
String entityId = details.getEntityId();
// ...
}
// 迁移后 (7.0)
public void processAssertion(AssertingPartyMetadata metadata) {
String entityId = metadata.getEntityId();
// ...
}
Saml2AuthenticatedPrincipal弃用
Spring Security 7.0 将断言详情与主体分离。凭证现在实现 Saml2ResponseAssertionAccessor 而不是使用 Saml2AuthenticatedPrincipal。
默认行为使用 Saml2AssertionAuthentication,它自动处理此功能。
自定义实现可能需要调整:
java
// 迁移前 (6.x)
public Authentication authenticate(Authentication authentication) {
// ... 认证逻辑
Saml2AuthenticatedPrincipal principal = createPrincipal(response);
return new Saml2Authentication(principal, saml2Response, authorities);
}
// 迁移后 (7.0 - 如果使用默认)
// ResponseAuthenticationConverter 现在返回 Saml2AssertionAuthentication
// 如果使用默认设置则无需更改
// 迁移后 (7.0 - 如果自定义)
@Bean
OpenSaml5AuthenticationProvider authenticationProvider() {
OpenSaml5AuthenticationProvider provider =
new OpenSaml5AuthenticationProvider();
ResponseAuthenticationConverter defaults =
new ResponseAuthenticationConverter();
// 包装以返回 Saml2AssertionAuthentication
provider.setResponseAuthenticationConverter(
defaults.andThen(authentication ->
new Saml2AssertionAuthentication(
authentication.getPrincipal(),
authentication.getSaml2Response(),
authentication.getAuthorities()
)
)
);
return provider;
}
GET请求支持移除
Saml2AuthenticationTokenConverter 和 OpenSaml5AuthenticationTokenConverter 默认不再处理 GET 请求,因为 SAML 2.0 规范不支持通过 GET 的 <saml2:Response>。
这是推荐的行为。如果必须支持 GET 请求(不推荐),请显式启用:
java
@Bean
OpenSaml5AuthenticationTokenConverter authenticationConverter(
RelyingPartyRegistrationRepository registrations) {
OpenSaml5AuthenticationTokenConverter converter =
new OpenSaml5AuthenticationTokenConverter(registrations);
converter.setShouldConvertGetRequests(true); // 不推荐
return converter;
}
需要OpenSAML 5
OpenSAML 4 支持已移除。迁移到 OpenSAML 5。
Maven:
xml
<dependency>
<groupId>org.opensaml</groupId>
<artifactId>opensaml-core</artifactId>
<version>5.0.0</version>
</dependency>
<dependency>
<groupId>org.opensaml</groupId>
<artifactId>opensaml-saml-api</artifactId>
<version>5.0.0</version>
</dependency>
<dependency>
<groupId>org.opensaml</groupId>
<artifactId>opensaml-saml-impl</artifactId>
<version>5.0.0</version>
</dependency>
Web安全变更
会话并发控制
SessionLimit 函数式接口提供更灵活的会话控制策略。虽然基于整数的 API 仍然支持,但函数式方法允许按用户动态会话限制。
java
// 简单方法(仍受支持)
http.sessionManagement(session -> session
.sessionConcurrency(concurrency -> concurrency
.maximumSessions(1)
.maxSessionsPreventsLogin(true)
)
);
// 函数式方法(7.0特性)
http.sessionManagement(session -> session
.sessionConcurrency(concurrency -> concurrency
.maximumSessions(SessionLimit.of(authentication -> {
// 基于用户角色的动态限制
if (hasRole(authentication, "ADMIN")) {
return -1; // 管理员无限制
}
return 1; // 普通用户限制
}))
.maxSessionsPreventsLogin(true)
)
);
默认登录页面多因素支持
默认登录页面现在支持基于 factor.type 和 factor.reason 请求参数显示多因素认证提示。这不需要迁移但提供了增强的功能。
模块特定变更
Kerberos集成
Spring Security Kerberos 扩展现在是 Spring Security 主项目的一部分。将导入从扩展项目更新到主项目。
java
// 迁移前 (使用扩展)
import org.springframework.security.extensions.kerberos.*;
// 迁移后 (7.0)
import org.springframework.security.kerberos.*;
Spring授权服务器
Spring授权服务器现在是Spring Security的一部分。相应更新依赖:
Maven:
xml
<!-- 迁移前 (独立项目) -->
<dependency>
<groupId>org.springframework.security.experimental</groupId>
<artifactId>spring-authorization-server</artifactId>
</dependency>
<!-- 迁移后 (7.0) -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-authorization-server</artifactId>
</dependency>
LDAP变更
ApacheDsContainer 和相关 Apache DS 支持已移除。使用 UnboundID 进行嵌入式 LDAP 服务器。
迁移:
xml
<!-- 迁移前 (6.x - Apache DS) -->
<ldap-server mode="apacheds" />
<!-- 迁移后 (7.0 - UnboundID) -->
<ldap-server mode="unboundid" />
或在 Java 配置中:
java
// 迁移后 (7.0)
@Bean
EmbeddedLdapServerContextSourceFactoryBean contextSourceFactoryBean() {
EmbeddedLdapServerContextSourceFactoryBean factory =
new EmbeddedLdapServerContextSourceFactoryBean();
factory.setPort(0); // 随机端口
return factory;
}
测试更新
MockMvc匹配器
Spring Security 7.0 添加了新的测试工具用于验证权限:
java
// 7.0 新增
mockMvc.perform(get("/api/resource")
.with(user("testuser")))
.andExpect(status().isOk())
.andExpect(withAuthorities("ROLE_USER", "ROLE_ADMIN"));
验证和测试
完成迁移步骤后,执行以下验证:
检查清单
- ✅ 应用程序启动时 XML 配置没有 BeanDefinitionParsingException
- ✅ 所有安全过滤器正确注册(检查过滤器链的日志)
- ✅ 所有配置的机制的身份验证都正常工作
- ✅ 授权规则正确执行
- ✅ 会话管理按预期工作
- ✅ CSRF 保护正常工作(如果启用)
- ✅ OAuth 2.0 流成功完成(如果适用)
- ✅ SAML 2.0 认证和注销工作(如果适用)
- ✅ 自定义 AuthorizationManager 实现正常工作
- ✅ 方法安全注解正确执行
- ✅ 集成测试通过
- ✅ 没有已移除 API 的弃用警告
常见问题
| 问题 | 解决方案 |
|---|---|
| XML schema 的 BeanDefinitionParsingException | 将 schema 位置更新为 spring-security-7.0.xsd 或 spring-security.xsd |
| Access Decision API 的 ClassNotFoundException | 添加 spring-security-access 依赖 |
and() 方法的编译错误 |
移除 and() 调用并使用 lambda DSL |
| Jackson 序列化失败 | 完成 Jackson 2 到 Jackson 3 的迁移 |
| SAML 注销链中断 | 验证 Saml2LogoutResponseResolver 返回错误响应 |
| 请求匹配器错误 | 将 AntPathRequestMatcher/MvcRequestMatcher 替换为 PathPatternRequestMatcher |
配置实体引用
关键类和接口
| 组件 | 位置 | 目的 |
|---|---|---|
| SecurityNamespaceHandler | config/src/main/java/org/springframework/security/config/SecurityNamespaceHandler.java |
解析 XML 安全命名空间,强制执行版本要求 |
| SpringSecurityCoreVersion | core/src/main/java/org/springframework/security/core/SpringSecurityCoreVersion.java |
版本检查和兼容性验证 |
| PathPatternRequestMatcher | web/src/main/java/org/springframework/security/web/util/matcher/ |
替换 Ant/MVC 匹配器的默认请求匹配器 |
| AuthorizationManager | org.springframework.security.authorization.AuthorizationManager |
核心授权 API |
| BearerTokenAuthenticationConverter | org.springframework.security.oauth2.server.resource.web.authentication.BearerTokenAuthenticationConverter |
OAuth 2.0 承载令牌处理配置 |
| Saml2ResponseAssertionAccessor | org.springframework.security.saml2.provider.service.authentication.Saml2ResponseAssertionAccessor |
SAML 断言访问接口 |
| SessionLimit | web/src/main/java/org/springframework/security/web/authentication/session/SessionLimit.java |
会话限制的函数式接口 |
| AssertingPartyMetadata | org.springframework.security.saml2.provider.service.registration.AssertingPartyMetadata |
SAML 断言方配置接口 |
总结
Spring Security 7.0 代表了框架的重大现代化,移除了长期弃用的 API 并提高了一致性。关键迁移主题包括:
- 授权API : 从
AccessDecisionManager到AuthorizationManager的完全转换 - 配置 : 移除
and()方法,XML schema 版本要求 - 请求匹配 : 基于
PathPatternRequestMatcher的标准化 - Jackson: 迁移到 Jackson 3
- OAuth 2.0: 默认启用 PKCE 的增强安全性,移除密码授权
- SAML 2.0: 改进的规范合规性,要求 OpenSAML 5
- 模块架构: 集成以前独立的项目
通过遵循本指南中的步骤并使用 Spring Security 6.5 作为准备版本,你可以自信地迁移到 7.0。对于本指南未涵盖的应用程序特定迁移场景,请咨询本指南开头链接的详细迁移页面。
相关资源: