Try   HackMD

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());
	}
}

相關筆記