java - 如何阻止或限制对一个操作的多次调用 Struts(抓取器)

标签 java spring struts2

我有一个 Struts2/Spring 应用程序,我想知道如何阻止“抓取者”。

在我的服务器上,我检测到某些抓取器多次调用相同的操作struts,结果是我的服务器在抓取过程中速度太慢并且数据库有多次命中。

如何停止对同一操作 struts 的多次调用并最大限度地减少数据库命中?

例如,抓取器每分钟调用相同操作超过 40 次。

<小时/>

从安全角度来看,我认为我应该使用缓存来存储 IP 地址和电话号码,并在超过限制时阻止 IP。

但我不知道该怎么做。

如果您已经做了同样的事情,请告诉我如何实现解决方案?

最佳答案

如果您同时使用 struts2 和 spring,您应该检查 Spring Security 限制用户尝试的功能。如果用户尝试失败 3 次,则应阻止用户并且无法访问页面,如果尝试次数少于 3 次,我们应重置计数器。此外,csrf token 应以不同的方式用于每次登录尝试。

Spring 安全

看看this实现。

主要文件是LimitLoginAuthenticationProvider.java

package com.mkyong.web.handler;

import java.util.Date;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Component;

import com.mkyong.users.dao.UserDetailsDao;
import com.mkyong.users.model.UserAttempts;

@Component("authenticationProvider")
public class LimitLoginAuthenticationProvider extends DaoAuthenticationProvider {

@Autowired
UserDetailsDao userDetailsDao;

@Autowired
@Qualifier("userDetailsService")
@Override
public void setUserDetailsService(UserDetailsService userDetailsService) {
    super.setUserDetailsService(userDetailsService);
}

@Override
public Authentication authenticate(Authentication authentication) 
      throws AuthenticationException {

  try {

    Authentication auth = super.authenticate(authentication);

    //if reach here, means login success, else an exception will be thrown
    //reset the user_attempts
    userDetailsDao.resetFailAttempts(authentication.getName());

    return auth;

  } catch (BadCredentialsException e) { 

    //invalid login, update to user_attempts
    userDetailsDao.updateFailAttempts(authentication.getName());
    throw e;

  } catch (LockedException e){

    //this user is locked!
    String error = "";
    UserAttempts userAttempts = 
                userDetailsDao.getUserAttempts(authentication.getName());

           if(userAttempts!=null){
        Date lastAttempts = userAttempts.getLastModified();
        error = "User account is locked! <br><br>Username : " 
                       + authentication.getName() + "<br>Last Attempts : " + lastAttempts;
    }else{
        error = e.getMessage();
    }

  throw new LockedException(error);
}

}

}

Struts2

同样可以通过在struts2中实现拦截器来完成。

public class MyAction implements SessionAware {
private Map<String, Object> session;

@Override
public String execute() {
    if (session.containsKey("loginAttempts")) {
        Integer loginAttempts = (Integer) session.get("loginAttempts");
        if (loginAttempts > 3) {
            //block user
        } else {
            session.put("loginAttempts", loginAttempts+1);
        }
    }
}

@Override
public void setSession(Map<String, Object> session) {
    this.session = session;
}
}

同样使用拦截器

public String intercept (ActionInvocation invocation) throws Exception {
// Get the action context from the invocation so we can access the
// HttpServletRequest and HttpSession objects.
final ActionContext context = invocation.getInvocationContext ();
HttpServletRequest request = (HttpServletRequest) context.get(HTTP_REQUEST);
HttpSession session =  request.getSession (true);

// Is there a "user" object stored in the user's HttpSession?
Object user = session.getAttribute (USER_HANDLE);
if (user == null) {
    // The user has not logged in yet.

    // Is the user attempting to log in right now?
    String loginAttempt = request.getParameter (LOGIN_ATTEMPT);
    if (! StringUtils.isBlank (loginAttempt) ) { // The user is attempting to log in.

        // Process the user's login attempt.
        if (processLoginAttempt (request, session) ) {
            // The login succeeded send them the login-success page.
            return "login-success";
        } else {
            // The login failed. Set an error if we can on the action.
            Object action = invocation.getAction ();
            if (action instanceof ValidationAware) {
                ((ValidationAware) action).addActionError ("Username or password incorrect.");
            }
        }
    }

    // Either the login attempt failed or the user hasn't tried to login yet, 
    // and we need to send the login form.
    return "login";
} else {
    return invocation.invoke ();
}
}

您还可以在尝试 3 次失败后使用 Recaptcha 或重置密码。

从安全角度来看,您必须做更多的事情。例如,使用缓存来存储 IP 地址和登录尝试,并在用尽所有登录尝试时阻止该 IP。随着 Spring 和Guavas自动过期缓存很容易使用expireAfterWrite(10, TimeUnit.MINUTES)实现。

如果您只想存储/缓存 ip 地址并计为键值对 Spring Radis在 Spring 框架中也是一个不错的选择。

关于java - 如何阻止或限制对一个操作的多次调用 Struts(抓取器),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37821440/

相关文章:

java - 如何捕获 "NotesException: Notes error: Remote system no longer responding"并重试?

java - 查找大小,即存储在 Java 数组中的非空元素的数量

spring - JHipster:CORS 在表单登录时失败(实际请求,不是飞行前)

Spring MVC 静态资源映射

java - 设置包的默认结果类型

java - 我怎样才能使用 struts 2 为大文件上传文件?

java - Spring状态机嵌套机器同步运行

java - 提取字符串中的相邻单词以帮助提高命名实体识别器的准确性

java - 不调用 Spring REST API Controller

java - 如何使用struts 2标签检查jsp中的list.contains