Если вы используете struts2 и spring вместе, вам следует проверить функцию Spring Security по ограничению попыток пользователей. Если попытка пользователя не удалась 3 раза, пользователь должен быть заблокирован и не иметь доступа к странице, а если попыток меньше 3, мы должны сбросить счетчик. Также токен csrf следует использовать по-разному для каждой попытки входа в систему.
Весенняя безопасность
Взгляните на эту реализацию.
Основные файлы: 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 ();
}
}
Вы также можете использовать Recaptcha после 3 неудачных попыток или сбросить пароль.
С точки зрения безопасности вы должны сделать немного больше. Например, используйте кеш для хранения IP-адресов и попыток входа в систему и заблокируйте IP-адрес, если они израсходовали все попытки входа в систему. С помощью Spring и Guavas кеш с автоматическим истечением срока действия легко реализовать с помощью expireAfterWrite(10, TimeUnit.MINUTES)
.
Если вы хотите хранить/кешировать только IP-адрес и считать его парой значений ключа, Spring Radis также хорошая альтернатива в Spring Framework.
17.06.2016
expireAfterWrite(10, TimeUnit.MINUTES)
. Но используете ли вы ehcache? 22.06.2016CacheBuilder.removalListener(RemovalListener)
.RemovalListener
передаетсяRemovalNotification
, который указываетRemovalCause
, ключ и значение. Для целей регистрации исключения на этой странице есть пример. 22.06.2016