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

如何在SpringBoot的SecurityConfig中统一实现licenseKey授权验证

统一处理LicenseKey验证的Spring Boot Security配置方案

步骤1:自定义LicenseKey认证过滤器

创建一个过滤器,负责从请求头提取licenseKey并完成验证,验证通过后将用户信息存入Spring Security上下文:

import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
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;

public class LicenseKeyAuthenticationFilter extends OncePerRequestFilter {

    private final UserDetailsService userDetailsService;

    public LicenseKeyAuthenticationFilter(UserDetailsService userDetailsService) {
        this.userDetailsService = userDetailsService;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        // 从请求头获取licenseKey
        String licenseKey = request.getHeader("licenseKey");

        // 仅当licenseKey存在且当前未认证时执行验证
        if (licenseKey != null && SecurityContextHolder.getContext().getAuthentication() == null) {
            try {
                // 复用原有逻辑验证licenseKey
                UserDetails userDetails = userDetailsService.loadUserByUsername(licenseKey);
                
                // 生成认证Token并放入上下文
                UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
                        userDetails, null, userDetails.getAuthorities());
                SecurityContextHolder.getContext().setAuthentication(authToken);
            } catch (Exception e) {
                // 验证失败直接返回401
                response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "无效或缺失licenseKey");
                return;
            }
        }

        filterChain.doFilter(request, response);
    }
}

步骤2:配置SecurityConfig类

在继承WebSecurityConfigurerAdapter的配置类中,注册自定义过滤器并配置全局规则:

import org.springframework.beans.factory.annotation.Autowired;
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.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    private final UserDetailsService userDetailsService;

    @Autowired
    public SecurityConfig(UserDetailsService userDetailsService) {
        this.userDetailsService = userDetailsService;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                // REST接口关闭CSRF防护
                .csrf().disable()
                // 所有请求必须经过认证
                .authorizeRequests()
                .anyRequest().authenticated()
                .and()
                // 在默认用户名密码过滤器前添加自定义过滤器
                .addFilterBefore(licenseKeyAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
    }

    @Bean
    public LicenseKeyAuthenticationFilter licenseKeyAuthenticationFilter() {
        return new LicenseKeyAuthenticationFilter(userDetailsService);
    }
}

步骤3:改造接口方法

接口不再需要传入HttpServletRequest,直接从Security上下文获取已认证用户:

@GetMapping(path="some/path")
public ResponseEntity<> viewDetails(MappingVO mappingVO) {
    // 从上下文获取用户信息
    CustomUserDetails userDetails = (CustomUserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    String licenseUser = userDetails.getUser().getEmailAddress();
    // 后续业务逻辑
    ....
}

@DeleteMapping(path="some/path")
public ResponseEntity<> deletePart(Part part) {
    CustomUserDetails userDetails = (CustomUserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    String licenseUser = userDetails.getUser().getEmailAddress();
    // 后续业务逻辑
    ....
}

可选:自定义异常处理

如果需要统一的错误响应格式,添加全局异常处理器:

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(BadCredentialsException.class)
    public ResponseEntity<String> handleBadCredentials() {
        return new ResponseEntity<>("无效的licenseKey", HttpStatus.UNAUTHORIZED);
    }

    @ExceptionHandler(Exception.class)
    public ResponseEntity<String> handleAuthFailure(Exception e) {
        return new ResponseEntity<>("认证失败:" + e.getMessage(), HttpStatus.UNAUTHORIZED);
    }
}

完成以上配置后,所有请求都会先经过过滤器验证licenseKey,无需在每个接口中重复编写验证逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 21:40:58