一简介
在Java Web应用程中特别是网站开发中我们有时候需要为应用程序增加一个入侵检测程序来防止恶意刷新的功能防止非法用户不断的往Web应用中重复发送数据当然入侵检测可以用很多方法实现包括软件硬件防火墙入侵检测的策略也很多在这里我们主要介绍的是Java Web应用程序中通过软件的方式实现简单的入侵检测及防御
该方法的实现原理很简单就是用户访问Web系统时记录每个用户的信息然后进行对照并根据设定的策略(比如秒钟刷新页面次)判断用户是否属于恶意刷新
我们的入侵检测程序应该放到所有Java Web程序的执行前也即若发现用户是恶意刷新就不再继续执行Java Web中的其它部分内容否则就会失去了意义这就需要以插件的方式把入侵检测的程序置入Java Web应用中使得每次用户访问Java Web都先要到这个入侵检测程序中报一次到符合规则才能放行
Java Web应用大致分为两种一种纯JSP(+Java Bean)方式一种是基于框架(如StrutsEasyJWeb等)的第一种方式的Java Web可以通过Java Servlet中的Filter接口实现也即实现一个Filter接口在其doFilter方法中插入入侵检测程序然后再webxml中作简单的配置即可在基于框架的Web应用中由于所有应用都有一个入口因此可以把入侵检测的程序直接插入框架入口引擎中使框架本身支持入侵检测功能当然也可以通过实现Filter接口来实现
在EasyJWeb框架中已经置入了简单入侵检测的程序因此这里我们以EasyJWeb框架为例介绍具体的实现方法及源码完整的代码可以在EasyJWeb源码中找到
在基于EasyJWeb的Java Web应用中默认情况下你只要连续刷新页面次数过多即会弹出如下的错误
EasyJWeb框架友情提示!)
您对页面的刷新太快请等待秒后再刷新页面!
二用户访问信息记录UserConnectjava类
这个类是一个简单的Java Bean主要代表用户的信息包括用户名IP第一次访问时间最后登录时间登录次数用户状态等全部
代码如下
package comeasyjfweb;
import javautilDate;
/**
*
*
Title:用户验证信息
*
Description:记录用户登录信息判断用户登录情况
*
Copyright: Copyright (c)
*
Company:
* @author 蔡世友
* @version
*/
public class UserConnect {
private String userName;
private String ip;
private Date firstFailureTime;
private Date lastLoginTime;
private int failureTimes;//用户登录失败次数
private int status=;//用户状态表示正常表示锁定
public int getFailureTimes() {
return failureTimes;
}
public void setFailureTimes(int failureTimes) {
thisfailureTimes = failureTimes;
}
public Date getFirstFailureTime() {
return firstFailureTime;
}
public void setFirstFailureTime(Date firstFailureTime) {
thisfirstFailureTime = firstFailureTime;
}
public String getIp() {
return ip;
}
public void setIp(String ip) {
thisip = ip;
}
public Date getLastLoginTime() {
return lastLoginTime;
}
public void setLastLoginTime(Date lastLoginTime) {
thislastLoginTime = lastLoginTime;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
thisuserName = userName;
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
thisstatus = status;
}
}
三监控线程UserConnectManagejava类
这是入侵检测的核心部分主要实现具体的入侵检测记录判断用户信息在线用户的刷新等功能并提供其它应用程序使用本组件的调用接口
package comeasyjfweb;
import javautilDate;
import javautilHashMap;
import javautilHashSet;
import javautilIterator;
import javautilMap;
import javautilSet;
import orgapachelogjLogger;
/**
*
*
Title:用户入侵检测信息
*
Description:用于判断用户刷新情况检查默认为秒钟之内连续连接次为超时
*
Copyright: Copyright (c)
*
Company:
* @author 蔡世友
* @version
*/
public class UserConnectManage {
private static final Logger logger = (Logger) LoggergetLogger(UserConnectManageclassgetName())
private static int maxFailureTimes=;//最大登录失败次数
private static long maxFailureInterval=;//毫秒达到最大登录次数且在这个时间范围内
private static long waitInterval=;//失败后接受连接的等待时间默认分钟
private static int maxOnlineUser=;//同时在线的最大数
private final static Map users=new HashMap()//使用ip+userName为key存放用户登录信息UserLoginAuth
private static Thread checkThread=null;
private static class CheckTimeOut implements Runnable{
private Thread parentThread;
public CheckTimeOut(Thread parentThread)
{
thisparentThread=parentThread;
synchronized(this){
if(checkThread==null){
checkThread= new Thread(this)
//Systemoutprintln(创建一个新线程!)
checkThreadstart()
}
}
}
public void run() {
while(true)
{
if(parentThreadisAlive()){
try{
Threadsleep()
int i=;
if(userssize()>maxOnlineUser)//当达到最大用户数时清除
{
synchronized(users){//执行删除操作
Iterator it=userskeySet()iterator()
Set set=new HashSet()
Date now=new Date()
while(ithasNext())
{
Object key=itnext()
UserConnect user=(UserConnect)usersget(key)
if(nowgetTime()usergetFirstFailureTime()getTime()>maxFailureInterval)//删除超时的用户
{
setadd(key)
(删除了一个超时的连接+i)
i++;
}
}
if(i<)//如果删除少于个则强行删除/在线记录牺牲性能的情况下保证内存
{
int num=maxOnlineUser/;
it=userskeySet()iterator()
while(ithasNext() && i {
setadd(itnext())
(删除了一个多余的连接+i)
i++;
}
}
userskeySet()removeAll(set)
}
}
}
catch(Exception e)
{
eprintStackTrace()
}
}
else
{
break;
}
}
(监视程序运行结束!)
}
}
//通过checkLoginValidate判断是否合法的登录连接如果合法则继续非法则执行
public static boolean checkLoginValidate(String ipString userName)//只检查认证失败次数
{
boolean ret=true;
Date now=new Date()
String key=ip+:+userName;
UserConnect auth=(UserConnect)usersget(key)
if(auth==null)//把用户当前的访问信息加入到users容器中
{
auth=new UserConnect()
authsetIp(ip)
authsetUserName(userName)
authsetFailureTimes()
authsetFirstFailureTime(now)
usersput(keyauth)
if(checkThread==null)new CheckTimeOut(ThreadcurrentThread())
}
else
{
if(authgetFailureTimes()>maxFailureTimes)
{
//如果在限定的时间间隔内则返回拒绝用户连接的信息
if((nowgetTime()authgetFirstFailureTime()getTime()) {
ret=false;
authsetStatus()
}
else if(authgetStatus()== && (nowgetTime()authgetFirstFailureTime()getTime()<(maxFailureInterval+waitInterval)))//重置计数器
{
ret=false;
}
else
{
authsetFailureTimes()
authsetFirstFailureTime(now)
authsetStatus()
}
}
//登录次数加
authsetFailureTimes(authgetFailureTimes()+)
}
//Systemoutprintln(key+:+authgetFailureTimes()+:+ret+:+(nowgetTime()authgetFirstFailureTime()getTime()))
return ret;
}
public static void reset(String ipString userName)//重置用户信息
{
Date now=new Date()
String key=ip+:+userName;
UserConnect auth=(UserConnect)usersget(key)
if(auth==null)//把用户当前的访问信息加入到users容器中
{
auth=new UserConnect()
authsetIp(ip)
authsetUserName(userName)
authsetFailureTimes()
authsetFirstFailureTime(now)
usersput(keyauth)
}
else
{
authsetFailureTimes()
authsetFirstFailureTime(now)
}
}
public static void remove(String ipString userName)//删除用户在容器中的记录
{
String key=ip+:+userName;
usersremove(key)
}
public static void clear()//清空容器中内容
{
if(!usersisEmpty())usersclear()
}
public static long getMaxFailureInterval() {
return maxFailureInterval;
}
public static void setMaxFailureInterval(long maxFailureInterval) {
UserConnectManagemaxFailureInterval = maxFailureInterval;
}
public static int getMaxFailureTimes() {
return maxFailureTimes;
}
public static void setMaxFailureTimes(int maxFailureTimes) {
UserConnectManagemaxFailureTimes = maxFailureTimes;
}
public static int getMaxOnlineUser() {
return maxOnlineUser;
}
public static void setMaxOnlineUser(int maxOnlineUser) {
UserConnectManagemaxOnlineUser = maxOnlineUser;
}
public static long getWaitInterval() {
return waitInterval;
}
public static void setWaitInterval(long waitInterval) {
UserConnectManagewaitInterval = waitInterval;
}
四调用接口
在需要进入侵检测判断的地方直接使用UserConnectManage类中的checkLoginValidate方法即可如EasyJWeb的核心Servlet
comeasyjfwebActionServlet中调用UserConnectManage的代码
if(!UserConnectManagecheckLoginValidate(requestgetRemoteAddr()guest))
{
info(requestresponsenew Exception(您对页面的刷新太快请等待+UserConnectManagegetWaitInterval()/+秒
后再刷新页面!))
return;
}
五总结
当然这里提供的方法只是一个简单的实现示例由于上面的用户信息是直接保存在内存中若并发用户很大的时候的代码的占用可以考虑引入数据库来记录用户的访问信息当然相应的执行效率肯定用降低上面介绍的实现中入侵检测判断的策略也只有用户访问次数及时间间隔两个元素您还可以根据你的实现情况增加其它的检测元素