Spring security 当配置为使用JWT时,Spring OAuth2RestTemplate将丢失令牌

Spring security 当配置为使用JWT时,Spring OAuth2RestTemplate将丢失令牌,spring-security,oauth-2.0,spring-boot,jwt,spring-cloud,Spring Security,Oauth 2.0,Spring Boot,Jwt,Spring Cloud,我有一系列配置为使用Spring Boot和Cloud OAuth2 SSO的微服务项目。它具有以下组件: 弹簧靴1.3.0 春季云彩Brixton.M3 auth server有Spring Boot 1.2.7和Cloud Angel.SR4 Zuul代理(网关) 身份验证服务器 资源服务器 用户界面服务器 尤里卡服务器 基本流程是: 使用解析UI服务器的URL点击网关 通过身份验证服务器进行身份验证(通过SSO) 重定向到UI服务器 UI服务器从资源服务器请求资源(就像Hello W

我有一系列配置为使用Spring Boot和Cloud OAuth2 SSO的微服务项目。它具有以下组件:

弹簧靴1.3.0 春季云彩Brixton.M3

auth server有Spring Boot 1.2.7和Cloud Angel.SR4

  • Zuul代理(网关)
  • 身份验证服务器
  • 资源服务器
  • 用户界面服务器
  • 尤里卡服务器
基本流程是:

  • 使用解析UI服务器的URL点击网关
  • 通过身份验证服务器进行身份验证(通过SSO)
  • 重定向到UI服务器
  • UI服务器从资源服务器请求资源(就像Hello World一样的字符串)
  • UI服务器在页面上显示字符串
当在auth服务器上使用“userInfoUri”和“/user”端点时,实际上一切都很好,如各种Spring指南中所示

但是,我们希望尝试使用JWT令牌,以避免服务和身份验证服务器之间通过/user端点的持续聊天

因此,我将auth服务器配置为使用JWT:

        @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        KeyPair keyPair = new KeyStoreKeyFactory(
                new ClassPathResource("keystore.jks"), "foobar".toCharArray())
                .getKeyPair("test");
        converter.setKeyPair(keyPair);
        return converter;
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.authenticationManager(authenticationManager).accessTokenConverter(jwtAccessTokenConverter());
    }
然后使用公钥更改“客户端”服务的配置:

security:
  oauth2:
    resource:
      jwt:
        keyValue: |
           -----BEGIN PUBLIC KEY-----
            MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnGp/Q5lh0P8nPL21oMMrt2RrkT9AW5jgYwLfSUnJVc9G6uR3cXRRDCjHqWU5WYwivcF180A6CWp/ireQFFBNowgc5XaA0kPpzEtgsA5YsNX7iSnUibB004iBTfU9hZ2Rbsc8cWqynT0RyN4TP1RYVSeVKvMQk4GT1r7JCEC+TNu1ELmbNwMQyzKjsfBXyIOCFU/E94ktvsTZUHF4Oq44DBylCDsS1k7/sfZC2G5EU7Oz0mhG8+Uz6MSEQHtoIi6mc8u64Rwi3Z3tscuWG2ShtsUFuNSAFNkY7LkLn+/hxLCu2bNISMaESa8dG22CIMuIeRLVcAmEWEWH5EEforTg+QIDAQAB
           -----END PUBLIC KEY-----
在UI服务器上运行之前,一切似乎都正常。也就是说,网关获取JWT令牌并可以对其进行解密,然后调用UI服务器。但是UI服务器中有一些代码调用另一个资源:

@EnableResourceServer
@EnableDiscoveryClient
@RestController
@SpringBootApplication
public class UiServerApplication {

    // Must use OAuth2RestTemplate for token forwarding.
    // To use load balancing could probably use Ribbon API to get service URL
    @Autowired
    private OAuth2RestTemplate restTemplate;

    private static final String RESOURCE_URL = "http://localhost:9004/resource";

    @RequestMapping("/")
    public String home() {
        return this.restTemplate.getForObject(RESOURCE_URL, String.class);
    }

    public static void main(String[] args) {
        SpringApplication.run(UiServerApplication.class, args);
    }

