如何跳过Spring Security中服务间Feign调用的@PreAuthorize校验
场景说明
服务A通过Spring Security配置了方法级权限校验,同时通过FeignClient调用服务B。目前FeignClient接口上标注了@PreAuthorize注解,导致服务A内部调用该Feign方法时会触发权限校验,需要避免这一行为。
服务A的Security配置
@Configuration @EnableWebSecurity @EnableMethodSecurity( securedEnabled = true, jsr250Enabled = true ) @SecurityScheme( name = "bearerAuth", type = SecuritySchemeType.HTTP, bearerFormat = "JWT", scheme = "bearer" ) @RequiredArgsConstructor public class SecurityConfig { private final JwtTokenFilter jwtTokenFilter; @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http.csrf(AbstractHttpConfigurer::disable) .cors(AbstractHttpConfigurer::disable) .sessionManagement(httpSecuritySessionManagementConfigurer -> httpSecuritySessionManagementConfigurer.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .authorizeHttpRequests(req -> req .requestMatchers( "/login/**", ).permitAll() .anyRequest().authenticated()) .addFilterBefore(jwtTokenFilter, UsernamePasswordAuthenticationFilter.class); return http.build(); } }
FeignClient及调用逻辑
// UserService调用FeignClient @Service public class UserService { @Autowired private SlApiClient slApiClient; public CustomerDTO getCustomerById(UUID id) { return slApiClient.getCustomerById(id); } /** * 其他使用slApiClient的方法 */ }
// FeignClient接口 @FeignClient(url = "${endpoints.sl-api-uri}", name = "SL-API-CLIENT-V1", primary = false) public interface SlApiClient { @GetMapping("/users/{id}") @PreAuthorize("hasAuthority('users.read')") CustomerDTO getCustomerById(@PathVariable UUID id); /** * 其他方法 */ }
解决方案
方案1:直接移除FeignClient接口上的@PreAuthorize注解
@PreAuthorize注解的作用是对当前服务(服务A)的方法调用做权限校验,而Feign调用是跨服务请求,权限校验逻辑应该放在服务B的接口上。服务A作为调用方,不需要在Feign接口上添加该注解,直接删除即可跳过校验:
@FeignClient(url = "${endpoints.sl-api-uri}", name = "SL-API-CLIENT-V1", primary = false) public interface SlApiClient { @GetMapping("/users/{id}") // 移除@PreAuthorize注解 CustomerDTO getCustomerById(@PathVariable UUID id); /** * 其他方法 */ }
方案2:修改@PreAuthorize表达式放行内部调用
如果需要保留注解(比如有特殊场景),可以修改表达式直接放行该方法的权限校验:
@FeignClient(url = "${endpoints.sl-api-uri}", name = "SL-API-CLIENT-V1", primary = false) public interface SlApiClient { @GetMapping("/users/{id}") // 放行该方法的权限校验 @PreAuthorize("permitAll()") CustomerDTO getCustomerById(@PathVariable UUID id); /** * 其他方法 */ }
方案3:配置方法级权限的忽略规则
如果有多个Feign接口需要批量处理,可以在SecurityConfig中自定义规则忽略Feign接口的方法校验:
@Configuration @EnableWebSecurity @EnableMethodSecurity( securedEnabled = true, jsr250Enabled = true ) public class SecurityConfig { // ... 其他已有配置 @Bean public MethodSecurityMetadataSource methodSecurityMetadataSource() { DefaultMethodSecurityMetadataSource metadataSource = new DefaultMethodSecurityMetadataSource(); // 忽略SlApiClient接口下所有方法的权限校验 metadataSource.addIgnoredMethods(SlApiClient.class); return metadataSource; } }
内容的提问来源于stack exchange,提问作者ifog
相关产品推荐
相关产品推荐

