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

Spring Security 5 OAuth2使用JWT Bearer授权类型遇401错误求助

问题:Spring Security 5 OAuth2中使用JWT Bearer授权类型返回401 Unauthorized错误

我近期尝试在Spring Security 5 OAuth2中使用urn:ietf:params:oauth:grant-type:jwt-bearer授权类型,但调用http://127.0.0.1:8081/postArticles时返回401 Unauthorized错误,希望排查原因并解决。我搭建了三个项目:授权服务器(spring-security-oauth2-authorization_server,端口8080)、资源服务器(spring-security-oauth2-resource_server,端口8081)、客户端(spring-security-oauth2-client,端口8082),相关代码片段如下:


1. spring-security-oauth2-client

ArticlesController.java

@GetMapping(value = "/postArticles")
public String postArticles(
  @RegisteredOAuth2AuthorizedClient("articles-client-jwt-bearer") OAuth2AuthorizedClient authorizedClient
    ,HttpServletRequest request
    ,HttpServletResponse response
  ) 
{    
    return webClient
      .post()
      .uri("http://127.0.0.1:8081/postArticles")
      .attributes(oauth2AuthorizedClient(authorizedClient))
      .bodyValue("new articles" + new Date())
      .retrieve()
      .bodyToMono(String.class)
      .block();
    
}

WebClientConfig.java

@Bean(name = "wc")
WebClient webClient(
OAuth2AuthorizedClientManager authorizedClientManager) {
    ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Client = new ServletOAuth2AuthorizedClientExchangeFilterFunction(
            authorizedClientManager);
    return WebClient.builder().apply(oauth2Client.oauth2Configuration()).build();

}
@Bean
OAuth2AuthorizedClientManager authorizedClientManager(ClientRegistrationRepository clientRegistrationRepository,
        OAuth2AuthorizedClientRepository authorizedClientRepository) {

    OAuth2AuthorizedClientProvider authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
            .authorizationCode()
            .refreshToken()
            .clientCredentials()
            .provider(new JwtBearerOAuth2AuthorizedClientProvider())
            .build();

    DefaultOAuth2AuthorizedClientManager authorizedClientManager = new DefaultOAuth2AuthorizedClientManager(
            clientRegistrationRepository, authorizedClientRepository);
    authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
    
    return authorizedClientManager;
}

SecurityConfig.java

@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    http
//      .authorizeRequests(authorizeRequests ->
//        authorizeRequests.anyRequest().authenticated()
//      )
      .headers().frameOptions().sameOrigin() //for h2
      .and()
      .csrf().ignoringAntMatchers("/h2-console/**")
      .and()
      .oauth2Client(withDefaults())
      ;
    return http.build();
}

application-oauth2.properties

spring.security.oauth2.client.registration.articles-client-jwt-bearer.provider: spring
spring.security.oauth2.client.registration.articles-client-jwt-bearer.client-id: articles-client
spring.security.oauth2.client.registration.articles-client-jwt-bearer.client-secret: secret
spring.security.oauth2.client.registration.articles-client-jwt-bearer.authorization-grant-type: urn:ietf:params:oauth:grant-type:jwt-bearer
spring.security.oauth2.client.registration.articles-client-jwt-bearer.scope: articles.read,articles.write
spring.security.oauth2.client.registration.articles-client-jwt-bearer.client-name: articles-client-jwt-bearer


spring.security.oauth2.client.provider.spring.issuer-uri: http://auth-server:8080

2. spring-security-oauth2-authorization_server

AuthorizationServerConfig.java

@Configuration(proxyBeanMethods = false)
public class AuthorizationServerConfig {

    

    /**
     * A Spring Security filter chain for the Protocol Endpoints.
     * 
     * @param http
     * @return
     * @throws Exception
     */
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
        OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
        http
                // Redirect to the login page when not authenticated from the
                // authorization endpoint
                .exceptionHandling((exceptions) -> exceptions
                        .authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/login")))
                .oauth2ResourceServer().jwt()
                .and().and()

                ;
        return http.build();
    }

