如何在Spring Boot中设计两套独立的身份认证与授权配置
Spring Boot 双路径分组认证授权实现方案
核心实现思路
使用Spring Security原生的多SecurityFilterChain机制,为两类路径分别配置独立的安全规则,避免链路互相干扰。
前置依赖
在pom.xml中引入安全starter:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency>
1. 配置/api/**路径的安全规则
该路径优先级设置为最高,仅校验请求头的api-secret-key,无需身份认证:
1.1 自定义Api密钥校验过滤器
@Component public class ApiSecretKeyFilter extends OncePerRequestFilter { // 可从配置文件注入合法密钥 @Value("${api.secret-key}") private String validSecretKey; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String requestKey = request.getHeader("x-api-secret-key"); // 密钥不匹配直接返回401 if (!validSecretKey.equals(requestKey)) { response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); response.getWriter().write("Invalid api secret key"); return; } // 校验通过直接放行,无需设置认证上下文 filterChain.doFilter(request, response); } }
1.2 对应安全过滤器链配置
@Configuration @Order(1) // 优先级高于admin路径配置 public class ApiSecurityConfig { @Autowired private ApiSecretKeyFilter apiSecretKeyFilter; @Bean public SecurityFilterChain apiSecurityFilterChain(HttpSecurity http) throws Exception { // 仅匹配/api/**路径的请求 http.securityMatcher("/api/**") .csrf(AbstractHttpConfigurer::disable) .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) // 所有api路径请求全部放行,仅通过过滤器校验密钥 .authorizeHttpRequests(auth -> auth.anyRequest().permitAll()) // 把密钥校验过滤器加在用户名密码校验过滤器之前 .addFilterBefore(apiSecretKeyFilter, UsernamePasswordAuthenticationFilter.class); return http.build(); } }
2. 配置/admin/**路径的安全规则
该路径优先级次高,需用户名密码登录获取token后才可访问:
2.1 自定义JWT token校验过滤器
@Component public class AdminTokenFilter extends OncePerRequestFilter { // 自行实现JWT工具类,包含token生成、校验、解析用户名功能 @Autowired private JwtUtil jwtUtil; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String token = request.getHeader("Authorization"); // 登录接口直接放行 if (request.getRequestURI().equals("/admin/login")) { filterChain.doFilter(request, response); return; } // token为空或校验不通过返回401 if (token == null || !jwtUtil.validateToken(token)) { response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); response.getWriter().write("Invalid or missing admin token"); return; } // 校验通过后将用户信息存入安全上下文 String username = jwtUtil.getUsernameFromToken(token); UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(username, null, Collections.emptyList()); SecurityContextHolder.getContext().setAuthentication(auth); filterChain.doFilter(request, response); } }
2.2 登录接口实现
@RestController @RequestMapping("/admin") public class AdminLoginController { @Autowired private AuthenticationManager authenticationManager; @Autowired private JwtUtil jwtUtil; @PostMapping("/login") public String login(@RequestBody LoginRequest request) { // 调用自定义UserDetailService做身份认证 Authentication authentication = authenticationManager.authenticate( new UsernamePasswordAuthenticationToken(request.getUsername(), request.getPassword()) ); SecurityContextHolder.getContext().setAuthentication(authentication); // 认证通过生成token返回 return jwtUtil.generateToken(authentication.getName()); } }
2.3 对应安全过滤器链配置
@Configuration @Order(2) public class AdminSecurityConfig { @Autowired private AdminTokenFilter adminTokenFilter; @Autowired private CustomUserDetailService customUserDetailService; @Bean public SecurityFilterChain adminSecurityFilterChain(HttpSecurity http) throws Exception { http.securityMatcher("/admin/**") .csrf(AbstractHttpConfigurer::disable) .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .authorizeHttpRequests(auth -> auth // 登录接口放行 .requestMatchers("/admin/login").permitAll() // 其他admin接口需要认证 .anyRequest().authenticated() ) .addFilterBefore(adminTokenFilter, UsernamePasswordAuthenticationFilter.class) // 注册自定义UserDetailService和密码加密器 .userDetailsService(customUserDetailService) .authenticationProvider(authenticationProvider()); return http.build(); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Bean public AuthenticationProvider authenticationProvider() { DaoAuthenticationProvider provider = new DaoAuthenticationProvider(); provider.setUserDetailsService(customUserDetailService); provider.setPasswordEncoder(passwordEncoder()); return provider; } @Bean public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception { return config.getAuthenticationManager(); } }
关键注意事项
- 每个
SecurityFilterChain必须显式调用securityMatcher绑定对应路径,否则会默认匹配所有请求,导致高优先级链路拦截全部流量 - 优先级数值越小优先级越高,需保证/api链路的优先级高于/admin链路
- 两类路径的过滤器互相独立,不会交叉生效,无需额外做路径排除逻辑
内容的提问来源于stack exchange,提问作者coding_philosophy
相关产品推荐
相关产品推荐

