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

Spring 5.7+如何基于IP地址与凭证限制资源访问

问题根因

你的配置有5个核心错误,导致自定义IP校验逻辑完全不生效:

  • 核心规则配置错误:security.authorizeRequests().anyRequest().permitAll() 直接放行所有请求,请求根本不会进入认证流程,你写的CustomIpAuthenticationProvider完全没有触发机会。
  • 配置类语法错误:SecurityConfig里filterChain方法外多写了一个闭合大括号,导致userDetailsService的Bean定义不在配置类范围内,既不会被Spring注册,也和你自定义的认证逻辑完全脱节。
  • 认证逻辑冲突:你手动禁用了formLogin,但测试用例却在请求formLogin专属的/login路径,这个路径在禁用formLogin后根本不存在;同时你自定义Provider里硬编码了账号密码校验,和你注册的内存用户完全无关,等于内存用户的配置是无效代码。
  • 不必要的自动配置排除:手动排除SecurityAutoConfiguration会打乱Spring Security默认的过滤器链注册逻辑,反而会导致自定义配置加载异常。
  • IP校验逻辑有遗漏:本地访问默认会走IPv6地址0:0:0:0:0:0:0:1,不在你写的白名单里,本地测试时会误拦截;如果服务前有反向代理,直接取remoteAddress只能拿到代理IP,拿不到真实用户IP。
正确实现方案

1. 修正主启动类

去掉不必要的Security自动配置排除:

@SpringBootApplication
public class MainApp {
    public static void main(String[] args) {
        SpringApplication.run(MainApp.class, args);
    }
}

2. 修正安全配置类

修复语法错误,调整权限规则为所有请求需认证,注册自定义认证提供者,开启HTTP Basic作为凭证传输方式(也可根据需求替换为其他认证方式,默认登录页已禁用):

@Configuration
@EnableWebSecurity
public class SecurityConfig  {

    @Bean
    public CustomIpAuthenticationProvider customIpAuthenticationProvider(
            UserDetailsService userDetailsService,
            PasswordEncoder passwordEncoder
    ) {
        return new CustomIpAuthenticationProvider(userDetailsService, passwordEncoder);
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity security) throws Exception {
        security
                .csrf().disable()
                // 禁用默认登录表单
                .formLogin().disable()
                // 开启HTTP Basic作为凭证校验方式,可根据业务替换
                .httpBasic(Customizer.withDefaults())
                .authorizeHttpRequests(auth -> auth
                        // 所有请求必须认证通过才能访问
                        .anyRequest().authenticated()
                )
                .authenticationProvider(customIpAuthenticationProvider(
                        userDetailsService(),
                        PasswordEncoderFactories.createDelegatingPasswordEncoder()
                ));

        return security.build();
    }

    @Bean
    public InMemoryUserDetailsManager userDetailsService() {
        PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
        UserDetails user = User.withUsername("test_user")
                .password(encoder.encode("password"))
                .roles("USER")
                .build();
        return new InMemoryUserDetailsManager(user);
    }
}

3. 修正自定义认证提供者

去掉硬编码的账号密码校验,注入Spring管理的UserDetailsService和密码编码器做凭证校验,补充IPv6本地地址到白名单方便本地测试:

public class CustomIpAuthenticationProvider implements AuthenticationProvider {

    private final Set<String> whitelist = new HashSet<>();
    private final UserDetailsService userDetailsService;
    private final PasswordEncoder passwordEncoder;

    public CustomIpAuthenticationProvider(UserDetailsService userDetailsService, PasswordEncoder passwordEncoder) {
        this.userDetailsService = userDetailsService;
        this.passwordEncoder = passwordEncoder;
        // 初始化IP白名单
        whitelist.add("11.11.11.11");
        whitelist.add("127.0.0.1");
        whitelist.add("0:0:0:0:0:0:0:1"); // 兼容IPv6本地地址
    }

    @Override
    public Authentication authenticate(Authentication auth){
        WebAuthenticationDetails details = (WebAuthenticationDetails) auth.getDetails();
        String userIp = details.getRemoteAddress();
        System.out.println("当前请求来源IP:" + userIp);

        // 先校验IP白名单
        if (!whitelist.contains(userIp)) {
            throw new BadCredentialsException("IP地址不在白名单范围内");
        }

        // 校验账号密码
        String username = auth.getName();
        String presentedPassword = auth.getCredentials().toString();
        UserDetails user = userDetailsService.loadUserByUsername(username);
        if (!passwordEncoder.matches(presentedPassword, user.getPassword())) {
            throw new BadCredentialsException("用户名或密码错误");
        }

        return new UsernamePasswordAuthenticationToken(user, presentedPassword, user.getAuthorities());
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);
    }
}

4. 提供测试接口与修正测试用例

先写一个测试用的POST接口用于验证效果:

@RestController
public class ResourceController {
    @PostMapping("/api/resource")
    public String getProtectedResource() {
        return "受保护资源访问成功";
    }
}

用MockMvc做单元测试,可手动模拟请求来源IP,避免RestAssured测试时无法修改来源IP的问题:

@SpringBootTest
@AutoConfigureMockMvc
public class IpAuthenticationTest {

    @Autowired
    private MockMvc mockMvc;

    @Test
    public void givenUserWithWrongIPReturnForbidden() throws Exception {
        mockMvc.perform(
                MockMvcRequestBuilders.post("/api/resource")
                        .with(request -> {
                            // 模拟非白名单IP
                            request.setRemoteAddr("12.12.12.12");
                            return request;
                        })
                        .with(SecurityMockMvcRequestPostProcessors.httpBasic("test_user", "password"))
        ).andExpect(MockMvcResultMatchers.status().isForbidden());
    }

    @Test
    public void givenUserWithCorrectIPAndCredentialReturnSuccess() throws Exception {
        mockMvc.perform(
                MockMvcRequestBuilders.post("/api/resource")
                        .with(request -> {
                            // 模拟白名单IP
                            request.setRemoteAddr("11.11.11.11");
                            return request;
                        })
                        .with(SecurityMockMvcRequestPostProcessors.httpBasic("test_user", "password"))
        ).andExpect(MockMvcResultMatchers.status().isOk())
         .andExpect(MockMvcResultMatchers.content().string("受保护资源访问成功"));
    }
}
注意事项
  • 如果服务前部署了Nginx等反向代理,需要在application配置中添加server.forward-headers-strategy=native,让Spring能从X-Forwarded-For请求头获取真实客户端IP,否则白名单校验拿到的永远是代理服务器的内网IP。
  • IP白名单校验如果要提前拦截减少资源消耗,可以自定义OncePerRequestFilter在过滤器链最前面做校验,不用等走到认证Provider层再判断。
  • 生产环境不要硬编码IP白名单,建议放到配置文件或者配置中心动态维护。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.03 10:54:32