游乐游手机版
首页/编程语言/文章详情

SpringSecurity实现APIKey认证的完整方案详解

时间:2026-07-22 19:19
基于SpringSecurity实现APIKey认证,需自定义过滤器、令牌及认证提供者,使流程适配接口密钥模式。核心组件包括ApiKeyAuthFilter、ApiKeyAuthenticationToken、ApiKeyAuthenticationProvider及SecurityConfig,通过过滤器链与认证管理器完成密钥校验与授权。

先说几个核心判断:基于Spring Security实现API Key认证,本质上就是自定义一套过滤器、令牌和认证提供者,让原本面向用户名密码的认证流程能够无缝适配接口密钥模式。整个方案由四个关键组件构成,下面逐一拆解。

核心实现类

  • ApiKeyAuthFilter 认证过滤器
  • ApiKeyAuthenticationToken 认证令牌
  • ApiKeyAuthenticationProvider API认证鉴权
  • SecurityConfig 安全配置类

ApiKeyAuthFilter 认证过滤器

方法概要

基于SpringSecurity实现APIKey认证的完整方案

OncePerRequestFilter 这个抽象基类,由 Spring Web 专门提供,用于确保过滤器在一次HTTP请求的完整处理流程中仅执行一次。继承它,你只需要实现 doFilterInternal 方法即可。

通过 @Component 注解标注,自动注册为 Spring Bean。然后注入 AuthenticationManager——这是 Spring Security 的认证总入口。调用它的 authenticate() 方法,会遍历所有 AuthenticationProvider,找到支持 ApiKeyAuthenticationToken 的那个(即你自定义的 ApiKeyAuthenticationProvider),执行验证逻辑。

@Component
public class ApiKeyAuthFilter extends OncePerRequestFilter {

    @Autowired
    private AuthenticationManager authenticationManager; // 注入 AuthenticationManager

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        String apiKey = request.getHeader("X-API-Key");
        // 只处理 /api/ 开头的接口(根据你的需求调整)
        if (apiKey != null && request.getRequestURI().startsWith("/api/")) {
            try {

                // 1. 创建未认证的 Token
                ApiKeyAuthenticationToken authToken = new ApiKeyAuthenticationToken(apiKey);
                // 2. ⭐️ 关键:显式调用 AuthenticationManager 进行认证
                Authentication authenticated = authenticationManager.authenticate(authToken);
                // 3. 将认证成功的 Token 放入 SecurityContext
                SecurityContextHolder.getContext().setAuthentication(authenticated);

            } catch (AuthenticationException e) {
                // 认证失败:清空上下文,并返回 401
                SecurityContextHolder.clearContext();
                response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
                response.getWriter().write("Invalid API Key");
                return;
            }
        }

        filterChain.doFilter(request, response);
    }
}

ApiKeyAuthenticationToken 认证令牌

AbstractAuthenticationToken 是 Spring Security 提供的认证令牌抽象类。继承它后,只需实现用户登录及令牌校验的操作。当 setAuthenticated 为 true 时,直接放行。

public class ApiKeyAuthenticationToken extends AbstractAuthenticationToken {
    private final Object principal;

    public ApiKeyAuthenticationToken(Object principal) {
        // credentials 设为 null(API Key 已用于认证,无需再存)
        super(null);
        this.principal = principal;
        // 初始为未认证(由 AuthenticationProvider 设置为 true)
        setAuthenticated(false);
    }

    @Override
    public Object getCredentials() {
        return principal;
    }

    @Override
    public Object getPrincipal() {
        return principal;
    }
}

ApiKeyAuthenticationProvider 认证提供者

这是 Spring Security 中实现 API Key 认证的核心组件,负责验证客户端传入的 API Key 是否合法,并生成已认证的安全上下文(Authentication)。

核心关系:接口约定 + 方法实现(契约模式)

AuthenticationProvider 是 Spring Security 定义的认证核心接口(契约),authenticate() 是这个接口强制要求实现的核心方法(契约内容)。简单说:

  • implements AuthenticationProvider:表示你的类“承诺遵守 Spring Security 的认证规则”;
  • authenticate():是你兑现这个承诺的“具体认证逻辑”(校验 API Key、用户名密码等)。
@Component
public class ApiKeyAuthenticationProvider implements AuthenticationProvider {

    @Autowired
    private ISysApiKeysService sysApiKeysService;

    // 核心方法:执行具体的认证逻辑
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        String providedKey = (String) authentication.getCredentials();
        SysApiKeys sysApiKeys = sysApiKeysService.selectByApikey(providedKey);
        if (sysApiKeys != null) {
            ApiKeyAuthenticationToken authenticatedToken =
                    new ApiKeyAuthenticationToken(sysApiKeys);
            return authenticatedToken;
        }

