Spring Authorization Server /auth/oauth2/authorize端点404问题排查
配置代码
@Configuration @EnableWebSecurity @Import(OAuth2AuthorizationServerConfiguration::class) class AuthorizationServerConfig( private val passwordEncoder: PasswordEncoder, @Value("\${key.store.password.private}") private val password: String ) { @Bean @Order(1) @Throws(Exception::class) fun authorizationServerSecurityFilterChain( http: HttpSecurity, jdbcOperations: JdbcOperations, registeredClientRepository: RegisteredClientRepository, jwtService: JwtService ): SecurityFilterChain { val authorizationServerConfigurer = OAuth2AuthorizationServerConfigurer.authorizationServer() http.authorizeHttpRequests { it.requestMatchers("/auth/**").permitAll() } .securityMatcher(authorizationServerConfigurer.endpointsMatcher).with( authorizationServerConfigurer ) { authorizationServer -> authorizationServer.authorizationService( JdbcOAuth2AuthorizationService( jdbcOperations, registeredClientRepository ) ).registeredClientRepository(registeredClientRepository(jdbcOperations)).authorizationConsentService( JdbcOAuth2AuthorizationConsentService( jdbcOperations, registeredClientRepository ) ) .authorizationEndpoint { it } .authorizationServerSettings(endpoints).tokenGenerator(JwtGenerator(NimbusJwtEncoder(jwkSource()))) .oidc { oidcConfigurer -> oidcConfigurer.userInfoEndpoint { userInfoEndpointConfigurer -> userInfoEndpointConfigurer.userInfoMapper { logger.info(it.accessToken.tokenValue) logger.info(it.accessToken.expiresAt?.toString()) val claims = jwtService.getAllClaims(it.accessToken.tokenValue) OidcUserInfo(claims) } } } } return http.build() } @Bean fun registeredClientRepository(jdbcOperations: JdbcOperations): RegisteredClientRepository { val adminClient = RegisteredClient.withId(UUID.randomUUID().toString()).clientId("admin-client") .clientSecret(passwordEncoder.encode("secret")).clientAuthenticationMethods { it.apply { add(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) add(ClientAuthenticationMethod.CLIENT_SECRET_POST) add(ClientAuthenticationMethod.PRIVATE_KEY_JWT) add(ClientAuthenticationMethod.TLS_CLIENT_AUTH) add(ClientAuthenticationMethod.CLIENT_SECRET_JWT) add(ClientAuthenticationMethod.SELF_SIGNED_TLS_CLIENT_AUTH) } }.authorizationGrantTypes { it.apply { add(AuthorizationGrantType.CLIENT_CREDENTIALS) add(AuthorizationGrantType.AUTHORIZATION_CODE) add(AuthorizationGrantType.REFRESH_TOKEN) add(AuthorizationGrantType.JWT_BEARER) add(AuthorizationGrantType.DEVICE_CODE) add(AuthorizationGrantType.TOKEN_EXCHANGE) } }.redirectUri("http://localhost:8081/new").scope(OidcScopes.OPENID).scope(OidcScopes.PROFILE) .scope("client.create").scope("message.write") .clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build()).build() // adminClient already persisting in DB return JdbcRegisteredClientRepository(jdbcOperations) } // Other beans (jwkSource, jwtDecoder, etc.) omitted for brevity }
问题详情
发送GET请求到http://localhost:8081/auth/oauth2/authorize?response_type=code&client_id=admin-client&redirect_uri=http://localhost:8081/new&scope=openid&state=abc123时,收到响应:
{ "timestamp": "2025-02-03T09:36:04.774+00:00", "status": 404, "error": "Not Found", "path": "/auth/oauth2/authorize" }
已尝试操作
- 验证服务器运行在8081端口,无自定义上下文路径;
- 确认redirect_uri与RegisteredClient中注册的一致;
- 检查安全配置未拦截/oauth2/authorize端点。
疑问
- 为何/auth/oauth2/authorize端点返回404 Not Found错误?
- 暴露/oauth2/authorize端点是否需要额外配置或依赖?
- 如何进一步调试该问题?
问题排查与解决方案
1. 404错误的核心原因
(1)端点路径未配置前缀
Spring Authorization Server默认授权端点为/oauth2/authorize,你请求的路径带/auth前缀,但代码中未配置该前缀。需补充AuthorizationServerSettings Bean配置路径前缀:
@Bean fun authorizationServerSettings(): AuthorizationServerSettings { return AuthorizationServerSettings.builder() .baseUrl("/auth") // 全局前缀,所有授权服务器端点都会带上/auth // 或单独指定授权端点路径:.authorizationEndpoint("/auth/oauth2/authorize") .build() }
缺少该配置时,默认端点不带/auth前缀,因此带前缀的请求会返回404。
(2)SecurityFilterChain匹配逻辑不匹配
你使用.securityMatcher(authorizationServerConfigurer.endpointsMatcher),该Matcher默认仅匹配授权服务器的默认端点路径(不带/auth前缀),导致/auth/**路径的请求不会被该FilterChain处理,进而找不到对应端点。
需调整匹配规则,让FilterChain覆盖/auth/**路径:
http.securityMatcher("/auth/**") .authorizeHttpRequests { it.requestMatchers("/auth/**").permitAll() } .with(authorizationServerConfigurer) { /* 原有授权服务器配置 */ }
2. 端点暴露的配置与依赖要求
- 依赖:确保已引入
spring-security-oauth2-authorization-server依赖,版本需与Spring Boot/Spring Security兼容(如Spring Boot 3.x对应Authorization Server 1.x)。 - 配置:无需额外暴露端点,但必须保证请求路径与授权服务器配置的端点路径一致,同时SecurityFilterChain能正确匹配这些路径。自定义前缀必须通过
AuthorizationServerSettings配置。
3. 进一步调试方法
- 启用调试日志:添加日志配置,查看请求过滤链和端点映射:
可清晰看到请求经过的过滤器,以及端点是否被正确注册。logging.level.org.springframework.security=DEBUG logging.level.org.springframework.web.servlet.mvc.method.annotation=TRACE - 检查端点映射:引入
spring-boot-starter-actuator依赖,开启mappings端点,访问/actuator/mappings查看是否存在/auth/oauth2/authorize的映射。 - 测试默认端点:直接请求
http://localhost:8081/oauth2/authorize(不带/auth前缀),若能正常响应,说明问题出在路径前缀配置。 - 确认FilterChain顺序:确保
authorizationServerSecurityFilterChain的@Order(1)优先级高于其他SecurityFilterChain,避免请求被其他过滤器拦截。
内容的提问来源于stack exchange,提问作者OmniCoder77
相关产品推荐
相关产品推荐

