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

Java中生成API Key并加密存储及接口鉴权的最优方案咨询

Java中API Key认证的最优实现方案

核心思路

  • 生成高熵API Key,客户端留存明文,服务端仅存储不可逆的加密哈希值
  • 基于Spring Security实现请求拦截,校验请求头API_KEY与数据库哈希值的匹配性
  • 不推荐JWT:JWT是自包含令牌,更适合会话场景;API Key的简单密钥校验场景下,哈希校验方案更轻量、安全

步骤1:安全生成API Key

使用SecureRandom生成高随机度的字节数组,再通过Base64编码转为易传输的字符串:

import java.security.SecureRandom;
import java.util.Base64;

public class ApiKeyGenerator {
    private static final int KEY_BYTES_LENGTH = 32; // 256位长度,满足安全要求

    public static String generateApiKey() {
        SecureRandom secureRandom = new SecureRandom();
        byte[] keyBytes = new byte[KEY_BYTES_LENGTH];
        secureRandom.nextBytes(keyBytes);
        return Base64.getUrlEncoder().withoutPadding().encodeToString(keyBytes);
    }
}

步骤2:加密存储API Key

采用BCrypt自适应哈希算法存储,防暴力破解,避免明文存储:

import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

public class ApiKeyEncoder {
    // 工作因子设为12,平衡安全性与性能
    private static final BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(12);

    public static String encodeApiKey(String rawApiKey) {
        return encoder.encode(rawApiKey);
    }

    public static boolean matches(String rawApiKey, String encodedApiKey) {
        return encoder.matches(rawApiKey, encodedApiKey);
    }
}

步骤3:Spring Security拦截校验

自定义API Key过滤器

拦截所有API请求,提取请求头并校验:

import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;

public class ApiKeyAuthFilter extends OncePerRequestFilter {
    private final ApiKeyService apiKeyService;

    public ApiKeyAuthFilter(ApiKeyService apiKeyService) {
        this.apiKeyService = apiKeyService;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String apiKey = request.getHeader("API_KEY");

        if (apiKey != null && SecurityContextHolder.getContext().getAuthentication() == null) {
            // 从数据库获取对应客户端的加密API Key(实际业务可根据clientId/应用ID关联)
            String encodedApiKey = apiKeyService.getEncodedApiKey(request.getParameter("clientId"));

            if (encodedApiKey != null && ApiKeyEncoder.matches(apiKey, encodedApiKey)) {
                // 设置认证上下文,可扩展加入权限信息
                UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
                        request.getParameter("clientId"),
                        null,
                        null // 如需权限控制,传入GrantedAuthority列表
                );
                authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                SecurityContextHolder.getContext().setAuthentication(authToken);
            } else {
                response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "无效的API Key");
                return;
            }
        }

        filterChain.doFilter(request, response);
    }
}

Spring Security配置

将自定义过滤器加入拦截链:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

@Configuration
@EnableWebSecurity
public class SecurityConfig {
    private final ApiKeyService apiKeyService;

    public SecurityConfig(ApiKeyService apiKeyService) {
        this.apiKeyService = apiKeyService;
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                .csrf(csrf -> csrf.disable()) // API场景默认关闭CSRF
                .authorizeHttpRequests(auth -> auth
                        .anyRequest().authenticated()
                )
                .addFilterBefore(new ApiKeyAuthFilter(apiKeyService), UsernamePasswordAuthenticationFilter.class);

        return http.build();
    }
}

模拟API Key数据服务

替换为实际数据库查询逻辑:

import org.springframework.stereotype.Service;

@Service
public class ApiKeyService {
    // 实际项目中用JPA/MyBatis实现数据库查询
    public String getEncodedApiKey(String clientId) {
        if ("client_001".equals(clientId)) {
            // 模拟从数据库取出的BCrypt加密值
            return "$2a$12$ZkDd5WzX7yF9hG8jK6lM.NvUoIpQrStVbWcXdYeRfTgShUiJkLmNo";
        }
        return null;
    }
}

关键注意事项

  • 禁止明文存储:BCrypt的自适应哈希特性,即使数据库泄露,攻击者也难以暴力破解
  • 密钥分发:生成后仅向客户端展示一次,客户端需自行妥善保存,丢失后需重新生成
  • 权限扩展:可在认证时为客户端绑定不同权限,实现细粒度API访问控制
  • 定期轮换:提供API Key轮换机制,降低密钥泄露后的风险

内容的提问来源于stack exchange,提问作者Girija Dash

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 17:10:16