You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot调用TenantAccessor.getCurrentTenant()报TenantNotAvailableException求助

解决SAP Cloud SDK中TenantAccessor.getCurrentTenant()报"No Request Available"的问题

看起来你遇到的问题核心是RequestContext没有在请求处理流程中被正确初始化,导致SAP Cloud SDK的TenantAccessor无法从携带Bearer令牌的请求中提取租户信息。同时,@RequestScope的尝试因为应用启动时(entityManagerFactory初始化阶段)不存在请求上下文而失败。下面是具体的分析和解决方案:

问题原因拆解

  1. RequestContext缺失:SAP Cloud SDK的TenantAccessor依赖于RequestContext来读取请求中的JWT令牌(包含租户信息)。如果这个上下文没有在请求进入时被初始化,即使请求带了Authorization头,SDK也无法关联到当前请求,就会抛出TenantNotAvailableException。
  2. @RequestScope的冲突:entityManagerFactory是应用启动时就初始化的单例Bean,而@RequestScope的Bean只能在请求上下文存在时创建,启动阶段没有请求,自然会初始化失败。

解决方案步骤

1. 注册RequestContextFilter确保上下文初始化

首先要确保RequestContextFilter被正确注册到Spring Boot的过滤器链中,并且执行顺序在OAuth2相关过滤器之前——它负责在请求进来时创建RequestContext,把请求的JWT等信息注入到上下文中。

创建一个配置类来注册过滤器:

import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.sap.cloud.sdk.cloudplatform.servlet.RequestContextFilter;

@Configuration
public class CloudSdkConfig {
    @Bean
    public FilterRegistrationBean<RequestContextFilter> requestContextFilter() {
        final FilterRegistrationBean<RequestContextFilter> filterRegistrationBean = new FilterRegistrationBean<>();
        filterRegistrationBean.setFilter(new RequestContextFilter());
        filterRegistrationBean.setOrder(1); // 确保在OAuth2过滤器之前执行
        return filterRegistrationBean;
    }
}

2. 调整TenantIdentifierResolver的实现

不要给TenantIdentifierResolver加@RequestScope,而是让它在**运行时(处理请求时)**从RequestContext中获取租户信息,而不是在Bean注入阶段依赖请求上下文。

示例实现:

import com.sap.cloud.sdk.cloudplatform.tenant.Tenant;
import com.sap.cloud.sdk.cloudplatform.tenant.TenantAccessor;
import com.sap.cloud.sdk.cloudplatform.tenant.exception.TenantNotAvailableException;
import com.sap.cloud.sdk.persistence.multitenancy.TenantIdentifierResolver;

public class CustomTenantIdentifierResolver implements TenantIdentifierResolver {
    @Override
    public String resolveTenantIdentifier() {
        try {
            final Tenant currentTenant = TenantAccessor.getCurrentTenant();
            return currentTenant.getTenantId();
        } catch (final TenantNotAvailableException e) {
            throw new IllegalStateException("Failed to resolve tenant identifier from request", e);
        }
    }
}

这个Resolver是单例Bean,初始化时不会依赖请求上下文;只有在实际处理请求、调用resolveTenantIdentifier方法时,才会从已经初始化好的RequestContext中读取租户信息。

3. 验证OAuth2配置正确性

确保Spring Security的OAuth2配置能正确解析Bearer令牌,并且JWT中包含SAP Cloud SDK默认识别的租户字段(zid)。示例Spring Security配置:

import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
            .anyRequest().authenticated()
            .and()
            .oauth2ResourceServer()
            .jwt()
            .jwtAuthenticationConverter(jwtAuthenticationConverter());
    }

    private JwtAuthenticationConverter jwtAuthenticationConverter() {
        final JwtAuthenticationConverter converter = new JwtAuthenticationConverter();
        // 如需自定义JWT字段映射,可在此添加逻辑,SDK默认会读取zid字段作为租户ID
        return converter;
    }
}

4. 排查验证RequestContext状态

可以在你的RestController中添加一段调试代码,确认RequestContext是否被正确初始化:

import com.sap.cloud.sdk.cloudplatform.servlet.RequestContextAccessor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TenantTestController {
    @GetMapping("/check-context")
    public String checkRequestContext() {
        if (RequestContextAccessor.isCurrentRequestContextAvailable()) {
            return "RequestContext is available - tenant should be resolvable";
        } else {
            return "NO RequestContext found - check filter configuration";
        }
    }
}

用Postman调用这个接口,如果返回"RequestContext is available",说明上下文初始化正常,后续的租户获取逻辑应该就能正常工作了。

内容的提问来源于stack exchange,提问作者Michal Holič

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 09:37:33