Spring security 使用过期的承载令牌访问资源失败,http代码为500
这是我在SpringBoot 1.2.3.0版本中的应用程序设置 认证应用程序Spring security 使用过期的承载令牌访问资源失败,http代码为500,spring-security,spring-security-oauth2,spring-cloud,Spring Security,Spring Security Oauth2,Spring Cloud,这是我在SpringBoot 1.2.3.0版本中的应用程序设置 认证应用程序 @SpringBootApplication @RestController @SessionAttributes("authorizationRequest") @EnableResourceServer public class AuthserverApplication extends WebMvcConfigurerAdapter { public static void main(String[]
@SpringBootApplication
@RestController
@SessionAttributes("authorizationRequest")
@EnableResourceServer
public class AuthserverApplication extends WebMvcConfigurerAdapter {
public static void main(String[] args) {
SpringApplication.run(AuthserverApplication.class, args);
}
@RequestMapping("/user")
public Principal user(Principal user) {
return user;
}
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/login").setViewName("login");
registry.addViewController("/oauth/confirm_access").setViewName("authorize");
}
@Configuration
@Order(-10)
protected static class LoginConfig extends WebSecurityConfigurerAdapter {
@Autowired
private AuthenticationManager authenticationManager;
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http.formLogin().loginPage("/login").permitAll()
.and().requestMatchers()
.antMatchers("/login", "/oauth/authorize", "/oauth/confirm_access")
.and().authorizeRequests()
.anyRequest().authenticated();
// @formatter:on
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.parentAuthenticationManager(authenticationManager);
}
}
@Configuration
@EnableAuthorizationServer
protected static class OAuth2Config extends AuthorizationServerConfigurerAdapter {
@Autowired
private AuthenticationManager authenticationManager;
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager);
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory().withClient("acme").secret("acmesecret")
.authorizedGrantTypes("authorization_code", "refresh_token", "password").scopes("openid")
.and().withClient("trusted").secret("shuush").authorizedGrantTypes("client_credentials").scopes("openid");
}
}
}
使用application.properties
server.port=9999
security.user.password=password
server.contextPath=/uaa
server.port: 8801
spring.oauth2.resource.userInfoUri: http://localhost:9999/uaa/user
资源应用程序
@SpringBootApplication
@EnableOAuth2Resource
@RestController
public class ResourceApplication {
@RequestMapping("/resource")
public Map<String, String> resource() {
Map<String, String> t = new HashMap<String, String>();
t.put("Hello", "World!");
return t;
}
public static void main(String[] args) {
SpringApplication.run(ResourceApplication.class, args);
}
}
客户端:
步骤1:我使用curl获取承载令牌
curl -v -u trusted:shuush -d "grant_type=client_credentials" http://localhost:9999/uaa/oauth/token
得到答复
{"access_token":"a9983ae3-e4db-4702-8039-7ecfd7472594","token_type":"bearer","expires_in":43095,"scope":"openid"}
步骤2:我访问资源
curl -v -H "Authorization: Bearer a9983ae3-e4db-4702-8039-7ecfd7472594" http://localhost:8801/resource
这给了我一个漂亮的回答
{"Hello":"World!"}
步骤3:在我的令牌过期很久之后,我再次尝试访问资源(与步骤2相同),我希望收到401 http代码,其响应类似
{“error”:“unauthorized”,“error_description”:“token expired…”
然而,我得到了500个代码和响应
{
"timestamp": 1433861559805,
"status": 500,
"error": "Internal Server Error",
"exception": "org.springframework.security.oauth2.client.resource.UserRedirectRequiredException",
"message": "A redirect is required to get the users approval",
"path": "/resource"
}
堆栈跟踪:
org.springframework.security.oauth2.client.resource.UserRedirectRequiredException: A redirect is required to get the users approval
at org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeAccessTokenProvider.getRedirectForAuthorization(AuthorizationCodeAccessTokenProvider.java:347)
at org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeAccessTokenProvider.obtainAccessToken(AuthorizationCodeAccessTokenProvider.java:194)
at org.springframework.security.oauth2.client.OAuth2RestTemplate.acquireAccessToken(OAuth2RestTemplate.java:221)
at org.springframework.security.oauth2.client.OAuth2RestTemplate.getAccessToken(OAuth2RestTemplate.java:173)
at org.springframework.security.oauth2.client.OAuth2RestTemplate.createRequest(OAuth2RestTemplate.java:105)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:565)
at org.springframework.security.oauth2.client.OAuth2RestTemplate.doExecute(OAuth2RestTemplate.java:143)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:530)
at org.springframework.web.client.RestTemplate.getForEntity(RestTemplate.java:262)
at org.springframework.cloud.security.oauth2.resource.UserInfoTokenServices.getMap(UserInfoTokenServices.java:105)
at org.springframework.cloud.security.oauth2.resource.UserInfoTokenServices.loadAuthentication(UserInfoTokenServices.java:58)
at org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationManager.authenticate(OAuth2AuthenticationManager.java:83)
at org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationProcessingFilter.doFilter(OAuth2AuthenticationProcessingFilter.java:150)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:110)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:57)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:50)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192)
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:85)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.springframework.boot.actuate.autoconfigure.MetricFilterAutoConfiguration$MetricsFilter.doFilterInternal(MetricFilterAutoConfiguration.java:90)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:219)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:501)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:142)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:516)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1086)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:659)
at org.apache.coyote.http11.Http11NioProtocol$Http11ConnectionHandler.process(Http11NioProtocol.java:223)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1558)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1515)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:745)
不知道我是否缺少一些配置。有什么建议吗
更新1:
奇怪的是,有时我会得到不同的反应
{"error":"invalid_request","error_description":"Possible CSRF detected - state parameter was present but no state could be found"}
少量调试显示端点http://localhost:9999/uaa/user
确实会抛出类似于{error=“invalid_token”,error\u description=“invalid access token…”的错误
,但会被吞没为抛出org.springframework.security.oauth2.client.http.AccessTokenRequiredException
进一步的org.springframework.security.oauth2.client.OAuth2RestTemplate
吞咽org.springframework.security.oauth2.client.http.AccessTokenRequiredException
并通过在抛出完全不同的错误时删除令牌来重试坏令牌
我相信这是一个bug,除非我的配置有问题错误来自SpringCloud中的
UserInfoTokenServices
。我建议你在那里提出一个问题。如果你想预测一下未来,Spring Boot 1.3也有这些功能(即Spring Cloud将在某个时候删除它)。谢谢@DaveSyer。我希望修复程序和Spring Boot 1.3也能解决不一致的错误消息