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

Spring Boot中Auth Server与Resource Server同部署及客户端访问问题

同Spring Boot应用部署Auth Server与Resource Server的问题

我此前已实现Auth Server、Resource Server、客户端三者分离的部署模式,现在希望将Auth Server与Resource Server部署在同一个Spring Boot应用中,但配置后无法正常运行。现有配置代码如下:

安全配置类

@Configuration
@EnableWebSecurity
public class SecurityConfig {

@Bean
@Order(1)
SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http,
        CorsConfigurationSource corsConfigurationSource) throws Exception {
    OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
    http.getConfigurer(OAuth2AuthorizationServerConfigurer.class).oidc(Customizer.withDefaults());

    http.exceptionHandling(
            (exceptions) -> exceptions.authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/login")))
            .oauth2ResourceServer((resourceServer) -> resourceServer.jwt(Customizer.withDefaults()));

    http.cors(customizer -> customizer.configurationSource(corsConfigurationSource));
    return http.build();

}

@Bean
@Order(2)
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
    http.authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated())
            .oauth2ResourceServer((oauth2) -> oauth2.jwt(Customizer.withDefaults()))
            .formLogin(Customizer.withDefaults());

    return http.build();
}

@Bean
UserDetailsService userDetailsService() {
    UserDetails userDetails = User.withDefaultPasswordEncoder().roles("user").username("vlatko").password("pw")
            .build();

    return new InMemoryUserDetailsManager(userDetails);
}

@Bean
RegisteredClientRepository registeredClientRepository() {
    RegisteredClient oidcClient = RegisteredClient.withId(UUID.randomUUID().toString()).clientId("oidc-client")
            .clientSecret("{noop}secret").clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
            .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
            .authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
            .redirectUri("http://localhost:8080/login/oauth2/code/oidc-client")
            .postLogoutRedirectUri("http://localhost:8080/").scope(OidcScopes.OPENID).scope(OidcScopes.PROFILE)
            .clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build()).build();

    return new InMemoryRegisteredClientRepository(oidcClient);
}
@Bean
AuthorizationServerSettings authorizationServerSettings() {
    return AuthorizationServerSettings.builder().build();
}
}

资源控制器与服务类

@RestController
public class ResourceController {

private final ResourceService service;

ResourceController(ResourceService service) {
    this.service = service;
}

@GetMapping("/hello")
Map<String, String> hello() {
    return service.sayHello();
}
}

@Service
public class ResourceService {

@PreAuthorize("hasAuthority('SCOPE_user.read')")
public Map<String, String> sayHello() {
    var jwt = (Jwt) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    return Map.of("message", "Hello " + jwt.getSubject());
}
}

遇到的问题

  • 使用无OAuth依赖的独立客户端访问/hello接口时,收到401 Unauthorized响应
  • 使用oauthdebugger获取授权码后,请求http://localhost:8080/oauth2/token同样返回401

问题解答

1. 是否可将Auth Server与Resource Server同部署,同时使用无OAuth配置的独立客户端?

可以。Spring Security OAuth2授权服务器支持与资源服务器共部署在同一Spring Boot应用中,同时也允许无OAuth SDK依赖的原生客户端(如纯前端页面、curl脚本等)通过标准OAuth2流程完成交互,访问受保护资源。

2. 当前Auth Server配置是否符合该场景要求?

当前配置存在多处不符合场景的问题,是导致报错的主要原因:

  • 权限范围不匹配:资源服务的@PreAuthorize("hasAuthority('SCOPE_user.read')")要求令牌包含user.read范围,但注册客户端时仅配置了OPENID和PROFILE范围,未添加user.read,导致令牌缺少必要权限,访问资源时被拦截。
  • Token端点认证失败:请求/oauth2/token返回401,大概率是客户端凭证校验失败。需确认两点:
    • 是否按CLIENT_SECRET_BASIC要求,在请求头中携带了Authorization: Basic [base64编码的clientId:clientSecret](即对oidc-client:secret做base64编码)
    • 客户端密码配置为{noop}secret,说明使用明文校验,发送的密码必须是secret
  • SecurityFilterChain配置冗余冲突:第一个FilterChain(Order(1))已配置oauth2ResourceServer,第二个FilterChain(Order(2))重复配置,且anyRequest().authenticated()会覆盖部分资源访问规则,易导致权限逻辑混乱。

3. 无OAuth配置的客户端应如何完成OAuth2.0交互以访问受保护资源?

以授权码流程为例,无OAuth依赖的客户端需手动完成以下三步:

步骤1:获取授权码

构造授权请求URL,跳转至授权服务器的授权端点:

GET http://localhost:8080/oauth2/authorize?client_id=oidc-client&response_type=code&scope=openid profile user.read&redirect_uri=http://localhost:8080/login/oauth2/code/oidc-client&state=随机字符串

登录用户账号并同意授权后,授权服务器会重定向到指定的redirect_uri,并在URL参数中返回code(授权码)和state。

步骤2:交换访问令牌

向/oauth2/token端点发送POST请求,携带授权码与客户端凭证:

  • 请求头:Authorization: Basic b2lkYy1jbGllbnQ6c2VjcmV0(oidc-client:secret的base64编码结果)
  • 请求体(form-data格式):
    grant_type=authorization_code
    code=步骤1获取的授权码
    redirect_uri=http://localhost:8080/login/oauth2/code/oidc-client
    
    成功请求后,会返回包含access_token、refresh_token的JSON响应。

步骤3:访问受保护资源

调用/hello接口时,在请求头中携带访问令牌:

GET http://localhost:8080/hello
Authorization: Bearer 步骤2获取的access_token

配置修正建议

针对当前配置,需做以下调整以适配场景:

  1. 添加资源访问范围:在注册客户端时补充user.read范围
    .scope(OidcScopes.OPENID)
    .scope(OidcScopes.PROFILE)
    .scope("user.read") // 新增该范围
    
  2. 优化SecurityFilterChain配置:移除第二个FilterChain中的冗余oauth2ResourceServer配置,调整资源授权规则避免冲突
    @Bean
    @Order(2)
    SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
        http.authorizeHttpRequests((authorize) -> authorize
                .requestMatchers("/login").permitAll()
                .anyRequest().authenticated())
                .formLogin(Customizer.withDefaults());
        return http.build();
    }
    
  3. 若客户端为跨域应用,需确保CORS配置正确,避免请求被拦截。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 07:17:11