启用 Spring 安全性后无法在本地启动应用程序

Fail to start application locally after enabling Spring Security

我正在尝试根据教程 https://blogs.sap.com/2017/07/18/step-7-with-sap-s4hana-cloud-sdk-secure-your-application-on-sap-cloud-platform-cloudfoundry/.

在基于 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

重现问题的步骤:

  1. 基于Spring

  2. 新建一个sdk原型项目
  3. 取消注释掉SecurityConfig.java

  4. 中的所有代码
  5. 导入所有安全库

  6. 在本地启动应用程序

该应用程序运行在 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

您有以下选择:

这应该可行(我从未测试过该设置与 SAP Cloud SDK 的结合)。