我正在创建一个Spring boot 2 OAuth2客户端/服务器应用程序。授权服务器成功登录并重定向到客户端,但当客户端收到重定向(http://localhost:8080/login/oauth2/code/xe?code=ACK4Ae&state=Jw-dCGYvJa6QV-fcoTGjgY-6FyUyJHa-HBjWdsp3HM4%3D)时,客户端浏览器显示:
Your login attempt was not successful, try again.
Reason: [invalid_token_response] An error occurred parsing the Access Token response: The HTTP Content-Type header must be application/json; charset=UTF-8
stacktrace
Authentication request failed: org.springframework.security.oauth2.core.OAuth2AuthenticationException: [invalid_token_response] An error occurred parsing the Access Token response: The HTTP Content-Type header must be application/json; charset=UTF-8
org.springframework.security.oauth2.core.OAuth2AuthenticationException: [invalid_token_response] An error occurred parsing the Access Token response: The HTTP Content-Type header must be application/json; charset=UTF-8
at org.springframework.security.oauth2.client.endpoint.NimbusAuthorizationCodeTokenResponseClient.getTokenResponse(NimbusAuthorizationCodeTokenResponseClient.java:105) ~[spring-security-oauth2-client-5.0.0.RELEASE.jar:5.0.0.RELEASE]
at org.springframework.security.oauth2.client.endpoint.NimbusAuthorizationCodeTokenResponseClient.getTokenResponse(NimbusAuthorizationCodeTokenResponseClient.java:67) ~[spring-security-oauth2-client-5.0.0.RELEASE.jar:5.0.0.RELEASE]
at org.springframework.security.oauth2.client.authentication.OAuth2LoginAuthenticationProvider.authenticate(OAuth2LoginAuthenticationProvider.java:113) ~[spring-security-oauth2-client-5.0.0.RELEASE.jar:5.0.0.RELEASE]
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:174) ~[spring-security-core-5.0.0.RELEASE.jar:5.0.0.RELEASE]
at org.springframework.security.oauth2.client.web.OAuth2LoginAuthenticationFilter.attemptAuthentication(OAuth2LoginAuthenticationFilter.java:129) ~[spring-security-oauth2-client-5.0.0.RELEASE.jar:5.0.0.RELEASE]
at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:212) [spring-security-web-5.0.0.RELEASE.jar:5.0.0.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-5.0.0.RELEASE.jar:5.0.0.RELEASE]
at org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestRedirectFilter.doFilterInternal(OAuth2AuthorizationRequestRedirectFilter.java:109) [spring-security-oauth2-client-5.0.0.RELEASE.jar:5.0.0.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-5.0.2.RELEASE.jar:5.0.2.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-5.0.0.RELEASE.jar:5.0.0.RELEASE]
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116) [spring-security-web-5.0.0.RELEASE.jar:5.0.0.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-5.0.0.RELEASE.jar:5.0.0.RELEASE]
at org.springframework.security.web.csrf.CsrfFilter.doFilterInternal(CsrfFilter.java:100) [spring-security-web-5.0.0.RELEASE.jar:5.0.0.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-5.0.2.RELEASE.jar:5.0.2.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-5.0.0.RELEASE.jar:5.0.0.RELEASE]
at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:64) [spring-security-web-5.0.0.RELEASE.jar:5.0.0.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-5.0.2.RELEASE.jar:5.0.2.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-5.0.0.RELEASE.jar:5.0.0.RELEASE]
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105) [spring-security-web-5.0.0.RELEASE.jar:5.0.0.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-5.0.0.RELEASE.jar:5.0.0.RELEASE]
at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56) [spring-security-web-5.0.0.RELEASE.jar:5.0.0.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-5.0.2.RELEASE.jar:5.0.2.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-5.0.0.RELEASE.jar:5.0.0.RELEASE]
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:215) [spring-security-web-5.0.0.RELEASE.jar:5.0.0.RELEASE]
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:178) [spring-security-web-5.0.0.RELEASE.jar:5.0.0.RELEASE]
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:357) [spring-web-5.0.2.RELEASE.jar:5.0.2.RELEASE]
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:270) [spring-web-5.0.2.RELEASE.jar:5.0.2.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-8.5.23.jar:8.5.23]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-8.5.23.jar:8.5.23]
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99) [spring-web-5.0.2.RELEASE.jar:5.0.2.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-5.0.2.RELEASE.jar:5.0.2.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-8.5.23.jar:8.5.23]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-8.5.23.jar:8.5.23]
at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:108) [spring-web-5.0.2.RELEASE.jar:5.0.2.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-5.0.2.RELEASE.jar:5.0.2.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-8.5.23.jar:8.5.23]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-8.5.23.jar:8.5.23]
at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:81) [spring-web-5.0.2.RELEASE.jar:5.0.2.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-5.0.2.RELEASE.jar:5.0.2.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-8.5.23.jar:8.5.23]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-8.5.23.jar:8.5.23]
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200) [spring-web-5.0.2.RELEASE.jar:5.0.2.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-5.0.2.RELEASE.jar:5.0.2.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-8.5.23.jar:8.5.23]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-8.5.23.jar:8.5.23]
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:199) [tomcat-embed-core-8.5.23.jar:8.5.23]
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) [tomcat-embed-core-8.5.23.jar:8.5.23]
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:478) [tomcat-embed-core-8.5.23.jar:8.5.23]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140) [tomcat-embed-core-8.5.23.jar:8.5.23]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81) [tomcat-embed-core-8.5.23.jar:8.5.23]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87) [tomcat-embed-core-8.5.23.jar:8.5.23]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342) [tomcat-embed-core-8.5.23.jar:8.5.23]
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:803) [tomcat-embed-core-8.5.23.jar:8.5.23]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) [tomcat-embed-core-8.5.23.jar:8.5.23]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868) [tomcat-embed-core-8.5.23.jar:8.5.23]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1459) [tomcat-embed-core-8.5.23.jar:8.5.23]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-8.5.23.jar:8.5.23]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [?:1.8.0_144]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [?:1.8.0_144]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-8.5.23.jar:8.5.23]
at java.lang.Thread.run(Thread.java:748) [?:1.8.0_144]
Caused by: com.nimbusds.oauth2.sdk.ParseException: The HTTP Content-Type header must be application/json; charset=UTF-8
at com.nimbusds.oauth2.sdk.util.ContentTypeUtils.ensureContentType(ContentTypeUtils.java:52) ~[oauth2-oidc-sdk-5.38.jar:5.38]
at com.nimbusds.oauth2.sdk.http.HTTPMessage.ensureContentType(HTTPMessage.java:133) ~[oauth2-oidc-sdk-5.38.jar:5.38]
at com.nimbusds.oauth2.sdk.http.HTTPResponse.ensureContentType(HTTPResponse.java:1) ~[oauth2-oidc-sdk-5.38.jar:5.38]
at com.nimbusds.oauth2.sdk.http.HTTPResponse.getContentAsJSONObject(HTTPResponse.java:369) ~[oauth2-oidc-sdk-5.38.jar:5.38]
at com.nimbusds.oauth2.sdk.AccessTokenResponse.parse(AccessTokenResponse.java:235) ~[oauth2-oidc-sdk-5.38.jar:5.38]
at com.nimbusds.oauth2.sdk.TokenResponse.parse(TokenResponse.java:74) ~[oauth2-oidc-sdk-5.38.jar:5.38]
at org.springframework.security.oauth2.client.endpoint.NimbusAuthorizationCodeTokenResponseClient.getTokenResponse(NimbusAuthorizationCodeTokenResponseClient.java:101) ~[spring-security-oauth2-client-5.0.0.RELEASE.jar:5.0.0.RELEASE]
... 60 more
服务器代码
@Configuration
@EnableAuthorizationServer
public class OAuth2AuthServerConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
private AuthenticationManager authenticationManager;
@Override
public void configure(final ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory().withClient("xe").secret("password")
.authorizedGrantTypes("authorization_code", "refresh_token", "password").scopes("user")
.autoApprove(true);
}
@Override
public void configure(final AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()");
}
@Override
public void configure(final AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(tokenStore()).authenticationManager(authenticationManager);
}
@Bean
public TokenStore tokenStore() {
return new InMemoryTokenStore();
}
}
@Configuration
@EnableWebSecurity
@Order(-20)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(final HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/login").permitAll().anyRequest().authenticated()
.and().formLogin().permitAll();
}
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser(User.withDefaultPasswordEncoder().username("tom").password("111").roles("USER"));
}
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
spring boot 2客户端配置
spring.security.oauth2.client.registration.xe.client-id=xe
spring.security.oauth2.client.registration.xe.client-secret=password
spring.security.oauth2.client.registration.xe.client-name=xe
spring.security.oauth2.client.registration.xe.provider=x-auth
spring.security.oauth2.client.registration.xe.scope=user
spring.security.oauth2.client.registration.xe.redirect-uri-template={baseUrl}/login/oauth2/code/{registrationId}
spring.security.oauth2.client.registration.xe.client-authentication-method=post
spring.security.oauth2.client.registration.xe.authorization-grant-type=authorization_code
spring.security.oauth2.client.provider.x-auth.authorization-uri=http://localhost:9090/auth/oauth/authorize
spring.security.oauth2.client.provider.x-auth.token-uri=http://localhost:9090/auth/oauth/token
spring.security.oauth2.client.provider.x-auth.user-info-uri=http://localhost:9090/auth/user
spring.security.oauth2.client.provider.x-auth.jwk-set-uri=http://localhost:9090/token_keys
spring.security.oauth2.client.provider.x-auth.user-name-attribute=username
在服务器pom中
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.0.M7</version>
<relativePath />
</parent>
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>2.2.1.RELEASE</version>
</dependency>
在客户端pom
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.0.M7</version>
</parent>
...
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-client</artifactId>
<version>5.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-jose</artifactId>
<version>5.0.0.RELEASE</version>
</dependency>
答案 0 :(得分:0)
NimbusAuthorizationCodeTokenResponseClient
在内部使用Nimbus OAuth 2.0 SDK类发送令牌请求(TokenRequest
)并解析令牌响应(TokenResponse
)。
令牌响应通过TokenResponse.parse()
进行解析,这是错误发生的地方,因为它会检查以确保响应中的Content-Type
标头可用并且设置为application/json
。这是spring-security-oauth的一个错误,我已经记录了问题here。
在修复此问题之前,spring-security-oauth的可能解决方法是使用@ControllerAdvice
拦截来自TokenEndpoint
的响应,并将Content-Type=application/json
标头添加到响应中,如下:
@ControllerAdvice(assignableTypes = TokenEndpoint.class)
public class TokenEndpointResponseAdvice<T> implements ResponseBodyAdvice<T> {
@Override
public boolean supports(MethodParameter returnType,
Class<? extends HttpMessageConverter<?>> converterType) {
return true;
}
@Nullable
@Override
public T beforeBodyWrite(@Nullable T body,
MethodParameter returnType,
MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType,
ServerHttpRequest request,
ServerHttpResponse response) {
response.getHeaders().add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
return body;
}
}
以下是有关此解决方案的更多资源:
注意:我自己并没有真正试过这个,但它应该可行。如果没有,那么将代码发布到GitHub并在此发布,我会检查它并让它为你工作。使用发生问题的相同代码库进行故障排除会更有效。