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 java.util.Collection;
|
||||||
|
|
||||||
|
import javax.persistence.Column;
|
||||||
import javax.persistence.Entity;
|
import javax.persistence.Entity;
|
||||||
import javax.persistence.GeneratedValue;
|
import javax.persistence.GeneratedValue;
|
||||||
import javax.persistence.GenerationType;
|
import javax.persistence.GenerationType;
|
||||||
@ -23,6 +24,7 @@ public class User {
|
|||||||
|
|
||||||
private String email;
|
private String email;
|
||||||
|
|
||||||
|
@Column(length = 60)
|
||||||
private String password;
|
private String password;
|
||||||
|
|
||||||
private boolean enabled;
|
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.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
import org.baeldung.persistence.dao.RoleRepository;
|
import org.baeldung.persistence.dao.RoleRepository;
|
||||||
import org.baeldung.persistence.dao.UserRepository;
|
import org.baeldung.persistence.dao.UserRepository;
|
||||||
import org.baeldung.persistence.model.Privilege;
|
import org.baeldung.persistence.model.Privilege;
|
||||||
@ -34,6 +36,12 @@ public class MyUserDetailsService implements UserDetailsService {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private RoleRepository roleRepository;
|
private RoleRepository roleRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private LoginAttemptService loginAttemptService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private HttpServletRequest request;
|
||||||
|
|
||||||
public MyUserDetailsService() {
|
public MyUserDetailsService() {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
@ -42,6 +50,11 @@ public class MyUserDetailsService implements UserDetailsService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public UserDetails loadUserByUsername(final String email) throws UsernameNotFoundException {
|
public UserDetails loadUserByUsername(final String email) throws UsernameNotFoundException {
|
||||||
|
String ip = request.getRemoteAddr();
|
||||||
|
if (loginAttemptService.isBlocked(ip)) {
|
||||||
|
throw new RuntimeException("blocked");
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final User user = userRepository.findByEmail(email);
|
final User user = userRepository.findByEmail(email);
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
|
@ -59,3 +59,4 @@ message.forgetPassword=Forget Password
|
|||||||
message.resetPassword=Reset Password
|
message.resetPassword=Reset Password
|
||||||
message.updatePassword=Update 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
|
@ -59,3 +59,4 @@ message.forgetPassword=Olvide la contrase
|
|||||||
message.resetPassword=Restablecer contraseña
|
message.resetPassword=Restablecer contraseña
|
||||||
message.updatePassword=Actualizar 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>
|
<spring:message code="auth.message.expired"></spring:message>
|
||||||
</div>
|
</div>
|
||||||
</c:when>
|
</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>
|
<c:otherwise>
|
||||||
<div class="alert alert-error">
|
<div class="alert alert-error">
|
||||||
<!-- <c:out value="${SPRING_SECURITY_LAST_EXCEPTION.message}"/> -->
|
<!-- <c:out value="${SPRING_SECURITY_LAST_EXCEPTION.message}"/> -->
|
||||||
|
@ -17,6 +17,10 @@
|
|||||||
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
|
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
|
||||||
</listener>
|
</listener>
|
||||||
|
|
||||||
|
<listener>
|
||||||
|
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
|
||||||
|
</listener>
|
||||||
|
|
||||||
<servlet>
|
<servlet>
|
||||||
<servlet-name>mvc</servlet-name>
|
<servlet-name>mvc</servlet-name>
|
||||||
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
|
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user