    /**
     * An instance of RegisteredClientRepository for managing clients. Registered
     * information.
     * 
     * @return
     */
    @Bean
    public RegisteredClientRepository registeredClientRepository(JdbcOperations jdbcOperations) {
        RegisteredClient registeredClient = RegisteredClient.withId(UUID.randomUUID().toString())
                .clientId("articles-client")
                .clientSecret("{noop}secret")
//              .clientName("client1")
                .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
                .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
                .authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
                .authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
                .authorizationGrantType(AuthorizationGrantType.JWT_BEARER)
                .redirectUri("http://127.0.0.1:8080/login/oauth2/code/articles-client-oidc")
                .redirectUri("http://127.0.0.1:8080/authorized")
                .scope(OidcScopes.OPENID)
                .scope(OidcScopes.PROFILE)
                .scope("articles.read")
                .scope("articles.write")
                .tokenSettings(TokenSettings.builder().accessTokenTimeToLive(Duration.ofDays(2))
                        .refreshTokenTimeToLive(Duration.ofDays(3)).build())
                .clientSettings(ClientSettings.builder().requireAuthorizationConsent(false) 
                        .build())
                .build();

        return new InMemoryRegisteredClientRepository(registeredClient); 
    }

    @Bean
    public OAuth2AuthorizationService authorizationService(JdbcOperations jdbcOperations,
            RegisteredClientRepository registeredClientRepository) {
        return new InMemoryOAuth2AuthorizationService();
    }

    @Bean
    public OAuth2AuthorizationConsentService authorizationConsentService(JdbcOperations jdbcOperations,
            RegisteredClientRepository registeredClientRepository) {
        return new InMemoryOAuth2AuthorizationConsentService();
    }

    /**
     * An instance of com.nimbusds.jose.jwk.source.JWKSource for signing access
     * tokens.
     * 
     * @return
     */
    @Bean
    public JWKSource<SecurityContext> jwkSource() {
        KeyPair keyPair = generateRsaKey();
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
        RSAKey rsaKey = new RSAKey.Builder(publicKey).privateKey(privateKey).keyID(UUID.randomUUID().toString())
                .build();
        JWKSet jwkSet = new JWKSet(rsaKey);
        return new ImmutableJWKSet<>(jwkSet);
    }

    /**
     * An instance of java.security.KeyPair with keys generated on startup used to
     * create the JWKSource above.
     * 
     * @return
     */
    private static KeyPair generateRsaKey() {
        KeyPair keyPair;
        try {
            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
            keyPairGenerator.initialize(2048);
            keyPair = keyPairGenerator.generateKeyPair();
        } catch (Exception ex) {
            throw new IllegalStateException(ex);
        }
        return keyPair;
    }

    /**
     * An instance of ProviderSettings to configure Spring Authorization Server.
     * 
     * @return
     */
    @Bean
    public ProviderSettings providerSettings() {
        return ProviderSettings.builder().issuer("http://auth-server:8080").build();
    }

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

}

3. spring-security-oauth2-resource_server

ResourceServerConfig.java

@Bean
@Order(2)
public SecurityFilterChain resourceServerFilterChain(HttpSecurity http) throws Exception {
    http  
        .authorizeHttpRequests(authorize -> authorize
            .mvcMatchers(HttpMethod.POST, "/postArticles").hasAuthority("SCOPE_articles.write")
            .anyRequest().authenticated()
        )
        .csrf().disable() 
        .oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt)
        ;
    return http.build();
}

ResourceController.java

@ResponseStatus(HttpStatus.CREATED)
@PostMapping("/postArticles")
public String postArticles(@RequestBody String newArticle) {
    return newArticle;
}

application-oauth2.properties

spring.security.oauth2.resourceserver.jwt.issuer-uri: http://auth-server:8080

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 16:20:58