Post

[Spring Security] UsernamePasswordAuthenticationFilter

[Spring Security] UsernamePasswordAuthenticationFilter

๐Ÿ“Œ ๊ฐœ์š”

์‚ฌ์šฉ์ž๊ฐ€ ์ž๊ฒฉ ์ฆ๋ช…์„ ์ž…๋ ฅํ•˜๋ฉด ์–ด๋–ค ๊ณผ์ •์„ ๊ฑฐ์ณ ์‚ฌ์šฉ์ž๋ฅผ ์ธ์ฆํ•˜๊ฒŒ ๋ ๊นŒ? ๊ทธ ๊ณผ์ •์— ๋Œ€ํ•ด ์•Œ์•„๋ณด์ž.

๐Ÿ“Œ UsernamePasswordAuthenticationFilter์ด๋ž€?

UsernamePasswordAuthenticationFilter ๋Š” ๋ณด์•ˆ ํ•„ํ„ฐ ์ค‘ ํ•˜๋‚˜๋กœ POST ์š”์ฒญ์œผ๋กœ ๋“ค์–ด์˜จ ์ž๊ฒฉ ์ฆ๋ช…์„ ํ† ๋Œ€๋กœ ์ธ์ฆ์„ ์ˆ˜ํ–‰ํ•œ๋‹ค.

๐Ÿ“Œ ์ž๊ฒฉ ์ฆ๋ช… ์š”์ฒญ ์ „์†ก ~ ์ธ์ฆ

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
		throws IOException, ServletException {
	if (!requiresAuthentication(request, response)) {
		chain.doFilter(request, response);
		return;
	}
	try {
		Authentication authenticationResult = attemptAuthentication(request, response);
		
		// ...
	}
	catch (InternalAuthenticationServiceException failed) {
		this.logger.error("An internal error occurred while trying to authenticate the user.", failed);
		unsuccessfulAuthentication(request, response, failed);
	}
	catch (AuthenticationException ex) {
		// Authentication failed
		unsuccessfulAuthentication(request, response, ex);
	}
}

๋จผ์ € AbstractAuthenticationProcessingFilter ๊ฐ€ ์‚ฌ์šฉ์ž ๋กœ๊ทธ์ธ ์š”์ฒญ์„ ๋ฐ›๋Š”๋‹ค. requiresAuthentication ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด ํ•ด๋‹น ์š”์ฒญ์ด ์ž์‹ ์ด ์ฒ˜๋ฆฌํ•  ๋Œ€์ƒ์ธ์ง€ ํ™•์ธํ•œ๋‹ค. ์•„๋‹ˆ๋ผ๋ฉด ๋‹ค์Œ ์ฒด์ธ์— ๋™์ž‘์„ ๋„˜๊ธด๋‹ค.

์ž์‹ ์ด ์ฒ˜๋ฆฌํ•  ์š”์ฒญ์ด๋ผ๋ฉด try ๋ธ”๋ก ๋‚ด์—์„œ attemptAuthentication ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
		throws AuthenticationException {
	if (this.postOnly && !request.getMethod().equals("POST")) {
		throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
	}
	String username = obtainUsername(request);
	username = (username != null) ? username.trim() : "";
	String password = obtainPassword(request);
	password = (password != null) ? password : "";
	UsernamePasswordAuthenticationToken authRequest = UsernamePasswordAuthenticationToken.unauthenticated(username,
			password);
	// Allow subclasses to set the "details" property
	setDetails(request, authRequest);
	return this.getAuthenticationManager().authenticate(authRequest);
}

attemptAuthentication ๋ฉ”์„œ๋“œ๋Š” ์ถ”์ƒ ๋ฉ”์„œ๋“œ๋กœ, ๊ธฐ๋ณธ์ ์œผ๋กœ ์ž์‹ ํด๋ž˜์Šค์ธ UsernamePasswordAuthenticationFilter ์—์„œ ์˜ค๋ฒ„๋ผ์ด๋”ฉํ•˜์—ฌ ์ž‘์„ฑ๋˜์–ด ์žˆ๋‹ค.

1
2
3
if (this.postOnly && !request.getMethod().equals("POST")) {
	throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
}

UsernamePasswordAuthenticationFilter ๋Š” POST ์š”์ฒญ๋งŒ ์ฒ˜๋ฆฌํ•œ๋‹ค. ๋ฐ›์€ ์š”์ฒญ์ด POST ์ธ์ง€ ๋จผ์ € ํ™•์ธํ•œ๋‹ค.

1
2
3
4
String username = obtainUsername(request);
username = (username != null) ? username.trim() : "";
String password = obtainPassword(request);
password = (password != null) ? password : "";

