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

基于XML配置的Spring MVC应用迁移spring-authorization-server的配置问题

同一端口配置Spring Authorization Server与资源服务器及令牌生成流程

一、同一端口配置授权服务器与资源服务器

1. 修正授权服务器SecurityFilterChain配置

你当前配置中,exceptionHandling设置了LoginUrlAuthenticationEntryPoint,会导致客户端访问令牌端点时直接跳转到登录页,而客户端认证需要使用专属的OAuth2AuthenticationEntryPoint。另外,必须调用.apply(authorizationServerConfigurer)加载授权服务器核心过滤器,否则端点无法被正确处理。

修正后的配置:

@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
    OAuth2AuthorizationServerConfigurer authorizationServerConfigurer =
            OAuth2AuthorizationServerConfigurer.authorizationServer();

    // 兼容旧端点/api/security/oauth/token,与授权服务器默认端点合并匹配
    RequestMatcher endpointsMatcher = new OrRequestMatcher(
            authorizationServerConfigurer.getEndpointsMatcher(),
            AntPathRequestMatcher.antMatcher("/api/security/oauth/token")
    );

    http
            .securityMatcher(endpointsMatcher)
            .authorizeHttpRequests(authorize -> authorize.anyRequest().authenticated())
            .csrf(csrf -> csrf.disable())
            // 替换为客户端认证入口,避免跳转到登录页
            .exceptionHandling(exceptions -> exceptions
                    .authenticationEntryPoint(new OAuth2AuthenticationEntryPoint())
            )
            .oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults()))
            .apply(authorizationServerConfigurer); // 加载授权服务器核心配置

    return http.build();
}

2. 添加资源服务器SecurityFilterChain

创建优先级更低的FilterChain,专门处理资源API请求(如/api/**),实现同一端口下授权与资源服务共存:

@Bean
@Order(2)
public SecurityFilterChain resourceServerSecurityFilterChain(HttpSecurity http) throws Exception {
    http
            .securityMatcher("/api/**")
            .authorizeHttpRequests(authorize -> authorize.anyRequest().authenticated())
            .oauth2ResourceServer(oauth2 -> 
                // 若继续使用自定义tokenServices,配置不透明令牌校验
                oauth2.opaqueToken(token -> 
                    token.introspector(tokenStr -> {
                        // 调用原customTokenServices验证令牌并返回认证信息
                        return customTokenServices.loadAuthentication(tokenStr);
                    })
                )
                // 若改用JWT令牌,替换为:oauth2.jwt(Customizer.withDefaults())
            );

    return http.build();
}

3. 客户端详情与令牌服务配置

替换原XML中的customClientDetailsService和customTokenServices,示例如下:

@Bean
public ClientDetailsService clientDetailsService() {
    // 复用原有客户端逻辑,此处以内存实现为例
    InMemoryClientDetailsService clientService = new InMemoryClientDetailsService();
    ClientRegistration client = ClientRegistration.withRegistrationId("your-client-id")
            .clientId("your-client-id")
            .clientSecret("{noop}your-client-secret") // 生产环境需加密存储
            .authorizationGrantType(AuthorizationGrantType.PASSWORD)
            .authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
            .scope("read", "write")
            .build();
    clientService.saveClient(client);
    return clientService;
}

// 自定义令牌生成器(若需兼容原有令牌格式,可替换为自定义TokenServices)
@Bean
public OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator() {
    JwtGenerator jwtGenerator = new JwtGenerator(jwtEncoder());
    OAuth2AccessTokenGenerator accessTokenGenerator = new OAuth2AccessTokenGenerator();
    OAuth2RefreshTokenGenerator refreshTokenGenerator = new OAuth2RefreshTokenGenerator();
    return new DelegatingOAuth2TokenGenerator(jwtGenerator, accessTokenGenerator, refreshTokenGenerator);
}

// JWT编码器配置(使用JWT令牌时需要)
@Bean
public JwtEncoder jwtEncoder() {
    KeyPair rsaKeyPair = KeyPairGeneratorUtils.generateRsaKey();
    RSAPublicKey publicKey = (RSAPublicKey) rsaKeyPair.getPublic();
    RSAPrivateKey privateKey = (RSAPrivateKey) rsaKeyPair.getPrivate();
    RSAKey jwk = new RSAKey.Builder(publicKey)
            .privateKey(privateKey)
            .keyID(UUID.randomUUID().toString())
            .build();
    JWKSource<SecurityContext> jwkSource = new ImmutableJWKSet<>(new JWKSet(jwk));
    return new NimbusJwtEncoder(jwkSource);
}

二、访问/api/oauth生成访问令牌的流程(以密码模式为例)

假设已兼容旧端点/api/security/oauth/token,流程如下:

  1. 客户端发起令牌请求
    通过POST请求发送x-www-form-urlencoded格式参数到/api/security/oauth/token:

    • grant_type=password:指定授权类型为密码模式
    • username=用户账号
    • password=用户密码
    • client_id=客户端ID
    • client_secret=客户端密钥
    • scope=read write:请求的权限范围
  2. 客户端身份校验
    服务器先验证客户端的client_id和client_secret,校验失败返回401 Unauthorized。

  3. 用户身份校验
    客户端认证通过后,服务器使用用户账号密码调用AuthenticationManager完成身份验证,校验失败返回400 Bad Request。

  4. 生成并返回令牌
    用户身份验证通过后,服务器根据配置的令牌生成器生成访问令牌(Access Token)和刷新令牌(Refresh Token),返回JSON格式响应:

    {
      "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
      "token_type": "Bearer",
      "expires_in": 3600,
      "refresh_token": "O3t8y...",
      "scope": "read write"
    }
    

若使用客户端凭证模式,只需将grant_type设为client_credentials,无需用户账号密码,直接验证客户端身份后生成令牌;若使用授权码模式,需先引导用户访问/oauth2/authorize完成授权获取授权码,再用授权码换取令牌。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 02:44:53