prevent brute force authentications
This commit is contained in:
parent
54bf8b0e1c
commit
aaabca5d2a
|
@ -2,6 +2,7 @@ package org.baeldung.persistence.model;
|
|||
|
||||
import java.util.Collection;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
|
@ -23,6 +24,7 @@ public class User {
|
|||
|
||||
private String email;
|
||||
|
||||
@Column(length = 60)
|
||||
private String password;
|
||||
|
||||
private boolean enabled;
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
package org.baeldung.security;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.security.authentication.event.AuthenticationFailureBadCredentialsEvent;
|
||||
import org.springframework.security.web.authentication.WebAuthenticationDetails;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class AuthenticationFailureListener implements ApplicationListener<AuthenticationFailureBadCredentialsEvent> {
|
||||
|
||||
@Autowired
|
||||
private LoginAttemptService loginAttemptService;
|
||||
|
||||
public void onApplicationEvent(AuthenticationFailureBadCredentialsEvent e) {
|
||||
WebAuthenticationDetails auth = (WebAuthenticationDetails) e.getAuthentication().getDetails();
|
||||
loginAttemptService.loginFailed(auth.getRemoteAddress());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package org.baeldung.security;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.security.authentication.event.AuthenticationSuccessEvent;
|
||||
import org.springframework.security.web.authentication.WebAuthenticationDetails;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class AuthenticationSuccessEventListener implements ApplicationListener<AuthenticationSuccessEvent> {
|
||||
|
||||
@Autowired
|
||||
private LoginAttemptService loginAttemptService;
|
||||
|
||||
public void onApplicationEvent(AuthenticationSuccessEvent e) {
|
||||
WebAuthenticationDetails auth = (WebAuthenticationDetails) e.getAuthentication().getDetails();
|
||||
loginAttemptService.loginSucceeded(auth.getRemoteAddress());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
package org.baeldung.security;
|
||||
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
|
||||
@Service
|
||||
public class LoginAttemptService {
|
||||
|
||||
private final int MAX_ATTEMPT = 10;
|
||||
private LoadingCache<String, Integer> attemptsCache;
|
||||
|
||||
public LoginAttemptService() {
|
||||
super();
|
||||
attemptsCache = CacheBuilder.newBuilder().expireAfterWrite(1, TimeUnit.DAYS).build(new CacheLoader<String, Integer>() {
|
||||
public Integer load(String key) {
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void loginSucceeded(String key) {
|
||||
attemptsCache.invalidate(key);
|
||||
}
|
||||
|
||||
public void loginFailed(String key) {
|
||||
int attempts = 0;
|
||||
try {
|
||||
attempts = attemptsCache.get(key);
|
||||
} catch (ExecutionException e) {
|
||||
attempts = 0;
|
||||
}
|
||||
attempts++;
|
||||
attemptsCache.put(key, attempts);
|
||||
}
|
||||
|
||||
public boolean isBlocked(String key) {
|
||||
try {
|
||||
return attemptsCache.get(key) >= MAX_ATTEMPT;
|
||||
} catch (ExecutionException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,6 +5,8 @@ import java.util.Arrays;
|
|||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.baeldung.persistence.dao.RoleRepository;
|
||||
import org.baeldung.persistence.dao.UserRepository;
|
||||
import org.baeldung.persistence.model.Privilege;
|
||||
|
@ -34,6 +36,12 @@ public class MyUserDetailsService implements UserDetailsService {
|
|||
@Autowired
|
||||
private RoleRepository roleRepository;
|
||||
|
||||
@Autowired
|
||||
private LoginAttemptService loginAttemptService;
|
||||
|
||||
@Autowired
|
||||
private HttpServletRequest request;
|
||||
|
||||
public MyUserDetailsService() {
|
||||
super();
|
||||
}
|
||||
|
@ -42,6 +50,11 @@ public class MyUserDetailsService implements UserDetailsService {
|
|||
|
||||
@Override
|
||||
public UserDetails loadUserByUsername(final String email) throws UsernameNotFoundException {
|
||||
String ip = request.getRemoteAddr();
|
||||
if (loginAttemptService.isBlocked(ip)) {
|
||||
throw new RuntimeException("blocked");
|
||||
}
|
||||
|
||||
try {
|
||||
final User user = userRepository.findByEmail(email);
|
||||
if (user == null) {
|
||||
|
|
|
@ -58,4 +58,5 @@ message.resendToken=We will send you a message with a new registration token to
|
|||
message.forgetPassword=Forget Password
|
||||
message.resetPassword=Reset Password
|
||||
message.updatePassword=Update Password
|
||||
message.userNotFound=User Not Found
|
||||
message.userNotFound=User Not Found
|
||||
auth.message.blocked=This ip is blocked for 24 hours
|
|
@ -58,4 +58,5 @@ message.resendToken=Le enviaremos un mensaje con un nuevo token de registro en s
|
|||
message.forgetPassword=Olvide la contraseña
|
||||
message.resetPassword=Restablecer contraseña
|
||||
message.updatePassword=Actualizar contraseña
|
||||
message.userNotFound=Usuario no encontrado
|
||||
message.userNotFound=Usuario no encontrado
|
||||
auth.message.blocked=Esta IP se bloquea durante 24 horas
|
|
@ -21,6 +21,12 @@
|
|||
<spring:message code="auth.message.expired"></spring:message>
|
||||
</div>
|
||||
</c:when>
|
||||
<c:when
|
||||
test="${SPRING_SECURITY_LAST_EXCEPTION.message == 'blocked'}">
|
||||
<div class="alert alert-error">
|
||||
<spring:message code="auth.message.blocked"></spring:message>
|
||||
</div>
|
||||
</c:when>
|
||||
<c:otherwise>
|
||||
<div class="alert alert-error">
|
||||
<!-- <c:out value="${SPRING_SECURITY_LAST_EXCEPTION.message}"/> -->
|
||||
|
|
|
@ -16,7 +16,11 @@
|
|||
<listener>
|
||||
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
|
||||
</listener>
|
||||
|
||||
|
||||
<listener>
|
||||
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
|
||||
</listener>
|
||||
|
||||
<servlet>
|
||||
<servlet-name>mvc</servlet-name>
|
||||
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
|
||||
|
|
Loading…
Reference in New Issue