    // Bug workaround.  Apparently fixed in Spring Boot 1.3.1
    @Component
    @Order(Ordered.HIGHEST_PRECEDENCE)
    static class WorkaroundRestTemplateCustomizer implements UserInfoRestTemplateCustomizer {

        public void customize(OAuth2RestTemplate template) {
            template.setInterceptors(new ArrayList<>(template.getInterceptors()));
        }

    }
}
更新:下面是我有时看到的另一个错误:

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:359) ~[spring-security-oauth2-2.0.8.RELEASE.jar:na]
at org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeAccessTokenProvider.obtainAccessToken(AuthorizationCodeAccessTokenProvider.java:205) ~[spring-security-oauth2-2.0.8.RELEASE.jar:na]
at org.springframework.security.oauth2.client.OAuth2RestTemplate.acquireAccessToken(OAuth2RestTemplate.java:221) ~[spring-security-oauth2-2.0.8.RELEASE.jar:na]
at org.springframework.security.oauth2.client.OAuth2RestTemplate.getAccessToken(OAuth2RestTemplate.java:173) ~[spring-security-oauth2-2.0.8.RELEASE.jar:na]
at org.springframework.security.oauth2.client.OAuth2RestTemplate.createRequest(OAuth2RestTemplate.java:105) ~[spring-security-oauth2-2.0.8.RELEASE.jar:na]
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:592) ~[spring-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
at org.springframework.security.oauth2.client.OAuth2RestTemplate.doExecute(OAuth2RestTemplate.java:128) ~[spring-security-oauth2-2.0.8.RELEASE.jar:na]
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:557) ~[spring-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
at org.springframework.web.client.RestTemplate.getForObject(RestTemplate.java:264) ~[spring-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
at uk.co.aquilauk.UiServerApplication.home(UiServerApplication.java:33) ~[classes/:na]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_65]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_65]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_65]
at java.lang.reflect.Method.invoke(Method.java:497) ~[na:1.8.0_65]

令牌转发功能在Spring Cloud Security中(是谁为您创建了
OAuth2RestTemplate
)。它不是设计用来与JWT令牌一起工作的,但它可能可以被修改。为什么不提出一个问题?

发生这种情况的原因是,当OAuth2AuthenticationManager工作时(即没有JWT),它有一个UserInfoTokenServices实例,该实例在身份验证后在OAuth2RestTemplate中设置访问令牌。当为JWT配置时,它有一个DefaultTokenServices实例,但它不这样做。所以我不确定问题出在哪里-配置错误还是什么。。。
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:359) ~[spring-security-oauth2-2.0.8.RELEASE.jar:na]
at org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeAccessTokenProvider.obtainAccessToken(AuthorizationCodeAccessTokenProvider.java:205) ~[spring-security-oauth2-2.0.8.RELEASE.jar:na]
at org.springframework.security.oauth2.client.OAuth2RestTemplate.acquireAccessToken(OAuth2RestTemplate.java:221) ~[spring-security-oauth2-2.0.8.RELEASE.jar:na]
at org.springframework.security.oauth2.client.OAuth2RestTemplate.getAccessToken(OAuth2RestTemplate.java:173) ~[spring-security-oauth2-2.0.8.RELEASE.jar:na]
at org.springframework.security.oauth2.client.OAuth2RestTemplate.createRequest(OAuth2RestTemplate.java:105) ~[spring-security-oauth2-2.0.8.RELEASE.jar:na]
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:592) ~[spring-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
at org.springframework.security.oauth2.client.OAuth2RestTemplate.doExecute(OAuth2RestTemplate.java:128) ~[spring-security-oauth2-2.0.8.RELEASE.jar:na]
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:557) ~[spring-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
at org.springframework.web.client.RestTemplate.getForObject(RestTemplate.java:264) ~[spring-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
at uk.co.aquilauk.UiServerApplication.home(UiServerApplication.java:33) ~[classes/:na]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_65]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_65]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_65]
at java.lang.reflect.Method.invoke(Method.java:497) ~[na:1.8.0_65]