diff --git a/mhd-bi/src/main/java/com/mhd/bi/config/BiAccessProperties.java b/mhd-bi/src/main/java/com/mhd/bi/config/BiAccessProperties.java
new file mode 100644
index 000000000..e038e9aa5
--- /dev/null
+++ b/mhd-bi/src/main/java/com/mhd/bi/config/BiAccessProperties.java
@@ -0,0 +1,18 @@
+package com.mhd.bi.config;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+/**
+ * BI 开放接口访问控制(请求头 {@code X-BI-Access-Token})。
+ *
配置了 {@code accessToken} 时必须与请求头完全一致;未配置时仅校验请求头非空。
+ */
+@Data
+@ConfigurationProperties(prefix = "mhd.bi")
+public class BiAccessProperties {
+
+ /**
+ * 与 X-BI-Access-Token 比对的密钥;为空则只要请求头非空即可
+ */
+ private String accessToken = "";
+}
diff --git a/mhd-bi/src/main/java/com/mhd/bi/config/BiReportAccessConfiguration.java b/mhd-bi/src/main/java/com/mhd/bi/config/BiReportAccessConfiguration.java
new file mode 100644
index 000000000..5f5cfb19d
--- /dev/null
+++ b/mhd-bi/src/main/java/com/mhd/bi/config/BiReportAccessConfiguration.java
@@ -0,0 +1,25 @@
+package com.mhd.bi.config;
+
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.boot.web.servlet.FilterRegistrationBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.Ordered;
+
+/**
+ * 注册 /biReport 路径的访问令牌校验
+ */
+@Configuration
+@EnableConfigurationProperties(BiAccessProperties.class)
+public class BiReportAccessConfiguration {
+
+ @Bean
+ public FilterRegistrationBean biReportAccessTokenFilter(BiAccessProperties properties) {
+ FilterRegistrationBean registration = new FilterRegistrationBean<>();
+ registration.setFilter(new BiReportAccessTokenFilter(properties));
+ registration.addUrlPatterns("/biReport/*");
+ registration.setOrder(Ordered.HIGHEST_PRECEDENCE + 100);
+ registration.setName("biReportAccessTokenFilter");
+ return registration;
+ }
+}
diff --git a/mhd-bi/src/main/java/com/mhd/bi/config/BiReportAccessTokenFilter.java b/mhd-bi/src/main/java/com/mhd/bi/config/BiReportAccessTokenFilter.java
new file mode 100644
index 000000000..de9cc6675
--- /dev/null
+++ b/mhd-bi/src/main/java/com/mhd/bi/config/BiReportAccessTokenFilter.java
@@ -0,0 +1,60 @@
+package com.mhd.bi.config;
+
+import org.springframework.lang.NonNull;
+import org.springframework.util.StringUtils;
+import org.springframework.web.filter.OncePerRequestFilter;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * 限制 /biReport 下接口必须携带 {@code X-BI-Access-Token},否则 401。
+ */
+public class BiReportAccessTokenFilter extends OncePerRequestFilter {
+
+ private static final String UNAUTHORIZED_MSG = "未认证,请提供有效的访问Token";
+
+ public static final String HEADER_X_BI_ACCESS_TOKEN = "X-BI-Access-Token";
+
+ private final BiAccessProperties accessProperties;
+
+ public BiReportAccessTokenFilter(BiAccessProperties accessProperties) {
+ this.accessProperties = accessProperties;
+ }
+
+ @Override
+ protected void doFilterInternal(@NonNull HttpServletRequest request,
+ @NonNull HttpServletResponse response,
+ @NonNull FilterChain filterChain) throws ServletException, IOException {
+ if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
+ filterChain.doFilter(request, response);
+ return;
+ }
+
+ String presented = request.getHeader(HEADER_X_BI_ACCESS_TOKEN);
+ if (!StringUtils.hasText(presented)) {
+ writeUnauthorized(response);
+ return;
+ }
+
+ String expected = accessProperties.getAccessToken();
+ if (StringUtils.hasText(expected) && !expected.equals(presented)) {
+ writeUnauthorized(response);
+ return;
+ }
+
+ filterChain.doFilter(request, response);
+ }
+
+ private static void writeUnauthorized(HttpServletResponse response) throws IOException {
+ response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
+ response.setCharacterEncoding(StandardCharsets.UTF_8.name());
+ response.setContentType("application/json;charset=UTF-8");
+ String body = "{\"code\":401,\"msg\":\"" + UNAUTHORIZED_MSG.replace("\"", "\\\"") + "\"}";
+ response.getOutputStream().write(body.getBytes(StandardCharsets.UTF_8));
+ }
+}
diff --git a/mhd-bi/src/main/java/com/mhd/bi/domain/biReport/support/BiSnapshotDelFlag.java b/mhd-bi/src/main/java/com/mhd/bi/domain/biReport/support/BiSnapshotDelFlag.java
new file mode 100644
index 000000000..2e6f83d1f
--- /dev/null
+++ b/mhd-bi/src/main/java/com/mhd/bi/domain/biReport/support/BiSnapshotDelFlag.java
@@ -0,0 +1,18 @@
+package com.mhd.bi.domain.biReport.support;
+
+/**
+ * BI 快照表 {@code DEL_FLAG} 取值(三张 *\_SNAPSHOT 表一致)。
+ * 查询接口仅返回 {@link #NORMAL}。
+ */
+public final class BiSnapshotDelFlag {
+
+ /** 无状态 */
+ public static final int NONE = 0;
+ /** 正常(接口查询可见) */
+ public static final int NORMAL = 1;
+ /** 已删除(逻辑删除,接口不返回) */
+ public static final int DELETED = 2;
+
+ private BiSnapshotDelFlag() {
+ }
+}
diff --git a/mhd-bi/src/main/java/com/mhd/bi/interfaces/facadeApi/biReport/BiReportApi.java b/mhd-bi/src/main/java/com/mhd/bi/interfaces/facadeApi/biReport/BiReportApi.java
index fafae14c3..753219b74 100644
--- a/mhd-bi/src/main/java/com/mhd/bi/interfaces/facadeApi/biReport/BiReportApi.java
+++ b/mhd-bi/src/main/java/com/mhd/bi/interfaces/facadeApi/biReport/BiReportApi.java
@@ -20,7 +20,7 @@ import org.springframework.web.bind.annotation.RestController;
/**
* BI 报表接口
*/
-@Api(tags = "BI报表")
+@Api(tags = "BI报表", description = "须在请求头携带 X-BI-Access-Token,与配置 mhd.bi.access-token 一致")
@Slf4j
@RestController
@RequestMapping("/biReport")
diff --git a/mhd-bi/src/main/resources/bootstrap.yml b/mhd-bi/src/main/resources/bootstrap.yml
index 83148b69b..0ae3bc43a 100644
--- a/mhd-bi/src/main/resources/bootstrap.yml
+++ b/mhd-bi/src/main/resources/bootstrap.yml
@@ -1,6 +1,12 @@
# Tomcat
server:
port: 8021
+
+# BI 报表开放接口(/biReport):请求头 X-BI-Access-Token 须与 mhd.bi.access-token 一致
+mhd:
+ bi:
+ access-token: BI_TOKEN_8x7kL9mN2pQr5vWy3zA6cB
+
# Spring
spring:
application: