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-clientaccess_token、refresh_token的JSON响应。
步骤3:访问受保护资源
调用/hello接口时,在请求头中携带访问令牌:
GET http://localhost:8080/hello Authorization: Bearer 步骤2获取的access_token
配置修正建议
针对当前配置,需做以下调整以适配场景:
- 添加资源访问范围:在注册客户端时补充
user.read范围.scope(OidcScopes.OPENID) .scope(OidcScopes.PROFILE) .scope("user.read") // 新增该范围 - 优化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(); } - 若客户端为跨域应用,需确保CORS配置正确,避免请求被拦截。
内容的提问来源于stack exchange,提问作者xmlParser
相关产品推荐
相关产品推荐

