用Spring Boot搭建企业AI安全网关:输入审核、PII脱敏、输出校验与审计

文章摘要

企业接入多个大模型后,若每个业务系统自行处理Moderation、敏感信息脱敏、Prompt Injection、工具权限和日志审计,安全策略很快会失控。本文使用Spring Boot设计一个统一AI安全网关,通过标准请求对象串联身份校验、输入长度限制、PII检测、内容审核、Prompt Injection检测、模型代理、输出DLP和审计日志,并提供可扩展接口与核心代码骨架。

一、网关的目标

统一链路:

业务系统
→ AI安全网关
→ 模型Provider

网关负责:

  • 身份与租户;
  • 请求配额;
  • 模型白名单;
  • 输入审核;
  • PII检测与脱敏;
  • Prompt Injection检测;
  • 工具白名单;
  • 输出校验;
  • 成本与审计;
  • 统一错误码。

业务系统负责:

  • 业务场景;
  • 用户体验;
  • 领域权限;
  • 最终业务动作。

安全网关不能代替业务权限,但可以提供统一执行点。

二、项目结构

ai-security-gateway
├── controller
│   └── AiGatewayController.java
├── model
│   ├── AiGatewayRequest.java
│   ├── AiGatewayResponse.java
│   └── SecurityDecision.java
├── security
│   ├── SecurityPipeline.java
│   ├── InputLengthGuard.java
│   ├── PiiGuard.java
│   ├── ModerationGuard.java
│   ├── PromptInjectionGuard.java
│   └── OutputGuard.java
├── provider
│   ├── ModelProvider.java
│   └── SpringAiModelProvider.java
├── audit
│   ├── AuditEvent.java
│   └── AuditService.java
└── exception
    └── GatewayExceptionHandler.java

三、统一请求对象

public record AiGatewayRequest(
        String requestId,
        String tenantId,
        String userId,
        String scenario,
        String modelTier,
        String message,
        Map context,
        Set requestedTools
) {
}

注意:

tenantId和userId不应直接信任请求Body,应从认证Token或网关身份上下文写入。

对外请求可以不包含这两个字段,Controller负责补全。

四、统一安全动作

public enum SecurityAction {
    ALLOW,
    MASK_AND_ALLOW,
    SAFE_MODE,
    HUMAN_REVIEW,
    REJECT
}
public record SecurityDecision(
        SecurityAction action,
        Set reasons,
        String sanitizedText,
        Set sensitiveEntities,
        double riskScore
) {
    public boolean allowed() {
        return action == SecurityAction.ALLOW
                || action == SecurityAction.MASK_AND_ALLOW
                || action == SecurityAction.SAFE_MODE;
    }
}

五、定义Guard接口

public interface InputGuard {

    GuardResult evaluate(
            GuardContext context,
            String text
    );
}
public record GuardResult(
        boolean passed,
        SecurityAction recommendedAction,
        String transformedText,
        Set reasons,
        double score
) {
}

每个Guard只做一件事,SecurityPipeline负责汇总。

六、输入长度Guard

@Component
public class InputLengthGuard
        implements InputGuard {

    private static final int MAX_CHARS = 50_000;

    @Override
    public GuardResult evaluate(
            GuardContext context,
            String text
    ) {
        if (text == null || text.isBlank()) {
            return new GuardResult(
                    false,
                    SecurityAction.REJECT,
                    text,
                    Set.of("EMPTY_INPUT"),
                    1.0
            );
        }

        if (text.length() > MAX_CHARS) {
            return new GuardResult(
                    false,
                    SecurityAction.REJECT,
                    text,
                    Set.of("INPUT_TOO_LONG"),
                    1.0
            );
        }

        return passed(text);
    }
}

长度限制应按场景配置,而不是全局固定。

七、PII Guard

实体对象:

public record SensitiveEntity(
        String type,
        int start,
        int end,
        String replacement,
        String hash
) {
}

基础检测:

@Component
public class PiiGuard implements InputGuard {

    private static final Pattern MOBILE =
            Pattern.compile("(?");

        if (!masked.equals(text)) {
            return new GuardResult(
                    true,
                    SecurityAction.MASK_AND_ALLOW,
                    masked,
                    Set.of("MOBILE_DETECTED"),
                    0.7
            );
        }