์ดํ›„ HttpServletRequest ๊ฐ์ฒด์—์„œ ์•„์ด๋””์™€ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ๋ฌธ์ž์—ด๋กœ ์ถ”์ถœํ•œ๋‹ค.

1
2
UsernamePasswordAuthenticationToken authRequest = UsernamePasswordAuthenticationToken.unauthenticated(username,
		password);

์ถ”์ถœํ•œ ์•„์ด๋””์™€ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ํ† ๋Œ€๋กœ ์ธ์ฆ๋˜์ง€ ์•Š์€ ์ƒํƒœ์˜ Authentication ํ† ํฐ์„ ์ƒ์„ฑํ•œ๋‹ค.

1
setDetails(request, authRequest);

์ธ์ฆ ์š”์ฒญ์— ๋ถ€๊ฐ€์ ์ธ ์ปจํ…์ŠคํŠธ ์ •๋ณด๋ฅผ ๋‹ด๋Š”๋‹ค.

1
return this.getAuthenticationManager().authenticate(authRequest);

์ดํ›„ ์ธ์ฆ ์š”์ฒญ์„ AuthenticationManager ์—๊ฒŒ ์ „๋‹ฌํ•˜๊ณ  authenticate ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด ์ธ์ฆ์„ ์ง„ํ–‰ํ•œ๋‹ค.

์ธ์ฆ์— ์„ฑ๊ณตํ•˜๋ฉด ์ธ์ฆ์ด ์™„๋ฃŒ๋œ Authentication ๊ฐ์ฒด๊ฐ€ ๋ฆฌํ„ด๋˜๋ฉฐ, ์‹คํŒจ ์‹œ AuthenticationException ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
		throws IOException, ServletException {
	if (!requiresAuthentication(request, response)) {
		chain.doFilter(request, response);
		return;
	}
	try {
		Authentication authenticationResult = attemptAuthentication(request, response);
		if (authenticationResult == null) {
			// return immediately as subclass has indicated that it hasn't completed
			return;
		}
		this.sessionStrategy.onAuthentication(authenticationResult, request, response);
		// Authentication success
		if (this.continueChainBeforeSuccessfulAuthentication) {
			chain.doFilter(request, response);
		}
		successfulAuthentication(request, response, chain, authenticationResult);
	}
	catch (InternalAuthenticationServiceException failed) {
		this.logger.error("An internal error occurred while trying to authenticate the user.", failed);
		unsuccessfulAuthentication(request, response, failed);
	}
	catch (AuthenticationException ex) {
		// Authentication failed
		unsuccessfulAuthentication(request, response, ex);
	}
}

๋‹ค์‹œ ๋Œ์•„์™€์„œ ์ธ์ฆ์ด ์„ฑ๊ณต๋˜์—ˆ๋‹ค๋ฉด sessionStrategy.onAuthentication ์„ ํ†ตํ•ด ๊ธฐ์กด ์„ธ์…˜์„ ๋ฌดํšจํ™”ํ•˜๊ณ  ์ƒˆ๋กœ์šด ์„ธ์…˜์„ ๋ฐœ๊ธ‰ํ•œ๋‹ค. ์ดํ›„ successfulAuthentication ๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ์ธ์ฆ๋œ ๊ฐ์ฒด๋ฅผ SecurityContextHolder ์— ์ €์žฅํ•˜๊ณ  successHandler๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค.

์‹คํŒจํ•˜์˜€๋‹ค๋ฉด unsuccessfulAuthentication ๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ SecurityContextHolder ๋ฅผ ์ดˆ๊ธฐํ™”ํ•˜๊ณ  failureHandler ๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค.

๐Ÿ“Œ ์ธ์ฆ ํ›„

image.png

์ด๋ฒˆ์—๋Š” ํ”Œ๋กœ์šฐ๋ฅผ ๋ณด๊ณ  ๋‹ค์‹œ ํ•œ ๋ฒˆ ์ธ์ฆ ๊ณผ์ •์— ๋Œ€ํ•ด ์‚ดํŽด๋ณด์ž. ์ธ์ฆ ์„ฑ๊ณต ๋˜๋Š” ์‹คํŒจ ํ›„ ๊ทธ ๋‹ค์Œ ๊ณผ์ •์— ๋” ์ดˆ์ ์„ ๋งž์ถ”์–ด๋ณด์ž.