        throw new BadCredentialsException("Invalid API Key");
    }

    // 辅助方法:判断当前 Provider 是否支持处理某个类型的 Token
    @Override
    public boolean supports(Class authentication) {
        return ApiKeyAuthenticationToken.class.isAssignableFrom(authentication);
    }
}

SecurityConfig 安全配置类

@EnableMethodSecurity 注解(配合 @Configuration)用于启用方法级别的权限控制,例如通过 @PreAuthorize@Secured 等注解限制接口访问。

addFilterBefore() 是 Spring Security 中用于自定义过滤器链顺序的核心方法,其作用是在指定的内置(或已注册)Filter 之前插入你自己的 Filter。因此必须在原有 UsernamePasswordAuthenticationFilter 认证之前先执行 apiKeyAuthFilter

// 添加 Api filter
.addFilterBefore(apiKeyAuthFilter, UsernamePasswordAuthenticationFilter.class)

protected SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception 是 Spring Security 中配置 HTTP 层面安全规则的核心方法。它的核心作用是通过 HttpSecurity 对象定制请求的安全策略——哪些请求需要认证、哪些放行、用什么方式认证、异常如何处理等。下面从核心作用、常用配置、实战示例、与方法级权限的区别四个维度来讲清楚。

  • 该方法的本质是构建一个 SecurityFilterChain 过滤器链,Spring Security 会将其应用到所有 HTTP 请求上,实现:
    控制哪些请求需要认证、哪些请求可以匿名访问(放行);
    定义认证方式(如 HTTP Basic、表单登录、JWT 过滤器等);
    配置跨域(CORS)、CSRF 防护、会话管理;
    定制认证失败、权限不足的响应(如返回 JSON 而非默认页面);
    整合自定义过滤器(如 JWT 校验过滤器)。

完整代码

/**
 * spring security配置
 * 
 * @author 
 */
@EnableMethodSecurity(prePostEnabled = true, securedEnabled = true)
@Configuration
public class SecurityConfig
{
    /**
     * 自定义用户认证逻辑
     */
    @Autowired
    private UserDetailsService userDetailsService;
    
    /**
     * 认证失败处理类
     */
    @Autowired
    private AuthenticationEntryPointImpl unauthorizedHandler;

    /**
     * 退出处理类
     */
    @Autowired
    private LogoutSuccessHandlerImpl logoutSuccessHandler;

    /**
     * token认证过滤器
     */
    @Autowired
    private JwtAuthenticationTokenFilter authenticationTokenFilter;

    @Autowired
    private ApiKeyAuthFilter apiKeyAuthFilter;
    
    /**
     * 跨域过滤器
     */
    @Autowired
    private CorsFilter corsFilter;

    /**
     * 允许匿名访问的地址
     */
    @Autowired
    private PermitAllUrlProperties permitAllUrl;

    @Autowired
    ApiKeyAuthenticationProvider apiKeyAuthenticationProvider;

    /**
     * 身份验证实现
     */
    @Bean
    public AuthenticationManager authenticationManager()
    {
        DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
        daoAuthenticationProvider.setUserDetailsService(userDetailsService);
        daoAuthenticationProvider.setPasswordEncoder(bCryptPasswordEncoder());

        return new ProviderManager(Arrays.asList(daoAuthenticationProvider,
                apiKeyAuthenticationProvider));
    }

