启用 Spring 安全性后无法在本地启动应用程序
Fail to start application locally after enabling Spring Security
在基于 Spring 的 SDK 原型项目中启用 spring 安全性来保护我的后端服务
在本地启动应用程序后,出现一条错误消息 - "Environment variable VCAP_SERVICES not set"。下面是此错误的堆栈跟踪。
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfiguration': Unsatisfied dependency expressed through field 'tokenServices'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'resourceServerTokenServices' defined in class path resource [com/bosch/SecurityConfig.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.security.oauth2.provider.token.ResourceServerTokenServices]: Factory method 'resourceServerTokenServices' threw exception; nested exception is java.lang.IllegalStateException: Environment variable VCAP_SERVICES not set
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:596)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:90)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:374)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1378)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:575)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:498)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean[=11=](AbstractBeanFactory.java:320)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:846)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:863)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:546)
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:142)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:775)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:316)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1260)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1248)
at com.bosch.Application.main(Application.java:23)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'resourceServerTokenServices' defined in class path resource [com/bosch/SecurityConfig.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.security.oauth2.provider.token.ResourceServerTokenServices]: Factory method 'resourceServerTokenServices' threw exception; nested exception is java.lang.IllegalStateException: Environment variable VCAP_SERVICES not set
at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:625)
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:455)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1288)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1127)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:538)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:498)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean[=11=](AbstractBeanFactory.java:320)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:273)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.addCandidateEntry(DefaultListableBeanFactory.java:1455)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1419)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveMultipleBeans(DefaultListableBeanFactory.java:1338)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1197)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1166)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:593)
... 19 common frames omitted
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.security.oauth2.provider.token.ResourceServerTokenServices]: Factory method 'resourceServerTokenServices' threw exception; nested exception is java.lang.IllegalStateException: Environment variable VCAP_SERVICES not set
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:185)
at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:620)
... 35 common frames omitted
Caused by: java.lang.IllegalStateException: Environment variable VCAP_SERVICES not set
at com.sap.xs2.security.commons.SAPVcapServicesParser.parseVcapServices(SAPVcapServicesParser.java:42)
at com.sap.xs2.security.commons.SAPVcapServicesParser.<init>(SAPVcapServicesParser.java:30)
at com.sap.xs2.security.commons.SAPPropertyPlaceholderConfigurer.<init>(SAPPropertyPlaceholderConfigurer.java:35)
at com.sap.xs2.security.commons.SAPOfflineTokenServicesCloud.<init>(SAPOfflineTokenServicesCloud.java:27)
at com.bosch.SecurityConfig.resourceServerTokenServices(SecurityConfig.java:47)
at com.bosch.SecurityConfig$$EnhancerBySpringCGLIB$e72219b.CGLIB$resourceServerTokenServices(<generated>)
at com.bosch.SecurityConfig$$EnhancerBySpringCGLIB$e72219b$$FastClassBySpringCGLIB$8b0fba.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:244)
at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:363)
at com.bosch.SecurityConfig$$EnhancerBySpringCGLIB$e72219b.resourceServerTokenServices(<generated>)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154)
... 36 common frames omitted
重现问题的步骤:
基于Spring
新建一个sdk原型项目
取消注释掉SecurityConfig.java
中的所有代码
导入所有安全库
在本地启动应用程序
该应用程序运行在 SCP Cloud Foundry 上运行良好。
我知道根本原因是应用程序在 SecurityConfig.java 中初始化 bean 时将读取环境变量 VCAP_SERVICES。但它无法在本地环境中找到它。
如果我想 运行 在本地申请,有什么解决方法吗?
更新:
我刚刚发现我们之前有一个类似的问题和一个有用的答案:
由于安全功能严重依赖绑定的 XSUAA 服务,而且您需要通过 AppRouter 访问您的应用程序,因此没有 easy/recommended 本地启动安全应用程序的方法。
我们广泛使用的一个选项是在本地使用脚本来设置 VCAP_SERVICES 环境变量,类似于 Cloud Foundry (cf env)。我假设,这将满足 SAPOfflineTokenServicesCloud bean 的创建。
有了它,您可以在本地启动应用程序,但在应用程序受保护时不测试您的 Web 应用程序。因为在这种情况下,您需要使用授权 header 调用 Web 应用程序的端点,授权需要包含 "valid" 访问令牌。如果令牌未过期并且使用与 public RSA 密钥匹配的私有 RSA 密钥签名,则令牌被认为是有效的,该密钥由 uaa 的 jwks 令牌端点提供,如此处记录:https://docs.cloudfoundry.org/api/uaa/version/74.4.0/index.html#token-keys
您有以下选择:
- 对于集成测试,您可以使用此端点从 xsuaa 服务为您的(测试)用户获取新的访问令牌:https://docs.cloudfoundry.org/api/uaa/version/74.4.0/index.html#password-grant. You can use also the java token-client lib.
- 对于本地测试 (w/o) Xsuaa交互:
- 您需要使用例如 JwtGenerator 和
生成 Jwt 令牌
- 作为您的 VCAP_SERVICES 环境的一部分更改您的 verificationkey,它与私钥匹配,令牌是用它签名的(例如 JwtGenerator's public key) ,例如"verificationkey":"-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BA...".
- 用于单元测试
- 您需要使用例如 JwtGenerator 和
生成 Jwt 令牌
- 您需要照原样覆盖 SAPOfflineTokenServicesCloud here
这应该可行(我从未测试过该设置与 SAP Cloud SDK 的结合)。
在本地启动应用程序后,出现一条错误消息 - "Environment variable VCAP_SERVICES not set"。下面是此错误的堆栈跟踪。
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfiguration': Unsatisfied dependency expressed through field 'tokenServices'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'resourceServerTokenServices' defined in class path resource [com/bosch/SecurityConfig.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.security.oauth2.provider.token.ResourceServerTokenServices]: Factory method 'resourceServerTokenServices' threw exception; nested exception is java.lang.IllegalStateException: Environment variable VCAP_SERVICES not set
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:596)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:90)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:374)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1378)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:575)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:498)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean[=11=](AbstractBeanFactory.java:320)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:846)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:863)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:546)
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:142)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:775)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:316)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1260)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1248)
at com.bosch.Application.main(Application.java:23)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'resourceServerTokenServices' defined in class path resource [com/bosch/SecurityConfig.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.security.oauth2.provider.token.ResourceServerTokenServices]: Factory method 'resourceServerTokenServices' threw exception; nested exception is java.lang.IllegalStateException: Environment variable VCAP_SERVICES not set
at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:625)
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:455)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1288)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1127)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:538)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:498)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean[=11=](AbstractBeanFactory.java:320)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:273)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.addCandidateEntry(DefaultListableBeanFactory.java:1455)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1419)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveMultipleBeans(DefaultListableBeanFactory.java:1338)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1197)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1166)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:593)
... 19 common frames omitted
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.security.oauth2.provider.token.ResourceServerTokenServices]: Factory method 'resourceServerTokenServices' threw exception; nested exception is java.lang.IllegalStateException: Environment variable VCAP_SERVICES not set
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:185)
at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:620)
... 35 common frames omitted
Caused by: java.lang.IllegalStateException: Environment variable VCAP_SERVICES not set
at com.sap.xs2.security.commons.SAPVcapServicesParser.parseVcapServices(SAPVcapServicesParser.java:42)
at com.sap.xs2.security.commons.SAPVcapServicesParser.<init>(SAPVcapServicesParser.java:30)
at com.sap.xs2.security.commons.SAPPropertyPlaceholderConfigurer.<init>(SAPPropertyPlaceholderConfigurer.java:35)
at com.sap.xs2.security.commons.SAPOfflineTokenServicesCloud.<init>(SAPOfflineTokenServicesCloud.java:27)
at com.bosch.SecurityConfig.resourceServerTokenServices(SecurityConfig.java:47)
at com.bosch.SecurityConfig$$EnhancerBySpringCGLIB$e72219b.CGLIB$resourceServerTokenServices(<generated>)
at com.bosch.SecurityConfig$$EnhancerBySpringCGLIB$e72219b$$FastClassBySpringCGLIB$8b0fba.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:244)
at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:363)
at com.bosch.SecurityConfig$$EnhancerBySpringCGLIB$e72219b.resourceServerTokenServices(<generated>)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154)
... 36 common frames omitted
重现问题的步骤:
基于Spring
新建一个sdk原型项目
取消注释掉SecurityConfig.java
中的所有代码
导入所有安全库
在本地启动应用程序
该应用程序运行在 SCP Cloud Foundry 上运行良好。
我知道根本原因是应用程序在 SecurityConfig.java 中初始化 bean 时将读取环境变量 VCAP_SERVICES。但它无法在本地环境中找到它。
如果我想 运行 在本地申请,有什么解决方法吗?
更新:
我刚刚发现我们之前有一个类似的问题和一个有用的答案:
由于安全功能严重依赖绑定的 XSUAA 服务,而且您需要通过 AppRouter 访问您的应用程序,因此没有 easy/recommended 本地启动安全应用程序的方法。
我们广泛使用的一个选项是在本地使用脚本来设置 VCAP_SERVICES 环境变量,类似于 Cloud Foundry (cf env)。我假设,这将满足 SAPOfflineTokenServicesCloud bean 的创建。
有了它,您可以在本地启动应用程序,但在应用程序受保护时不测试您的 Web 应用程序。因为在这种情况下,您需要使用授权 header 调用 Web 应用程序的端点,授权需要包含 "valid" 访问令牌。如果令牌未过期并且使用与 public RSA 密钥匹配的私有 RSA 密钥签名,则令牌被认为是有效的,该密钥由 uaa 的 jwks 令牌端点提供,如此处记录:https://docs.cloudfoundry.org/api/uaa/version/74.4.0/index.html#token-keys
您有以下选择:
- 对于集成测试,您可以使用此端点从 xsuaa 服务为您的(测试)用户获取新的访问令牌:https://docs.cloudfoundry.org/api/uaa/version/74.4.0/index.html#password-grant. You can use also the java token-client lib.
- 对于本地测试 (w/o) Xsuaa交互:
- 您需要使用例如 JwtGenerator 和 生成 Jwt 令牌
- 作为您的 VCAP_SERVICES 环境的一部分更改您的 verificationkey,它与私钥匹配,令牌是用它签名的(例如 JwtGenerator's public key) ,例如"verificationkey":"-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BA...".
- 用于单元测试
- 您需要使用例如 JwtGenerator 和 生成 Jwt 令牌
- 您需要照原样覆盖 SAPOfflineTokenServicesCloud here
这应该可行(我从未测试过该设置与 SAP Cloud SDK 的结合)。