You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

如何在Spring Security中为/oauth/check_token端点配置IP白名单

嘿,针对你这种分离部署认证服务器和资源服务器、采用client_credentials授权类型的场景,要保护/oauth/check_token端点只允许指定资源服务器IP访问,我给你两个实用的实现方案,都是基于Spring Security的特性来做的:

方案1:直接通过AuthorizationServerSecurityConfigurer配置IP限制

在你的AuthorizationServerConfig类里,重写configure(AuthorizationServerSecurityConfigurer security)方法,结合Spring Security的hasIpAddress()表达式,给/oauth/check_token端点加上IP白名单限制:

@EnableAuthorizationServer
@Configuration
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    // 替换成你的资源服务器IP,多个IP用逗号分隔,比如"192.168.1.100,192.168.1.101"
    private static final String ALLOWED_RESOURCE_SERVER_IPS = "192.168.1.100";

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security
            // 仅允许已认证的客户端 + 白名单IP访问check_token端点
            .checkTokenAccess("isAuthenticated() and hasIpAddress('" + ALLOWED_RESOURCE_SERVER_IPS + "')")
            // 保留你原来的tokenKey访问规则
            .tokenKeyAccess("permitAll()");
        // 其他原有配置(比如tokenStore、clientDetailsService等)继续保留
    }

    // 你的其他配置方法...
}

这个方案的好处是简单直接,完全利用Spring Security的表达式语言实现,不需要额外写过滤器。如果资源服务器是一个IP段,还可以用CIDR格式,比如192.168.1.0/24来匹配整个网段。

方案2:自定义IP拦截过滤器(更灵活的场景)

如果你的白名单IP需要从配置文件读取、或者要处理代理场景下的真实客户端IP,那自定义过滤器会更合适:

首先创建一个IP校验过滤器:

@Component
public class IpWhitelistFilter extends OncePerRequestFilter {

    // 从配置文件读取允许的IP列表,比如在application.properties里配置:resource.server.ips=192.168.1.100,192.168.1.101
    @Value("#{'${resource.server.ips}'.split(',')}")
    private List<String> allowedIps;

    private static final String CHECK_TOKEN_ENDPOINT = "/oauth/check_token";

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        // 只对check_token端点做IP校验
        if (CHECK_TOKEN_ENDPOINT.equals(request.getRequestURI())) {
            String clientIp = getRealClientIp(request);
            if (!allowedIps.contains(clientIp.trim())) {
                response.setStatus(HttpServletResponse.SC_FORBIDDEN);
                response.getWriter().write("Access Denied: Your IP is not whitelisted");
                return;
            }
        }
        // 符合条件就继续执行后续过滤器
        filterChain.doFilter(request, response);
    }

    // 处理代理场景,获取真实客户端IP
    private String getRealClientIp(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        // 如果X-Forwarded-For有多个IP,取第一个(最原始的客户端IP)
        if (ip != null && ip.contains(",")) {
            ip = ip.split(",")[0].trim();
        }
        return ip;
    }
}

然后把这个过滤器注册到认证服务器的安全配置里:

@EnableAuthorizationServer
@Configuration
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private IpWhitelistFilter ipWhitelistFilter;

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security
            // 添加自定义IP过滤器到token端点的认证流程中
            .addTokenEndpointAuthenticationFilter(ipWhitelistFilter)
            // 保留客户端认证要求
            .checkTokenAccess("isAuthenticated()")
            .tokenKeyAccess("permitAll()");
        // 其他原有配置...
    }

    // 你的其他配置方法...
}

一些关键注意事项:

  • 如果你用了反向代理(比如Nginx),一定要确保代理服务器传递了真实的客户端IP(比如设置X-Forwarded-For头),同时在Spring配置中开启server.use-forward-headers=true,否则过滤器拿到的会是代理服务器的IP。
  • 不要去掉isAuthenticated()规则,因为client_credentials模式下,资源服务器调用/oauth/check_token时需要携带客户端的认证信息(比如Basic Auth),这个规则能防止白名单内的未认证请求访问端点。
  • 方案2的配置文件方式更适合生产环境,不需要修改代码就能更新白名单IP。

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

火山引擎 最新活动