我一直在尝试在 Spring Boot Security 中为 Twitter Oauth2 配置找到合适的配置,但没有任何效果。
单击“登录 Twitter”按钮后,启动流程,显示授权对话框,单击“授权”后,抛出以下错误: “[invalid_token_response] 尝试检索 OAuth 2.0 访问令牌时发生错误响应:401 未经授权:[无正文]”.
application.properties
文件中的配置如下所示:
spring.security.oauth2.client.registration.twitter.client-id=[my-client-id]
spring.security.oauth2.client.registration.twitter.client-secret=[my-secret]
spring.security.oauth2.client.registration.twitter.scope=users.read
spring.security.oauth2.client.registration.twitter.redirect-uri={baseUrl}/oauth2/callback/{registrationId}
spring.security.oauth2.client.registration.twitter.client-authentication-method=client_secret_post
spring.security.oauth2.client.provider.twitter.authorization-uri=https://twitter.com/i/oauth2/authorize?code_challenge=challenge&code_challenge_method=plain
spring.security.oauth2.client.provider.twitter.token-uri=https://api.twitter.com/2/oauth2/token
spring.security.oauth2.client.registration.twitter.authorization-grant-type=authorization_code
这是我在应用程序中的
SecurityConfig
:
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {
private final CustomOAuth2UserService customOAuth2UserService;
private final OAuth2AuthenticationSuccessHandler oAuthAuthenticationSuccessHandler;
private final OAuth2AuthenticationFailureHandler oAuth2AuthenticationFailureHandler;
private final HttpCookieOAuth2AuthorizationRequestRepository httpCookieOAuth2AuthorizationRequestRepository;
private final TokenAuthenticationFilter tokenAuthenticationFilter;
private final ClientRegistrationRepository clientRegistrationRepository;
@Bean
public HttpCookieOAuth2AuthorizationRequestRepository cookieAuthorizationRequestRepository() {
return new HttpCookieOAuth2AuthorizationRequestRepository();
}
@Bean
protected SecurityFilterChain configure(HttpSecurity http) throws Exception {
http.csrf(AbstractHttpConfigurer::disable);
http.cors(Customizer.withDefaults());
http.sessionManagement(sessionManagement -> sessionManagement.sessionCreationPolicy(STATELESS));
http.formLogin(AbstractHttpConfigurer::disable);
http.httpBasic(AbstractHttpConfigurer::disable);
http.authorizeHttpRequests(
auth -> auth
.requestMatchers("/token/refresh/**").permitAll()
.requestMatchers(
"/",
"/error"
).permitAll()
.requestMatchers("/auth/**", "/oauth2/**").permitAll()
.anyRequest().authenticated()
);
http.oauth2Login(oauth2 -> oauth2
.authorizationEndpoint(authorizationEndpointConfigurer -> authorizationEndpointConfigurer
.baseUri("/oauth2/authorize")
.authorizationRequestRepository(httpCookieOAuth2AuthorizationRequestRepository)
.authorizationRequestResolver(new CustomAuthorizationRequestResolver(
clientRegistrationRepository, "/oauth2/authorize"
))
)
.redirectionEndpoint(redirectionEndpointConfigurer -> redirectionEndpointConfigurer
.baseUri("/oauth2/callback/*")
)
.userInfoEndpoint(userInfoEndpointConfigurer -> userInfoEndpointConfigurer
.userService(customOAuth2UserService)
)
.tokenEndpoint(token -> token.accessTokenResponseClient(authorizationCodeTokenResponseClient()))
.successHandler(oAuthAuthenticationSuccessHandler)
.failureHandler(oAuth2AuthenticationFailureHandler)
);
http.addFilterBefore(tokenAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
private OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> authorizationCodeTokenResponseClient() {
OAuth2AccessTokenResponseHttpMessageConverter tokenResponseHttpMessageConverter =
new OAuth2AccessTokenResponseHttpMessageConverter();
tokenResponseHttpMessageConverter.setAccessTokenResponseConverter(new CustomAccessTokenResponseConverter());
RestTemplate restTemplate = new RestTemplate(Arrays.asList(
new FormHttpMessageConverter(), tokenResponseHttpMessageConverter));
restTemplate.setErrorHandler(new OAuth2ErrorResponseErrorHandler());
DefaultAuthorizationCodeTokenResponseClient tokenResponseClient = new DefaultAuthorizationCodeTokenResponseClient();
tokenResponseClient.setRestOperations(restTemplate);
return tokenResponseClient;
}
@Bean
public AuthenticationManager authenticationManagerBean(AuthenticationConfiguration authenticationConfiguration) throws Exception {
return authenticationConfiguration.getAuthenticationManager();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
这是我在 Twitter 开发者门户上的配置:
这里有人处理过这种情况吗?
PS。我在 LinkedIn 配置中收到相同的错误:
spring.security.oauth2.client.registration.linkedin.client-id=[client-id]
spring.security.oauth2.client.registration.linkedin.client-secret=[client-secret]
spring.security.oauth2.client.registration.linkedin.scope=profile,email,openid
spring.security.oauth2.client.registration.linkedin.redirect-uri={baseUrl}/oauth2/callback/{registrationId}
spring.security.oauth2.client.registration.linkedin.authorization-grant-type=authorization_code
spring.security.oauth2.client.registration.linkedin.client-authentication-method=client_secret_post
spring.security.oauth2.client.provider.linkedin.authorization-uri=https://www.linkedin.com/oauth/v2/authorization
spring.security.oauth2.client.provider.linkedin.token-uri=https://www.linkedin.com/oauth/v2/accessToken
spring.security.oauth2.client.provider.linkedin.user-info-uri=https://api.linkedin.com/v2/me?projection=(id,firstName,lastName,profilePicture(displayImage~:playableStreams))
spring.security.oauth2.client.provider.linkedin.user-name-attribute=localizedFirstName
spring.security.oauth2.client.provider.linkedin.jwk-set-uri=https://www.linkedin.com/oauth/openid/jwks
我已经检查了凭据(客户端 ID、客户端密钥),它们以及重定向 uri 都是正确的。
您遇到的错误很可能发生在您的应用程序点击
token-uri
端点以交换访问令牌的代码时。我在使用 Twitter API 时也遇到了类似的问题。
经过一番研究,我找到了这个文档。尽管提到的 API URL 不同,但 v2 的要求似乎是相同的。端点要求请求具有带有基本身份验证的标头
base64(clientId:clientSercret)
。
对我来说,解决方案是在 Spring Security 配置中使用
client-authentication-method: client_secret_basic
。因此,Spring 将添加 Basic auth 标头,并且交换应该成功。