[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
๋ฅผ ํธ์ถํ๋ค.
๐ ์ธ์ฆ ํ
์ด๋ฒ์๋ ํ๋ก์ฐ๋ฅผ ๋ณด๊ณ ๋ค์ ํ ๋ฒ ์ธ์ฆ ๊ณผ์ ์ ๋ํด ์ดํด๋ณด์. ์ธ์ฆ ์ฑ๊ณต ๋๋ ์คํจ ํ ๊ทธ ๋ค์ ๊ณผ์ ์ ๋ ์ด์ ์ ๋ง์ถ์ด๋ณด์.
์ฌ์ฉ์๊ฐ ์๊ฒฉ ์ฆ๋ช
์ ์
๋ ฅํ๊ณ ์๋ฒ์ ์์ฒญ์ ๋ณด๋ด๋ฉด 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
๋ฉ์๋๋ฅผ ํธ์ถํ๋ค.