You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

用户认证后Spring Authorization Server的token端点返回404问题

问题排查:Spring Authorization Server密码模式下token端点404

问题描述

搭建采用密码授权(password grant)模式的Spring Authorization Server时,用户认证流程已成功完成,但请求token端点oauth/token?grant_type=password&username=admin&password=password&scope=read返回404状态码,调试日志显示请求最终跳转到/error端点。

配置代码

@Configuration
@EnableWebSecurity
public class AuthorisationServerConfig {

    private final RSAProperties rsaProperties;
    AuthorisationServerConfig(final RSAProperties rsaProperties){
        this.rsaProperties=rsaProperties;
    }

    @Bean
    public RegisteredClientRepository registeredClientRepository() {
        RegisteredClient registeredClient = RegisteredClient.withId(UUID.randomUUID().toString())
                .clientId("client")
                .clientSecret("{noop}secret")
                .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
                .authorizationGrantType(AuthorizationGrantType.PASSWORD)
                .scope("read")
                .build();
        return new InMemoryRegisteredClientRepository(registeredClient);
    }

    @Bean
    public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity httpSecurity) throws Exception {
        //@formatter:off
        httpSecurity
                .authorizeHttpRequests(ar->ar.anyRequest().authenticated())
                .csrf().disable()
                .httpBasic();
        //@formatter:on
        return httpSecurity.build();
    }

    private static KeyPair generateRsaKey() throws NoSuchAlgorithmException {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        keyPairGenerator.initialize(2048);
        return keyPairGenerator.generateKeyPair();
    }

    @Bean
    public AuthorizationServerSettings providerSettings() {
        return AuthorizationServerSettings.builder()
                .issuer("http://localhost:9090")
                .build();
    }
    @Bean
    public JWKSource<SecurityContext> jwkSource() {
        RSAKey rsaKey = new RSAKey.Builder(rsaProperties.publicKey()).privateKey(rsaProperties.privateKey()).build();
        JWKSet jwkSet = new JWKSet(rsaKey);
        return (jwkSelector, securityContext) -> jwkSelector.select(jwkSet);
    }

    @Bean
    public JwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) {
        return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);
    }

    @Bean
    public UserDetailsService users() {
        UserDetails user = User.withDefaultPasswordEncoder()
                .username("admin")
                .password("password")
                .roles("USER")
                .build();
        return new InMemoryUserDetailsManager(user);
    }
}

日志信息

o.s.security.web.FilterChainProxy        : Securing POST /oauth/token?grant_type=password&username=admin&password=password&scope=read
o.s.s.a.dao.DaoAuthenticationProvider    : Authenticated user
o.s.s.w.a.www.BasicAuthenticationFilter  : Set SecurityContextHolder to UsernamePasswordAuthenticationToken [Principal=org.springframework.security.core.userdetails.User [Username=admin, Password=[PROTECTED], Enabled=true, AccountNonExpired=true, credentialsNonExpired=true, AccountNonLocked=true, Granted Authorities=[ROLE_USER]]
o.s.security.web.FilterChainProxy        : Secured POST /oauth/token?grant_type=password&username=admin&password=password&scope=read
o.s.security.web.FilterChainProxy        : Securing POST /error?grant_type=password&username=admin&password=password&scope=read
o.s.security.web.FilterChainProxy        : Secured POST /error?grant_type=password&username=admin&password=password&scope=read

项目依赖

plugins {
    java
    id("org.springframework.boot") version "3.0.1"
    id("io.spring.dependency-management") version "1.1.0"
}

dependencies {
    implementation("org.springframework.boot:spring-boot-starter")
    implementation("org.springframework.security:spring-security-oauth2-authorization-server:1.0.0")
    implementation("org.springframework.boot:spring-boot-autoconfigure-processor")
    testImplementation("org.springframework.boot:spring-boot-starter-test")
}

问题原因及解决方案

1. 缺少Authorization Server专属SecurityFilterChain配置

当前仅配置了通用的defaultSecurityFilterChain,但未设置专门处理OAuth2端点的SecurityFilterChain。Spring Authorization Server需要优先级更高的专属FilterChain来识别并处理/oauth2/token等端点。

添加以下配置:

@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
    OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
    return http.formLogin(Customizer.withDefaults()).build();
}

2. 端点路径错误

Spring Authorization Server 1.0.0的token端点默认路径为/oauth2/token,而非旧版OAuth2的/oauth/token,请求路径不匹配导致404。

将请求路径修改为/oauth2/token?grant_type=password&username=admin&password=password&scope=read。

3. 缺失Web环境依赖

当前依赖仅包含spring-boot-starter,缺少Web环境支持,无法处理HTTP请求。需添加:

implementation("org.springframework.boot:spring-boot-starter-web")

总结

按以下步骤修改后重新启动服务:

  • 添加优先级最高的Authorization Server SecurityFilterChain
  • 将请求端点改为/oauth2/token
  • 引入spring-boot-starter-web依赖

完成后再次请求token端点即可正常获取Token。

内容的提问来源于stack exchange,提问作者Dharmvir Tiwari

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 06:05:39