Implementing IP Whitelist in Nginx and Spring Boot with a Custom HandlerInterceptor
This article demonstrates two practical methods for restricting request IPs—configuring allow/deny rules in Nginx and building a Spring Boot whitelist check using a custom HandlerInterceptor, complete with database schema, service logic, and MVC configuration.
In this article, the author explains two approaches to restrict access by IP address for a front‑back separated project: configuring an allow/deny whitelist in Nginx and implementing a whitelist check inside a Spring Boot application using a custom HandlerInterceptor .
For the Nginx solution, the directives allow and deny must be used together, placed in the http , server or location blocks, with allow preceding deny . Example configurations for location , server and http modules are provided.
server {
listen 8059;
server_name xxx.xxx.com;
charset utf-8;
location /api {
allow 192.168.183.89;
deny all;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://127.0.0.1:9659/api;
proxy_read_timeout 259200;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
}
location /manage {
allow 192.168.183.89;
deny all;
.......
}
......
} server {
listen 8059;
server_name xxx.xxx.com;
charset utf-8;
allow 192.168.29.36;
allow 192.168.30.26;
......
deny all;
location /api {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://127.0.0.1:9659/api;
proxy_read_timeout 259200;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
}
} http {
include mime.types;
default_type application/octet-stream;
charset utf-8,gbk;
client_max_body_size 1024m;
client_body_buffer_size 300k;
sendfile on;
tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
fastcgi_connect_timeout 3000;
fastcgi_send_timeout 3000;
fastcgi_read_timeout 3000;
allow 192.168.56.92;
allow 192.168.89.129;
......
deny all;
server {
listen 8059;
server_name xxx.xxx.com;
charset utf-8;
.....
}
}Using Nginx requires a server reload after each change, which can be inconvenient.
The Spring Boot solution starts with a database table to store whitelist entries, then defines a mapper, service, and interceptor to enforce the rule at runtime without restarting the application.
Table definition (MySQL):
CREATE TABLE `sh_sys_white_list` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
`ip` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT 'ip地址',
`is_white` tinyint DEFAULT NULL COMMENT '是否白名单(1:是,2:否)',
`create_by` bigint DEFAULT NULL COMMENT '创建人',
`create_at` datetime DEFAULT NULL COMMENT '创建时间',
`update_by` bigint DEFAULT NULL COMMENT '更新人',
`update_at` datetime DEFAULT NULL COMMENT '更新时间',
`deleted` tinyint DEFAULT NULL COMMENT '逻辑删除(0:正常,1:删除)',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='白名单管理表';Service implementation that queries the table:
package com.zhush.admin.service.impl;
import com.mybatisflex.core.query.QueryWrapper;
import com.zhush.admin.entity.SysWhiteList;
import com.zhush.admin.mapper.SysWhiteListMapper;
import com.zhush.admin.service.ISysWhiteListService;
import com.zhush.common.service.BaseServiceImpl;
import org.springframework.stereotype.Service;
import java.util.Objects;
import static com.zhush.admin.entity.table.SysWhiteListTableDef.SYS_WHITE_LIST;
/**
* @ClassName SysWhiteListServiceImpl
*/
@Service
public class SysWhiteListServiceImpl extends BaseServiceImpl
implements ISysWhiteListService {
@Override
public boolean getWhiteListByIp(String ip) {
QueryWrapper wrapper = QueryWrapper.create().where(SYS_WHITE_LIST.IP.eq(ip));
SysWhiteList sysWhiteList = this.mapper.selectOneByQuery(wrapper);
if (sysWhiteList != null && Objects.equals(sysWhiteList.getIsWhite(), 1)) {
return true;
} else {
return false;
}
}
}Custom interceptor that checks the IP and returns an error response when the IP is not whitelisted:
package com.zhush.admin.handler;
import com.zhush.admin.service.ISysWhiteListService;
import com.zhush.common.enums.ResultCodeEnum;
import com.zhush.common.utils.IpUtils;
import com.zhush.common.utils.JacksonUtils;
import com.zhush.common.utils.Result;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import java.io.IOException;
import java.io.PrintWriter;
/**
* @ClassName WhiteListHandlerInterceptor
*/
public class WhiteListHandlerInterceptor implements HandlerInterceptor {
@Resource
private ISysWhiteListService whiteListService;
@Value("${zhush.white.enable}")
private boolean enable;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (!enable) {
return true;
}
String ipAddr = IpUtils.getIpAddr(request);
boolean whiteListByIp = whiteListService.getWhiteListByIp(ipAddr);
if (whiteListByIp) {
return true;
} else {
result(response, JacksonUtils.toJSONString(Result.build("IP不在白名单内", ResultCodeEnum.ERROR)));
return false;
}
}
private void result(HttpServletResponse response, String result) {
PrintWriter writer = null;
response.setCharacterEncoding("UTF-8");
response.setContentType("text/html; charset=utf-8");
try {
writer = response.getWriter();
writer.println(result);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (writer != null) {
writer.close();
}
}
}
}Utility class for extracting the real client IP from the request headers:
package com.zhush.common.utils;
import jakarta.servlet.http.HttpServletRequest;
public class IpUtils {
private static final String UNKNOWN = "unknown";
public static String getIpAddr(HttpServletRequest request) {
if (request == null) {
return UNKNOWN;
}
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.getHeader("HTTP_X_FORWARDED_FOR");
}
if (ip == null || ip.isEmpty() || UNKNOWN.equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
String[] ips = ip.split(",");
return ips[0].trim();
}
}The interceptor is registered in a custom WebMvcConfigurer implementation so that it intercepts all incoming requests:
package com.zhush.admin.config;
import cn.dev33.satoken.interceptor.SaInterceptor;
import cn.dev33.satoken.router.SaRouter;
import cn.dev33.satoken.stp.StpUtil;
import com.zhush.admin.handler.WhiteListHandlerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class ZhushWebMvcConfig implements WebMvcConfigurer {
@Bean
public WhiteListHandlerInterceptor whiteListHandlerInterceptor() {
return new WhiteListHandlerInterceptor();
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(whiteListHandlerInterceptor());
}
}By moving the whitelist logic to an interceptor backed by a database table, the system no longer needs to restart Nginx or redeploy the application when the allowed IP list changes; adding a row to the table is sufficient.
Java Architect Essentials
Committed to sharing quality articles and tutorials to help Java programmers progress from junior to mid-level to senior architect. We curate high-quality learning resources, interview questions, videos, and projects from across the internet to help you systematically improve your Java architecture skills. Follow and reply '1024' to get Java programming resources. Learn together, grow together.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.