์‚ฌ์šฉ์ž๊ฐ€ ์ž๊ฒฉ ์ฆ๋ช…์„ ์ž…๋ ฅํ•˜๊ณ  ์„œ๋ฒ„์— ์š”์ฒญ์„ ๋ณด๋‚ด๋ฉด SecurityFilterChain ๋‚ด์˜ AbstractAuthenticationProcessingFilter ๊ฐ€ ์ด๋ฅผ ๊ฐ€๋กœ์ฑˆ๋‹ค. ์ดํ›„ Authentication ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์ธ์ฆ ๊ณผ์ •์„ AuthenticationManager ์—๊ฒŒ ์œ„์ž„ํ•œ๋‹ค.

์ธ์ฆ ์„ฑ๊ณต

๋งŒ์•ฝ ์‚ฌ์šฉ์ž์˜ ์ž๊ฒฉ ์ฆ๋ช…์ด ์œ ํšจํ•˜๋‹ค๊ณ  ํŒ๋‹จํ•˜๋ฉด ์œ ํšจํ•œ Authentication ๊ฐ์ฒด๋ฅผ ๋ฆฌํ„ดํ•œ๋‹ค.

1
2
3
4
5
6
public interface SessionAuthenticationStrategy {

	void onAuthentication(Authentication authentication, HttpServletRequest request, HttpServletResponse response)
			throws SessionAuthenticationException;

}

์ดํ›„ SessionAuthenticationStrategy ์—์„œ ๊ธฐ์กด ์„ธ์…˜์„ ๋ฌดํšจํ™”ํ•˜๊ณ  ์ƒˆ๋กœ์šด ์„ธ์…˜์„ ๋ฐœ๊ธ‰ํ•˜๋Š” ๋™์ž‘์„ ์ˆ˜ํ–‰ํ•œ๋‹ค.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
		Authentication authResult) throws IOException, ServletException {
	SecurityContext context = this.securityContextHolderStrategy.createEmptyContext();
	context.setAuthentication(authResult);
	this.securityContextHolderStrategy.setContext(context);
	this.securityContextRepository.saveContext(context, request, response);
	
	// ...
	
	rememberMeServices.loginSuccess(request, response, authResult);
	if (this.eventPublisher != null) {
		this.eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass()));
	}
	this.successHandler.onAuthenticationSuccess(request, response, authResult);
}

๊ทธ ๋‹ค์Œ ์ธ์ฆ์ด ์™„๋ฃŒ๋œ Authentication ๊ฐ์ฒด๋ฅผ SecurityContextHolder ์— ์ €์žฅํ•œ๋‹ค. ์ด ์ˆœ๊ฐ„๋ถ€ํ„ฐ ํ•ด๋‹น ์‚ฌ์šฉ์ž ์ •๋ณด๋Š” ์„ธ์…˜์— ๋ณด๊ด€๋˜์–ด ๋‹ค๋ฅธ ๊ณณ์—์„œ ์ฐธ์กฐ๋  ์ˆ˜ ์žˆ๊ฒŒ ๋œ๋‹ค.

๋งŒ์•ฝ remember-me ๊ธฐ๋Šฅ์ด ํ™œ์„ฑํ™”๋˜์—ˆ๋‹ค๋ฉด rememberMeServices ์˜ loginSuccess ๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ remember-me ์ฟ ํ‚ค๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์ „์†กํ•œ๋‹ค.

์ดํ›„ ์ด๋ฒคํŠธ๋ฅผ ๋ฐœํ–‰ํ•˜์—ฌ ๋กœ๊ทธ์ธ ์„ฑ๊ณต ์—ฌ๋ถ€๋ฅผ ์ „๋‹ฌํ•œ๋‹ค.

๋งˆ์ง€๋ง‰์œผ๋กœ successHandler ์˜ onAuthenticationSuccess ๊ฐ€ ์ˆ˜ํ–‰๋œ๋‹ค.

์ธ์ฆ ์‹คํŒจ

1
2
3
4
5
6
7
8
9
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
		AuthenticationException failed) throws IOException, ServletException {
	this.securityContextHolderStrategy.clearContext();
	
	// ...
	
	this.rememberMeServices.loginFail(request, response);
	this.failureHandler.onAuthenticationFailure(request, response, failed);
}

์ธ์ฆ์ด ์‹คํŒจ๋˜์—ˆ๋‹ค๋ฉด SecurityContextHolder ๋ฅผ ์ดˆ๊ธฐํ™”ํ•˜๊ณ , ์œ ํšจํ•œ remember-me ์ฟ ํ‚ค๊ฐ€ ์žˆ์—ˆ๋‹ค๋ฉด ์ด๋ฅผ ๋ฌดํšจํ™”ํ•œ๋‹ค. ์ดํ›„ failureHandler์˜ onAuthenticationFailure ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค.

This post is licensed under CC BY 4.0 by the author.