Spring MVC非Boot应用OAuth2/OIDC登录页不自动重定向问题
Spring Security OAuth2/OIDC 自动重定向问题排查与修复
问题描述
在非Spring Boot的Spring MVC应用中集成Spring Security OAuth2/OIDC,预期访问受保护端点(如/page)时,系统自动重定向到OIDC提供商的登录页(携带client_id等必要参数),但实际访问受保护端点时无任何反应。已配置Spring Security但未实现预期效果,希望通过Spring Security原生能力实现自动登录重定向,无需手动编写控制器逻辑。
用户当前配置代码:
@EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { private static final Logger logger = LoggerFactory.getLogger(SecurityConfig.class); @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests(authorizeRequests -> authorizeRequests .antMatchers("/page").authenticated() .anyRequest().permitAll() ) .oauth2Login() .and() .csrf().disable(); } @Bean public ClientRegistrationRepository clientRegistrationRepository() { return new InMemoryClientRegistrationRepository(XClientRegistration()); } @Bean public OAuth2AuthorizedClientRepository authorizedClientRepository() { return new AuthenticatedPrincipalOAuth2AuthorizedClientRepository(authorizedClientService()); } @Bean public OAuth2AuthorizedClientService authorizedClientService() { return new InMemoryOAuth2AuthorizedClientService(clientRegistrationRepository()); } private ClientRegistration XClientRegistration() { logger.debug("Configuring x Client Registration"); return ClientRegistrations .fromIssuerLocation("https://CONF.com/") .clientId("xxxx") .clientSecret("xxxx") .scope("openid") .redirectUri("/OAUTH2").build(); } }
问题分析
- 非Spring Boot环境自动配置缺失:Spring Boot会自动注册OAuth2相关过滤器、端点,但纯Spring MVC环境需要手动配置这些核心组件,否则Security规则无法生效。
- 回调URI配置不规范:自定义的
/OAUTH2不符合Spring Security OAuth2的默认回调格式,导致认证流程无法闭环。 - 未明确登录触发端点:未指定OAuth2登录的入口端点,Spring Security无法引导未认证请求跳转至OIDC提供商。
- Web容器未加载Security过滤器:纯Spring MVC环境需手动配置
DelegatingFilterProxy,否则Security的拦截逻辑不会生效。
修复方案
1. 修正ClientRegistration配置
调整回调URI为Spring Security OAuth2默认格式,同时指定客户端注册ID:
private static final String CLIENT_REGISTRATION_ID = "x-client"; private ClientRegistration XClientRegistration() { logger.debug("Configuring x Client Registration"); return ClientRegistrations .fromIssuerLocation("https://CONF.com/") .clientId("xxxx") .clientSecret("xxxx") .scope("openid", "profile", "email") // 补充必要的OIDC权限范围 .redirectUri("{baseUrl}/login/oauth2/code/" + CLIENT_REGISTRATION_ID) .registrationId(CLIENT_REGISTRATION_ID) .build(); }
2. 完善HttpSecurity配置
明确登录触发端点,并绑定客户端配置:
@Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests(authorizeRequests -> authorizeRequests .antMatchers("/page").authenticated() .antMatchers("/login/oauth2/code/**", "/oauth2/authorization/**").permitAll() // 允许回调和登录端点访问 .anyRequest().permitAll() ) .oauth2Login(oauth2 -> oauth2 .loginPage("/oauth2/authorization/" + CLIENT_REGISTRATION_ID) // 指定OAuth2登录入口 .clientRegistrationRepository(clientRegistrationRepository()) .authorizedClientRepository(authorizedClientRepository()) ) .csrf().disable(); }
3. 配置Web容器加载Security过滤器
在web.xml中添加DelegatingFilterProxy,确保Spring Security的拦截链生效:
<filter> <filter-name>springSecurityFilterChain</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter> <filter-mapping> <filter-name>springSecurityFilterChain</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
4. 确保依赖完整(纯Spring MVC环境)
手动引入OAuth2相关依赖(Maven示例):
<dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-oauth2-client</artifactId> <version>5.7.11</version> <!-- 版本需与Spring核心版本匹配 --> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-oauth2-jose</artifactId> <version>5.7.11</version> </dependency>
完整修改后代码
@EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { private static final Logger logger = LoggerFactory.getLogger(SecurityConfig.class); private static final String CLIENT_REGISTRATION_ID = "x-client"; @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests(authorizeRequests -> authorizeRequests .antMatchers("/page").authenticated() .antMatchers("/login/oauth2/code/**", "/oauth2/authorization/**").permitAll() .anyRequest().permitAll() ) .oauth2Login(oauth2 -> oauth2 .loginPage("/oauth2/authorization/" + CLIENT_REGISTRATION_ID) .clientRegistrationRepository(clientRegistrationRepository()) .authorizedClientRepository(authorizedClientRepository()) ) .csrf().disable(); } @Bean public ClientRegistrationRepository clientRegistrationRepository() { return new InMemoryClientRegistrationRepository(XClientRegistration()); } @Bean public OAuth2AuthorizedClientRepository authorizedClientRepository() { return new AuthenticatedPrincipalOAuth2AuthorizedClientRepository(authorizedClientService()); } @Bean public OAuth2AuthorizedClientService authorizedClientService() { return new InMemoryOAuth2AuthorizedClientService(clientRegistrationRepository()); } private ClientRegistration XClientRegistration() { logger.debug("Configuring x Client Registration"); return ClientRegistrations .fromIssuerLocation("https://CONF.com/") .clientId("xxxx") .clientSecret("xxxx") .scope("openid", "profile", "email") .redirectUri("{baseUrl}/login/oauth2/code/" + CLIENT_REGISTRATION_ID) .registrationId(CLIENT_REGISTRATION_ID) .build(); } }
内容的提问来源于stack exchange,提问作者Siham Touzani
相关产品推荐
相关产品推荐