    /**
     * anyRequest          |   匹配所有请求路径
     * access              |   SpringEl表达式结果为true时可以访问
     * anonymous           |   匿名可以访问
     * denyAll             |   用户不能访问
     * fullyAuthenticated  |   用户完全认证可以访问(非remember-me下自动登录)
     * hasAnyAuthority     |   如果有参数,参数表示权限,则其中任何一个权限可以访问
     * hasAnyRole          |   如果有参数,参数表示角色,则其中任何一个角色可以访问
     * hasAuthority        |   如果有参数,参数表示权限,则其权限可以访问
     * hasIpAddress        |   如果有参数,参数表示IP地址,如果用户IP和参数匹配,则可以访问
     * hasRole             |   如果有参数,参数表示角色,则其角色可以访问
     * permitAll           |   用户可以任意访问
     * rememberMe          |   允许通过remember-me登录的用户访问
     * authenticated       |   用户登录后可访问
     */
    @Bean
    protected SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception
    {
        return httpSecurity
            // CSRF禁用,因为不使用session
            .csrf(csrf -> csrf.disable())
            // 禁用HTTP响应标头
            .headers((headersCustomizer) -> {
                headersCustomizer.cacheControl(cache -> cache.disable()).frameOptions(options -> options.sameOrigin());
            })
            // 认证失败处理类
            .exceptionHandling(exception -> exception.authenticationEntryPoint(unauthorizedHandler))
            // 基于token,所以不需要session
            .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
            // 注解标记允许匿名访问的url
            .authorizeHttpRequests((requests) -> {
                permitAllUrl.getUrls().forEach(url -> requests.antMatchers(url).permitAll());
                // 对于登录login 注册register 验证码captchaImage 允许匿名访问
                requests.antMatchers("/login", "/register", "/captchaImage").permitAll()
                    // 静态资源,可匿名访问
                    .antMatchers(HttpMethod.GET,
                            "/",
                            "/*.html",
                            "/**/*.html",
                            "/**/*.css",
                            "/**/*.js",
                            "/profile/**",
                            "/cadre/neo4j/**",
                            "/cadre/evaluation/rqcode/**")
                        .permitAll()
                    .antMatchers(HttpMethod.POST,
                            "/cadre/cadre/home/recalculate")
                        .permitAll()
                    .antMatchers(
                            "/swagger-ui.html",
                            "/swagger-resources/**",
                            "/webjars/**",
                            "/*/api-docs",
                            "/cadre/evaluation/qrcode/**",
                            "/cadre/evaluation/grade/rqcodeScore/**",
                            "/druid/**")
                        .permitAll()
                    // 除上面外的所有请求全部需要鉴权认证
                    .anyRequest().authenticated();
            })
            // 添加Logout filter
            .logout(logout -> logout.logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler))
            // 添加 Api filter
            .addFilterBefore(apiKeyAuthFilter, UsernamePasswordAuthenticationFilter.class)
            // 添加JWT filter
            .addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class)
            // 添加CORS filter
            .addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class)
            .addFilterBefore(corsFilter, LogoutFilter.class)
            .build();
    }

    /**
     * 强散列哈希加密实现
     */
    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder()
    {
        return new BCryptPasswordEncoder();
    }
}

以上就是基于Spring Security实现API Key认证的完整方案。

来源:https://www.jb51.net/program/3678421ef.htm
上一篇SpringBoot项目引入本地JAR包详细步骤 下一篇Rust类型系统:Send与Sync自动推导实现线程安全
本站内容用于信息整理与展示,如有侵权或内容问题请及时联系处理。

相关推荐

补充同频道和同主题内容,方便继续浏览更多相关内容。

同类最新

继续查看同栏目最近更新的文章。

更多
FileZilla断点续传设置与操作指南
编程语言 · 2026-07-25

FileZilla断点续传设置与操作指南

FileZilla支持断点续传,需客户端与服务器均开启REST命令。设置中确保启用断点续传及继续传输选项。中断后自动或手动从断点恢复。注意服务器支持、传输模式匹配及文件完整性校验。

Debian系统C++编译器位置查找方法
编程语言 · 2026-07-25

Debian系统C++编译器位置查找方法

在Debian系统中,通过apt安装的C++编译器g++默认位于 usr bin g++,可使用which或whereis命令验证路径。g++属于build-essential软件包,若未安装则需执行sudoaptinstallbuild-essential。该包还包含gcc、make等编译工具链,g++是GNUC++编译器,实际是符号链接指向具体版本,验证

Debian系统安装C++环境的方法
编程语言 · 2026-07-25

Debian系统安装C++环境的方法

在Debian系统安装C++开发环境:先sudoaptupdate更新包列表,再sudoaptinstallbuild-essential安装编译工具链,或单独安装g++。用g++--version验证。可选安装VSCode、GDB、CMake等工具并配置默认编译器版本。

Debian系统C++开发环境配置指南
编程语言 · 2026-07-25

Debian系统C++开发环境配置指南

在Debian系统中,先执行aptupdate更新软件包列表,再安装build-essential元包即可获得GCC、G++、Make和GDB。通过运行g++--version命令验证编译器安装成功。可选安装VisualStudioCode、CLion等编辑器及CMake构建工具,并编写一个简单的HelloWorld程序,使用g++编译运行以验证环境配置正确

通过cpustat工具查看CPU状态的具体方法与详细步骤
编程语言 · 2026-07-25

通过cpustat工具查看CPU状态的具体方法与详细步骤

cpustat是sysstat包中的CPU监控工具,可按固定间隔输出带时间戳的CPU使用率统计。安装后运行cpustat即可实时显示各核心信息,常用指标包括%usr、%sys、%iowait、%steal和%idle,用于定位用户态、内核态或I O瓶颈。高级选项-c可显示单核统计,-m可同时查看内存使用,适合脚本采集和性能分析。