---
# System prepended metadata

title: Spring Security 的防止暴力破解身份驗證
tags: [Spring, security, java, brutal, force]

---

# Spring Security 的防止暴力破解身份驗證
###### tags: `Spring` `security` `java` `force` `brutal`
## `/pom.xml`
```
		<!-- https://github.com/google/guava -->
		<dependency>
			<groupId>com.google.guava</groupId>
			<artifactId>guava</artifactId>
			<version>28.1-jre</version>
		</dependency>
```
>Guava is a set of core Java libraries from Google that includes new collection types (such as multimap and multiset), immutable collections, a graph library, and utilities for concurrency, I/O, hashing, caching, primitives, strings, and more! It is widely used on most Java projects within Google, and widely used by many other companies as well.
>https://github.com/google/guava
## 匯入監聽器
```
@SpringBootApplication
public class DemoApplication {

	public static void main(String[] args) {
		SpringApplication.run(DemoApplication.class, args);
	}
    
	/**
	 * 加入監聽器
	 */
	@Bean
	public RequestContextListener requestContextListener() {
		return new RequestContextListener();
	}
}
```
## 建立監聽器
### 嘗試登入的服務層
```
@Service
public class LoginAttemptService {

	private final int MAX_ATTEMPT = 5; //設定最大嘗試次數
	private LoadingCache<String, Integer> attemptsCache;

	//建構方法
	public LoginAttemptService() {
		attemptsCache = CacheBuilder.newBuilder().expireAfterWrite(
			60, // 失敗後可重新嘗試的時間
			TimeUnit.SECONDS // 時間單位
		).build(new CacheLoader<String, Integer>() {
			public Integer load(String key) {
				return 0;
			}
		});
	}

	/**
	 * 登入成功，將快取清除
	 * @param key 用戶 IP
	 */
	public void loginSucceeded(String key) {
		attemptsCache.invalidate(key); //清除快取
	}

	/**
	 * 登入失敗，累積嘗試次數
	 * @param key 用戶 IP
	 */
	public void loginFailed(String key) {
		int attempts = 0;
		try {
			attempts = attemptsCache.get(key);//取得快取 IP 的次數
		} catch (ExecutionException e) {
			attempts = 0;//找不到快取
		}
		attempts++;
		attemptsCache.put(key, attempts);//加入快取
		
	}

	/**
	 * 檢查是否超過嘗試次數
	 * @param key 用戶 IP
	 * @return 
	 */
	public boolean isBlocked(String key) {
		try {
			return attemptsCache.get(key) >= MAX_ATTEMPT;
		} catch (ExecutionException e) {
			return false;
		}
	}
}
```
### 發生登入失敗的動作時，執行
```
@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());
	}
}
```
### 發生登入成功的動作時，執行
```
@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());
	}
}
```
## 相關筆記
* [Spring Security](https://hackmd.io/@LeeLo/HJvoNobgL)
* [Domain Object Security (ACLs) in Spring](https://hackmd.io/@LeeLo/HJD8s_NUH)
* [Spring Boot Redis](https://hackmd.io/@LeeLo/HJBTC6Xb8)
* [Internationalization](https://hackmd.io/@LeeLo/By-z7tSZL)