        return passed(text);
    }
}

生产环境还应:

  • 校验身份证校验位;
  • 支持企业自定义字段;
  • 保留受控映射;
  • 区分允许使用和禁止外发;
  • 支持中文姓名与地址NER。

八、Moderation Guard

@Component
public class ModerationGuard
        implements InputGuard {

    private final ModerationModel moderationModel;

    public ModerationGuard(
            ModerationModel moderationModel
    ) {
        this.moderationModel = moderationModel;
    }

    @Override
    public GuardResult evaluate(
            GuardContext context,
            String text
    ) {
        ModerationResponse response =
                moderationModel.call(
                        new ModerationPrompt(text)
                );

        ModerationSummary summary =
                mapResponse(response);

        if (summary.highRisk()) {
            return new GuardResult(
                    false,
                    SecurityAction.REJECT,
                    text,
                    summary.categories(),
                    summary.maxScore()
            );
        }

        return passed(text);
    }
}

不要让业务层直接解析Provider特定结构。

九、Prompt Injection Guard

使用规则和模型组合:

@Component
public class PromptInjectionGuard
        implements InputGuard {

    private final List patterns = List.of(
            Pattern.compile("忽略.{0,20}(规则|指令|提示词)"),
            Pattern.compile("(输出|打印).{0,20}系统提示词"),
            Pattern.compile("(调用|执行).{0,20}(工具|命令)")
    );

    @Override
    public GuardResult evaluate(
            GuardContext context,
            String text
    ) {
        boolean matched = patterns.stream()
                .anyMatch(p -> p.matcher(text).find());

        if (matched) {
            return new GuardResult(
                    false,
                    SecurityAction.HUMAN_REVIEW,
                    text,
                    Set.of("PROMPT_INJECTION_SUSPECTED"),
                    0.75
            );
        }

        return passed(text);
    }
}

不能只检测中文固定短语,还需要对抗测试和语义分类。

十、SecurityPipeline

@Service
public class SecurityPipeline {

    private final List guards;

    public SecurityPipeline(
            List guards
    ) {
        this.guards = guards;
    }

    public SecurityDecision evaluateInput(
            GuardContext context,
            String originalText
    ) {
        String currentText = originalText;
        Set reasons = new LinkedHashSet();
        SecurityAction action = SecurityAction.ALLOW;
        double maxRisk = 0.0;

        for (InputGuard guard : guards) {
            GuardResult result = guard.evaluate(
                    context,
                    currentText
            );

            reasons.addAll(result.reasons());
            maxRisk = Math.max(maxRisk, result.score());

            if (result.transformedText() != null) {
                currentText = result.transformedText();
            }

            action = merge(action, result.recommendedAction());

            if (action == SecurityAction.REJECT) {
                break;
            }
        }

        return new SecurityDecision(
                action,
                reasons,
                currentText,
                Set.of(),
                maxRisk
        );
    }
}

动作优先级:

REJECT
> HUMAN_REVIEW
> SAFE_MODE
> MASK_AND_ALLOW
> ALLOW

十一、模型Provider抽象

public interface ModelProvider {

    ModelResult generate(
            SafeAiRequest request
    );
}
@Service
public class SpringAiModelProvider
        implements ModelProvider {

    private final ChatClient chatClient;

    @Override
    public ModelResult generate(
            SafeAiRequest request
    ) {
        String content = chatClient.prompt()
                .system(request.systemPrompt())
                .user(request.sanitizedMessage())
                .call()
                .content();

        return new ModelResult(content);
    }
}

Provider层不应绕过安全Pipeline。

十二、模型白名单

public ModelTier resolveModel(
        String tenantId,
        String scenario,
        String requestedTier
) {
    ModelPolicy policy = policyRepository.load(
            tenantId,
            scenario
    );

    if (!policy.allowedTiers().contains(requestedTier)) {
        throw new ModelNotAllowedException();
    }

    return ModelTier.valueOf(requestedTier);
}

防止业务系统随意使用高成本或未审批模型。

十三、工具白名单

请求中的工具集合必须与服务端策略求交集:

