如何在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
相关产品推荐
相关产品推荐

