Spring OAuth2体系下客户端通过WebClient调用资源服务器返回401问题排查求助
Spring OAuth2体系下客户端通过WebClient调用资源服务器返回401问题排查求助
各位好,我正在研究Spring新的Authorization Server用法,搭建了三个服务:
- 端口9090的授权服务器,依赖
spring-boot-starter-oauth2-authorization-server - 端口8081的资源服务器,依赖
spring-boot-starter-oauth2-resource-server - 端口8080的客户端,依赖
spring-boot-starter-oauth2-client
登录后能正常加载客户端的index.html页面,但调用资源服务器接口时一直返回401错误。我希望不用网关,直接通过WebClient自动获取AccessToken并携带到请求中,麻烦帮忙看看哪里配置漏了?
授权服务器SecurityConfig配置
@Configuration public class SecurityConfig { //An instance of RegisteredClientRepository for managing clients. @Bean RegisteredClientRepository registeredClientRepository() { var clientId = "XXX"; RegisteredClient clientThymeleaf = RegisteredClient .withId(UUID.randomUUID().toString()) .clientId(clientId) .clientSecret("YYY") .clientAuthenticationMethods(clientAuthenticationMethods -> clientAuthenticationMethods.addAll(Set.of( ClientAuthenticationMethod.CLIENT_SECRET_BASIC ))) .authorizationGrantTypes(grandTypes -> grandTypes.addAll(Set.of( AuthorizationGrantType.AUTHORIZATION_CODE, AuthorizationGrantType.REFRESH_TOKEN ))) .redirectUri("http://127.0.0.1:8080/login/oauth2/code/spring") .postLogoutRedirectUri("http://127.0.0.1:8080/") .scopes(scopes -> scopes.addAll(Set.of( "product.read", OidcScopes.OPENID ))) .clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build()) .build(); return new InMemoryRegisteredClientRepository(clientThymeleaf); } }
资源服务器配置与代码
YAML配置
spring : security: oauth2: resourceserver: jwt: issuer-uri: http://localhost:9000
ProduitController
@RestController @RequestMapping("/api/produits") public class ProduitController { private Logger log = LoggerFactory.getLogger(ProduitController.class); @Autowired private ProduitService produitService; @GetMapping public ResponseEntity<List<Produit>> listerProduits(Principal principal) { log.info("\n** User {} try to get all product.\n", principal.getName()); List<Produit> produits = produitService.listerProduits(); return ResponseEntity.ok(produits); } }
ProduitService
@Service public class ProduitService { @Autowired private ProduitRepository produitRepository; @PreAuthorize("hasAuthority('SCOPE_product.read')") public List<Produit> listerProduits() { return produitRepository.findAll(); } }
客户端配置与代码
YAML配置
spring: security: oauth2: client: provider: spring: issuer-uri: http://localhost:9090 registration: spring: provider: spring client-id: XXX client-secret: YYY authorization-grant-type: authorization_code client-authentication-method: client_secret_basic redirect-uri: "{baseUrl}/login/oauth2/code/{registrationId}" scope: product.read, openid
ProduitController
@Controller public class ProduitController { private Logger log = LoggerFactory.getLogger(ProduitController.class); @Autowired private ProduitService produitService; @GetMapping("/produits") public String listerProduits(Model model) { log.info("\n** Show Page List Products\n"); Mono<List<Produit>> produits = produitService.getProduits(); log.info("\n** Lists de produits obtenus\n"); model.addAttribute("produits", produits); return "listeProduits"; } }
ProduitService
@Service public class ProduitService { @Value("${ressource-uri.products}") private String ressource_products_uri; private WebClient webClient; public ProduitService(WebClient webClient) { this.webClient = webClient; } public Mono<List<Produit>> getProduits() { return webClient .get() .uri(ressource_products_uri) .retrieve() .bodyToMono(new ParameterizedTypeReference<List<Produit>>() { }); } }
WebClient配置
@Configuration public class WebClientConfiguration { @Bean WebClient webClient(ReactiveOAuth2AuthorizedClientManager authorizedClientManager) { ServerOAuth2AuthorizedClientExchangeFilterFunction oauth = new ServerOAuth2AuthorizedClientExchangeFilterFunction( authorizedClientManager); oauth.setDefaultOAuth2AuthorizedClient(true); // @formatter:off return WebClient.builder() .filter(oauth) .build(); // @formatter:on } @Bean ReactiveOAuth2AuthorizedClientManager authorizedClientManager( ReactiveClientRegistrationRepository clientRegistrationRepository, ServerOAuth2AuthorizedClientRepository authorizedClientRepository) { // @formatter:off ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider = ReactiveOAuth2AuthorizedClientProviderBuilder.builder() .authorizationCode() // .refreshToken() // .clientCredentials() // .password() .build(); // @formatter:on DefaultReactiveOAuth2AuthorizedClientManager authorizedClientManager = new DefaultReactiveOAuth2AuthorizedClientManager( clientRegistrationRepository, authorizedClientRepository); authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider); return authorizedClientManager; } }
自己梳理的排查方向
我初步想到几个可能的问题点,也想请大家帮忙验证:
- 资源服务器Issuer URI不匹配:资源服务器配置里的
issuer-uri写的是http://localhost:9000,但我的授权服务器实际跑在9090端口,这应该是笔误吧?是不是要改成http://localhost:9090? - WebClient Token携带问题:虽然设置了
oauth.setDefaultOAuth2AuthorizedClient(true),但不确定实际请求是否真的带上了Authorization: Bearer <token>头,有没有简单的方式验证这点? - 权限校验逻辑问题:资源服务器的
@PreAuthorize("hasAuthority('SCOPE_product.read')")是否能正确识别JWT里的scope?有没有相关日志可以查看权限校验的详细过程?
备注:内容来源于stack exchange,提问作者Madiagne DIAGNE
相关产品推荐
相关产品推荐

