我们常常会遇到需要修改配置文件,却不得不重启应用才能使配置生效的问题。
使用spring可以自动感知变化,但我们自己写的配置读取却需要通过jdk7提供的库来实现。
说白了,就是需要在适当的位置开启一个监听线程,使用WatchService去监听变化,处理变化。
例如我们在拦截器中读取了当前项目的config/Application.properties配置文件,把配置中的ip白名单读取到成员变量中,在配置文件改变后才会去刷新该成员变量。
配置
#Only allows auth ip visit
auth.ip[0]=127.0.0.1
auth.ip[1]=192.168.10.254
auth.ip[2]=192.168.0.170
拦截器写法
private List<String> ipList;
/**
*这里构造函数启动线程
*/
public AuthInInterceptor(List<String> ipList) {
new Thread(new Runnable() {
@Override
public void run() {
propertiesListener();
}
}).start();
}
/**
* 配置文件监听,只监听外部配置
*/
public void propertiesListener(){
try {
FileSystem fileSystem = FileSystems.getDefault(); //启动默认文件系统
WatchService watcher = fileSystem.newWatchService();//获取到监听服务
String dir = System.getProperty("user.dir");//读取当前应用目录
String p = dir + "\\config";
if(!new File(p).exists()){
p = dir;
}
if(!new File(p).exists()){//指定配置文件目录不存在直接返回
return;
}
Path myDir = fileSystem.getPath(p); //要监听的文件目录
myDir.register(watcher,StandardWatchEventKinds.ENTRY_MODIFY); //对该目录的修改进行注册监听
WatchKey watchKey = null;
while (true) {
watchKey = watcher.take();
for (WatchEvent<?> event : watchKey.pollEvents()) {
if (event.kind() == StandardWatchEventKinds.ENTRY_MODIFY) { //监听事件是修改
List<String> ips = null;
if("Application.properties".equalsIgnoreCase(event.context().toString())){ //修改的文件是指定的配置文件
//读取文件配置
InputStream in = new FileInputStream(p +"\\" + event.context().toString());
Properties properties = new Properties();
properties.load(in);
ips = new ArrayList<String>();
for (Object key : properties.keySet()) {
String strkey = key.toString();
String val = properties.getProperty(strkey);
if(strkey.startsWith("auth.ip") && !StringUtils.isEmpty(val)){
ips.add(val);
}
}
}
//读取到了新的配置,就替换到成员变量
if(!CollectionUtils.isEmpty(ips)){
ipList.clear();
ipList.addAll(ips);
}
}
}
//执行过一次就得把监控池重置,才能再次监控
boolean reset = watchKey.reset();
if (!reset)
break;
}
} catch (IOException e) {
logger.error("load properties error", e);
} catch (InterruptedException e) {
logger.error("watch properties events error", e);
}
}
如何判断请求ip是否在白名单里面呢?
boolean flag = false;
String ipAddress = getUsrIPAddr(request);// 获取客户端的IP地址
for (String ipString : ipList) {
if (ipAddress.equals(ipString)) {
flag = true;
break;
}
}
为true则允许访问
/**
* 获取客户端ip地址
* @param request
* @return
*/
public String getUsrIPAddr(HttpServletRequest request) {
String ip = "";
//1.首先考虑有反向代理的情况,如果有代理,通过“x-forwarded-for”获取真实ip地址
ip = request.getHeader("x-forwarded-for");
//2.如果squid.conf的配制文件forwarded_for项默认是off,则:X-Forwarded-For:unknown。考虑用Proxy-Client-IP或WL-Proxy-Client-IP获取
if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
//3.最后考虑没有代理的情况,直接用request.getRemoteAddr()获取ip
if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
//4.如果通过多级反向代理,则可能产生多个ip,其中第一个非unknown的IP为客户端真实IP(IP按照','分割)
if(ip != null && ip.split(",").length > 1){
ip = (ip.split(","))[0];
}
//5.如果是服务器本地访问,需要根据网卡获取本机真实ip
if("127.0.0.1".equals(ip)) {
try {
ip = InetAddress.getLocalHost().getHostAddress();
} catch (UnknownHostException e) {
logger.error(e.getMessage(),e);//获取服务器(本地)ip信息失败
return "";
}
}
// 6.校验ip的合法性,不合法返回""
if(!isValidIp(ip)) {
return "The ip is invalid.";
}else {
return ip;
}
}
/**
* 判断是否为合法IP地址
* @param ipAddress
* @return
*/
private boolean isValidIp(String ipAddress) {
boolean retVal = false;
try {
if(ipAddress!=null && !"".equals(ipAddress)){
Pattern pattern = Pattern.compile("([1-9]|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])(\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])){3}");
retVal = pattern.matcher(ipAddress).matches();
}
} catch(Exception e){
logger.error(e.getMessage(), e);
}
return retVal;
}