Set allowedTools =
        toolPolicyService.allowedTools(
                tenantId,
                userId,
                scenario
        );

Set effectiveTools =
        requestedTools.stream()
                .filter(allowedTools::contains)
                .collect(Collectors.toUnmodifiableSet());

不要直接把客户端传来的工具名注册给ChatClient。

十四、输出Guard

输出检查:

PII
Moderation
系统提示词泄露
密钥格式
证据引用
业务禁用表达

接口:

public interface OutputGuard {
    GuardResult evaluate(
            GuardContext context,
            String modelOutput
    );
}

高风险输出可以:

  • 拒绝;
  • 脱敏;
  • 重写;
  • 转人工;
  • 返回安全模板。

不要让同一个模型无条件“自我审核”后直接放行。

十五、Controller完整流程

@PostMapping("/v1/ai/generate")
public AiGatewayResponse generate(
        Authentication authentication,
        @Valid @RequestBody GatewayInput input
) {
    AuthenticatedUser user =
            identityService.current(authentication);

    AiGatewayRequest request =
            requestFactory.create(user, input);

    SecurityDecision inputDecision =
            securityPipeline.evaluateInput(
                    GuardContext.from(request),
                    request.message()
            );

    auditService.recordInputDecision(
            request,
            inputDecision
    );

    if (!inputDecision.allowed()) {
        throw new AiSecurityException(
                inputDecision.action()
        );
    }

    ModelResult modelResult = modelProvider.generate(
            safeRequestFactory.create(
                    request,
                    inputDecision
            )
    );

    SecurityDecision outputDecision =
            securityPipeline.evaluateOutput(
                    GuardContext.from(request),
                    modelResult.content()
            );

    if (!outputDecision.allowed()) {
        throw new AiOutputBlockedException();
    }

    return responseFactory.create(
            request,
            outputDecision.sanitizedText()
    );
}

十六、审计事件

public record AuditEvent(
        String requestId,
        String tenantId,
        String userIdHash,
        String scenario,
        String model,
        String inputHash,
        int inputLength,
        SecurityAction inputAction,
        SecurityAction outputAction,
        Set reasons,
        long latencyMs,
        Instant createdAt
) {
}

默认不要保存完整输入输出。

敏感原文如需留存,应进入单独加密审计库。

十七、统一错误码

AI_INPUT_BLOCKED
AI_OUTPUT_BLOCKED
AI_HUMAN_REVIEW_REQUIRED
AI_MODEL_NOT_ALLOWED
AI_TOOL_NOT_ALLOWED
AI_SECURITY_SERVICE_UNAVAILABLE
AI_RATE_LIMITED

前端不要看到内部风险规则和阈值。

十八、失败策略

Moderation不可用

高风险场景:

Fail Closed

低风险内部场景:

进入SAFE_MODE

DLP不可用

含敏感数据的业务应拒绝或转人工。

审计服务不可用

高风险工具调用应阻断;普通问答可以写入本地可靠队列后继续。

十九、可观测性

指标:

ai_gateway_requests_total
ai_gateway_blocked_total
ai_gateway_review_total
ai_gateway_pii_detected_total
ai_gateway_injection_detected_total
ai_gateway_security_latency
ai_gateway_model_latency
ai_gateway_output_blocked_total

标签不要使用原始userId或Prompt,避免高基数和隐私泄露。

二十、生产补齐项

  • Redis配额;
  • OPA或Cedar策略引擎;
  • OpenTelemetry;
  • 密钥管理;
  • 模型路由;
  • 流式输出安全;
  • 文件上传沙箱;
  • 多模态审核;
  • 人工审核后台;
  • 对抗测试集;
  • 灰度规则发布。

总结

企业AI安全网关的核心价值是把分散在各业务系统中的控制统一为:

身份
→ 输入安全
→ 数据脱敏
→ 模型与工具策略
→ 输出安全
→ 审计

它不替代业务权限,但可以成为所有模型调用必须经过的统一安全执行点。

延伸阅读

如果你正在关注Spring Boot、企业AI安全、RAG、Agent与模型网关建设,欢迎访问 智元界

https://www.zyentor.com/

智元界将持续分享可运行的技术实战、架构设计、问题排查与企业应用案例。