์ด๋ฒ์๋ ์ง๋ ํฌ์คํ ์ ์ด์ด์ ์คํ๋ง ์ํ๋ฆฌํฐ๋ฅผ ์ด์ฉํ์ฌ ๋ก๊ทธ์ธ์ ์งํํ๋ ์ฝ๋๋ฅผ ์์ฑํ๊ธฐ ์ ์ ์คํ๋ง ์ํ๋ฆฌํฐ์ ๋ก๊ทธ์ธ ์งํ๋ฐฉ์์ ์ดํด๋ณด๊ฒ ์ต๋๋ค.
์ด๋ฒ ํฌ์คํ ์ ์กฐ๊ธ ๊ธธ๊ธฐ ๋๋ฌธ์, ์ ์ ์ข ๋ง์ ์ํ๋ก ์์ํ์๋๊ฑธ ์ถ์ฒ๋๋ฆฝ๋๋ค.
์ฐ์ ์คํ๋ง ์ํ๋ฆฌํฐ๋ฅผ ์ค์ ์ ํตํด, ๊ธฐ๋ณธ์ ์ธ ์ ๊ทผ ์ ์ด์, ์คํ๋ง ์ํ๋ฆฌํฐ์์ ์ ๊ณตํ๋ PasswordEncoder๋ฅผ ๋ฑ๋กํ๋๋ก ํ๊ฒ ์ต๋๋ค.
- ์ํ๋ฆฌํฐ๋ฅผ ์ด์ฉํ JSON ๋ฐ์ดํฐ๋ก ๋ก๊ทธ์ธ (์งํ ์ค)
- JWT๋ฅผ ์ด์ฉํ ์ธ์ฆ
- ๋๋ฉ์ธ, ํ ์ด๋ธ ์ค๊ณ, ์ํฐํฐ ์์ฑ
- ํ์๊ฐ์ + ์ ๋ณด์์ ๋ฑ ํ์ ์๋น์ค ์ ๊ณต
- ์นดํ ๊ณ ๋ฆฌ๋ณ ๊ฒ์ํ ๋ถ๋ฅ
- ๊ฒ์๊ธ ํ์ด์ง
- 1๋๊ธ -> *(๋ฌดํ) ๋๋๊ธ ๊ตฌ์กฐ
- ๋์ ์ธ ๊ฒ์ ์กฐ๊ฑด์ ์ฌ์ฉํ ๊ฒ์
- ์ฌ์ฉ์ ๊ฐ ์ชฝ์ง ๊ธฐ๋ฅ
- ๋ฌดํ ์ชฝ์ง ์คํฌ๋กค
- ๊ฒ์๋ฌผ & ๋๊ธ์ ๋ํ ์๋
- ์ชฝ์ง์ ๋ํ ์๋
- ์ ์ํ ์ฌ์ฉ์ ๊ฐ ์ค์๊ฐ ์ฑํ
- ๋์ ์ธ ๊ฒ์ ์กฐ๊ฑด์ ์ฌ์ฉํ ๊ฒ์
- ํ์๊ฐ์ ์ ๊ฒ์ฆ(์: XX๋ํ๊ต XX๊ณผ๊ฐ ์๋๋ฉด ๊ฐ์ ํ ์ ์๊ฒ)
- Swagger๋ฅผ ์ฌ์ฉํ API ๋ฌธ์ ๋ง๋ค๊ธฐ
- ์ ๊ณ & ๋ธ๋๋ฆฌ์คํธ ๊ธฐ๋ฅ
- AOP
- ์ฌ์ดํธ ๊ตญ์ ํ
- ์ด๋๋ฏผ ํ์ด์ง
- ์บ์
- ๋ฐฐํฌ (+ ๋ฌด์ค๋จ ๋ฐฐํฌ)
- ๋ฐฐํฌ ์๋ํ
- ํฌํธ์ ์ด๋ํฐ ์ค๊ณ๋ฅผ ๋ฐ๋ฅด๋ ํจํค์ง ๊ตฌ์กฐ ์ค๊ณํ๊ธฐ
- ...
์คํ๋ง ์ํ๋ฆฌํฐ ์ค์ ํ๊ธฐ
ํจํค์ง ๊ฒฝ๋ก๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.formLogin().disable()//1 - formLogin ์ธ์ฆ๋ฐฉ๋ฒ ๋นํ์ฑํ
.httpBasic().disable()//2 - httpBasic ์ธ์ฆ๋ฐฉ๋ฒ ๋นํ์ฑํ(ํน์ ๋ฆฌ์์ค์ ์ ๊ทผํ ๋ username๊ณผ password ๋ฌผ์ด๋ด)
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/login", "/signUp","/").permitAll()
.anyRequest().authenticated();
}
@Bean
public PasswordEncoder passwordEncoder(){
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
}
์ ํฌ๋ json์ ํตํด ๋ก๊ทธ์ธ์ ์งํํ ๊ฒ์ด๋ฉฐ, ๋ก๊ทธ์ธ ์ดํ refreshToken์ด ๋ง๋ฃ๋๊ธฐ ์ ๊น์ง๋ ํ ํฐ์ ํตํด ์ธ์ฆ์ ์งํํ ๊ฒ์ด๊ธฐ์ formLogin, httpBasic์ ๋ชจ๋ disable ์ฒ๋ฆฌํ์๊ณ
csrf๋ disable
์ธ์ ์ Stateless(์ํ๋ฅผ ์ ์งํ์ง ์์)์ผ๋ก ์ค์ ํด ์ฃผ์์ต๋๋ค.
๋ก๊ทธ์ธ(/login), ํ์๊ฐ์ (/signUp), ๋ฉ์ธํ์ด์ง(/)์ ๋ํด์๋ ์ธ์ฆ ์์ด๋ ์ ๊ทผ ๊ฐ๋ฅํ๊ฒ ๋ง๋ค์์ต๋๋ค.
๋ค์์ผ๋ก ์ดํด๋ณผ ๊ฒ์ PasswordEncoder์ ๋๋ค.
PasswordEncoder
PasswordEncoderFactories.createDelegatingPasswordEncoder();
ํด๋น ์ฝ๋๋ฅผ ์ข ๋ ์์ธํ ์ดํด๋ด ์๋ค
encodingId๋ "bcrypt"์ด๊ณ , ์ด๋ BCryptPasswordEncoder์ ๋งคํ๋ฉ๋๋ค.
์ดํ DelegatingPasswordEncoder์ ์์ฑ์์ ์ธ์๋ก ๋๊ฒจ์ค๋๋ค.
์์ฑ์์์๋ PREFIX์ SUFFIX๋ฅผ ๊ฒ์ฌํฉ๋๋ค. PREFIX๋ "{" ์ด๊ณ , SUFFIX๋ "}" ์ด๋ฏ๋ก ํด๋น๋์ง ์๊ณ ,
๊ฒฐ๊ตญ passwordEncoderForEncode๋ก๋ BCryptPasswordEncoder๋ฅผ ์ฌ์ฉํ๋ DelegatingPasswordEncoder๋ฅผ ์ฌ์ฉํฉ๋๋ค.
์ฐธ๊ณ - BCrypt
bcrypt๋ ์ํธํ๋ฅผ ํ ๋ ํด์ ์๊ณ ๋ฆฌ์ฆ์ธ SHA-256์ ์ฌ์ฉํฉ๋๋ค.
ํด์ ์๊ณ ๋ฆฌ์ฆ์ ๋ณตํธํ๊ฐ ๋ถ๊ฐ๋ฅํฉ๋๋ค.
์ฆ DB์ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ ๋, Bcrypt๋ก ์ ์ฅ์ ํ๋ค๋ฉด ์ด๋ ์ํธํ๋์ ์ ์ฅ๋๊ณ , ์ด๋ฅผ ๋ณตํธํ ํ ์ ์๋ ๋ฐฉ๋ฒ์ ์์ต๋๋ค.
๊ฐ๋ ๋น๋ฐ๋ฒํธ ์ฐพ๊ธฐ๋ฅผ ํ๋ฉด, ์๋ก์ด ๋น๋ฐ๋ฒํธ๋ฅผ ์ ๋ ฅํ๊ฑฐ๋ ์์ ๋น๋ฐ๋ฒํธ๋ฅผ ๋ฐ๊ธ๋ฐ์ผ์ จ๋ ๊ธฐ์ต์ด ์์ผ์ค๊ฒ๋๋ค.
์ด๋ ๋น๋ฐ๋ฒํธ์ ๋ณตํธํ๊ฐ ๋ถ๊ฐ๋ฅํ์ฌ ์ฌ์ฉ์๊ฐ ์ธ์ฆ๋๋ฉด ์๋ก์ด ๋น๋ฐ๋ฒํธ ๊ฐ์ ๊ธฐ์กด์ ์๋ ๊ฐ ๋์ ์ ์ฌ์ฉํด์ผ ํ๊ธฐ ๋๋ฌธ์ ๊ทธ๋ ์ต๋๋ค.
๋น๋ฐ๋ฒํธ ์ผ์น ์ฌ๋ถ๋์?
(์ด๊ฑด ์ ๋ ์์ธํ๋ ๋ชจ๋ฅด๊ฒ ์ง๋ง DB์ ์ ์ฅํ ๋, ์ด๋ค ์ํธ ๋ก์ง? ์ ์ํธํํ์ฌ ์ ์ฅํ ํ, ์ด๋ฅผ ํตํด ๋น๊ตํ๋ ๊ทธ๋ฐ ๋๋์ธ๊ฑฐ ๊ฐ์ผ๋.. ์์ธํ๋ ๋ชจ๋ฅด๊ฒ ์ต๋๋ค. ์์ธํ ์๊ณ ์ถ๋ค๋ฉด ์ฐพ์๋ณด์๋๊ฑธ ์ถ์ฒ๋๋ฆฌ๋ฉฐ, ์๋ฌดํผ '๋น๋ฐ๋ฒํธ์ ๋น๊ต๋ ๊ฐ๋ฅํ๋ ๋ณตํธํ๋ ๋ถ๊ฐ๋ฅ ํ๋ค'๋ ๊ฒ๋ง ์๊ณ ๋์ด๊ฐ์ผ๋ฉด ์ข๊ฒ ์ต๋๋ค)
Reference
์ด์ DelegatingPasswordEncoder ์ ๋ํด์ ์์๋ณด๊ฒ ์ต๋๋ค.
DelegatingPasswordEncoder์ encode ๋ฉ์๋๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค
encode ๋ฉ์๋์ ๋ฐํ ๊ฒฐ๊ณผ๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
{bcrypt}BCryptPasswordEncoder๋ก ์ํธํ๋ ๋ฌธ์์ด
์คํ๋ง ์ํ๋ฆฌํฐ๋ '{์ํธํ ๋ฐฉ์}์ํธํ๋ ๋น๋ฐ๋ฒํธ' ํ์์ password๋ฅผ ํ์๋ก ํ๊ธฐ ๋๋ฌธ์ ๋ค์๊ณผ ๊ฐ์ด {}๋ก ๊ฐ์ธ์ ธ์ ๋ฐํ๋๋ ๊ฒ์ ์ ์ ์์ต๋๋ค.
์ฌ๊ธฐ์ BCryptPasswordEncoder๋ ์ด๋ป๊ฒ ์ํธํํ๋์ง ์์๋ณด๊ฒ ์ต๋๋ค.
BCryptPasswordEncoder์ ์ธ์ฆ ๋ฐฉ์์ ๋ค์ ์ฝ๋์ ๋์์์ต๋๋ค.
getSalt()๋ ์์์ ๋ฌธ์์ด์ ์ป์ด์ค๋ ๋ฉ์๋์ ๋๋ค.
๊ทธ๋ค์ hashpw๋ ์ต์ข ์ ์ผ๋ก ๋ค์ ๋ฉ์๋๋ฅผ ํธ์ถํฉ๋๋ค.
public static String hashpw(byte passwordb[], String salt) {
... ์๋ต
if (salt == null) { //์์์ ๋ฌธ์์ด์ธ salt๊ฐ null์ด๋ผ๋ฉด ์์ธ๋ฅผ ๋ฐ์์ํต๋๋ค.
throw new IllegalArgumentException("salt cannot be null");
}
int saltLength = salt.length();//๋๋ค ๋ฌธ์์ด์ ๊ธธ์ด๋ฅผ ๊ตฌํฉ๋๋ค.
if (saltLength < 28) {//์์์ ๋ฌธ์์ด์ ๊ธธ์ด๊ฐ 28๊ธ์๋ณด๋ค ์์ ๊ฒฝ์ฐ ์์ธ๋ฅผ ๋ฐ์์ํต๋๋ค.
//์ฆ BCryptPasswordEncoder์์ ์ฌ์ฉํ๋ salt๋ 28๊ธ์ ์ด์์ ์์์ ๋ฌธ์์ด์ ํ์๋ก ํ๋ค๋๊ฒ์ ์ ์ ์์ต๋๋ค.
throw new IllegalArgumentException("Invalid salt");
}
... ์๋ต
}
์ฃผ์์ ์ฐธ๊ณ ํด์ฃผ์ธ์.
์ด์ ๋น๋ฐ๋ฒํธ ์ํธํ๊ฐ ์ ์ด๋ฃจ์ด์ง๋์ง ํ์ธํด๋ณด๋ ํ ์คํธ์ฝ๋๋ฅผ ์๋ํ๊ฒ ์ต๋๋ค.
๋น๋ฐ๋ฒํธ ์ํธํ ํ ์คํธ
์์ฑํ๊ณ ์๋ ์ ํ๋ฆฌ์ผ์ด์ ๋ก์ง๊ณผ ๊ด๊ณ ์์ด, ์ด๋ ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ์ธ๋ถ ๊ธฐ๋ฅ์ ๊ฒ์ฆํด๋ณด๊ธฐ ์ํด ์ฌ์ฉ๋๋ ํ ์คํธ๋ฅผ ํ์ต ํ ์คํธ๋ผ๊ณ ํฉ๋๋ค. ์ด๋ฅผ ์ด์ฉํ์ฌ ๊ธฐ๋ฅ์ ๋ํ ์ฌ์ฉ๋ฒ์ ์ตํ๋ณผ ์ ์์ต๋๋ค.
learning์ด๋ผ๋ ํจํค์ง๋ฅผ ๋ง๋ค์ด ํ ์คํธ๋ฅผ ์์ฑํ๊ฒ ์ต๋๋ค.
๋น๋ฐ๋ฒํธ ์ํธํ
@Test
public void ํจ์ค์๋_์ํธํ() throws Exception {
//given
String password = "์ ๋ํShinDongHun";
//when
String encodePassword = passwordEncoder.encode(password);
//then
assertThat(encodePassword).startsWith("{");
assertThat(encodePassword).contains("{bcrypt}");
assertThat(encodePassword).isNotEqualTo(password);
}
BCryptPasswordEncoder๋ก ์ธ์ฝ๋ฉ๋ ํจ์ค์๋๋ {bcrypt}๋ฅผ ์์๋ถ๋ถ์ ํฌํจํด์ผ ํฉ๋๋ค.
์ํธํ์ ํญ์ ๋๋คํ salt๋ฅผ ํตํ ์ํธํ -> ํญ์ ๋ค๋ฅธ ๊ฒฐ๊ณผ๊ฐ ๋ฐํ
@Test
public void ํจ์ค์๋_๋๋ค_์ํธํ() throws Exception {
//given
String password = "์ ๋ํShinDongHun";
//when
String encodePassword = passwordEncoder.encode(password);
String encodePassword2 = passwordEncoder.encode(password);
//then
assertThat(encodePassword).isNotEqualTo(encodePassword2);
}
์ํธํ๋ ๋น๋ฐ๋ฒํธ ๋งค์น
@Test
public void ์ํธํ๋_๋น๋ฐ๋ฒํธ_๋งค์น() throws Exception {
//given
String password = "์ ๋ํShinDongHun";
//when
String encodePassword = passwordEncoder.encode(password);
//then
assertThat(passwordEncoder.matches(password, encodePassword)).isTrue();
}
๋ก๊ทธ์ธ ์งํ ๋ฐฉ์ - formLogin
์ด์ ์ํ๋ฆฌํฐ ํํฐ๋ฅผ ์ปค์คํ ํ๊ธฐ ์ ์ ๊ธฐ๋ณธ์ ์ธ ๋ก๊ทธ์ธ ์งํ ๊ณผ์ ์ ์์๋ณด๊ธฐ ์ํด FormLogin ๋ฐฉ์์ ๊ณผ์ ์ ์์๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
FormLogin์ ์งํํ๋ฉด ์๋์ ๊ฐ์ ๋ฐฉ์์ผ๋ก ์ธ์ฆ(Authentication)์ด ์งํ๋ฉ๋๋ค.
๋จผ์ /login์ผ๋ก POST ์์ฒญ์ ๋ณด๋ด๋ณด๊ฒ ์ต๋๋ค.
ํด๋น ์์ฒญ์ LogoutFilter ์ดํ์ AbstractAuthenticationProcessingFilter๋ฅผ ํธ์ถํ๋ฉฐ, AbstractAuthenticationProcessingFilter์์ ์ธ์ฆ(Authentication)์ ์งํํฉ๋๋ค.
์กฐ๊ธ ๋ ์ดํด๋ณด๊ฒ ์ต๋๋ค.
4๊ฐ์ ๋ถ์ ์ฌ๊ฐํ์ ์ ๋ณด์๋ฉด, ๋จผ์ requiresAuthentication๋ฅผ ํตํด ์ธ์ฆ์ ํ์์ฑ์ ์ฒดํฌํฉ๋๋ค.
๋ก์ง์ ์ดํด๋ณด๊ฒ ์ต๋๋ค.
requiresAuthenticationRequestMatcher์ matchs๋ฅผ ํธ์ถํฉ๋๋ค.
์ค์ ๋๋ requiresAuthenticationRequestMatcher๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
์ผํ ๋ณด๋ /login์ ๋ํด์, POST๋ก ์ค๋ ์์ฒญ์ด๋ฉด matches์ ๊ฑธ๋ฆฌ๋ ๊ฒ ๊ฐ์ต๋๋ค.
์ฐธ๊ณ ๋ก requiresAuthenticationRequestMatcher๋ก๋ AntPathRequestMatcher๊ฐ ์ฌ์ฉ๋ฉ๋๋ค. (๋๋ฒ๊น ํ๋ฉด ๋์ต๋๋ค)
์ ํฌ๋ /login์ POST ์์ฒญ์ ๋ณด๋๊ธฐ์, requiresAuthentication์ ํต๊ณผํฉ๋๋ค.
๋ค์์ผ๋ก๋ attemptAuthentication์ด ์คํ๋๋ฉฐ Authentication ๊ฐ์ฒด๋ฅผ ๋ฐํํฉ๋๋ค.
attemptAuthentication์ ์ถ์ ๋ฉ์๋๋ก
AbstractAuthenticationProcessingFilter๋ฅผ ๊ตฌํํ ํด๋์ค์์ ์ด๋ฅผ ๊ตฌํํด์ ์ฌ์ฉํ๊ฒ ๋ฉ๋๋ค.
formLogin์์๋ ์ด๋ค ํด๋์ค๊ฐ ์ฌ์ฉ๋ ๊น์?
UsernamePasswordAuthenticationFilter์ด ์ฌ์ฉ๋ฉ๋๋ค.
๊ทธ๋ผ ์ด์ UsernamePasswordAuthenticationFilter๋ฅผ ์กฐ๊ธ ์์ธํ ์ดํด๋ณด๊ฒ ์ต๋๋ค.
public class UsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "username";//2
public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "password";//2
private static final AntPathRequestMatcher DEFAULT_ANT_PATH_REQUEST_MATCHER = new AntPathRequestMatcher("/login",
"POST");//1
private boolean postOnly = true;
public UsernamePasswordAuthenticationFilter() {
super(DEFAULT_ANT_PATH_REQUEST_MATCHER);
}
public UsernamePasswordAuthenticationFilter(AuthenticationManager authenticationManager) {
super(DEFAULT_ANT_PATH_REQUEST_MATCHER, authenticationManager);
}
@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 : "";
username = username.trim();
String password = obtainPassword(request);
password = (password != null) ? password : "";
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);//3
// Allow subclasses to set the "details" property
setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);//4
}
//์๋ต
}
์กฐ๊ธ ์๋ตํ์ฌ ๊ฐ์ ธ์์ต๋๋ค.
๋๋ต์ ์ผ๋ก ์ค๋ช ํ์๋ฉด ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
- /login ์ POST ๋ฐฉ์์ผ๋ก ๋ค์ด์ค๋ ์์ฒญ์ ๋ํด์ ์๋ํฉ๋๋ค.
- username๊ณผ password๋ฅผ ํ๋ผ๋ฏธํฐ๋ก ๊ฐ์ง๊ณ ์์ด์ผ ํฉ๋๋ค.
- username๊ณผ password๋ฅผ ๊ฐ์ง๊ณ UsernamePasswordAuthenticationToken์ ์์ฑํฉ๋๋ค.
- UsernamePasswordAuthenticationToken์ AuthenticationManager์ authenticate()๋ฉ์๋์ ์ธ์๋ก์ ๋๊ฒจ์ฃผ๊ณ , ํด๋น ๋ฉ์๋์ ๋ฐํ๊ฒฐ๊ณผ์ธ Authentication ๊ฐ์ฒด๋ฅผ ๋ฐํํฉ๋๋ค.
๋ค์์ UsernamePasswordAuthenticationFilter์์ ํธ์ถํ๋ AuthenticationManager์ ๋ํด ์์๋ณด๊ฒ ์ต๋๋ค.
ProviderManager๊ฐ ์ฌ์ฉ๋ฉ๋๋ค.
ProviderManager๋ ๋ด๋ถ์ ์ผ๋ก AuthenticationProvider์ ๋ชฉ๋ก์ ๊ฐ์ง๊ณ ์์ผ๋ฉฐ,
์ด๋ค ์ค Authentication๊ฐ์ฒด๋ก ๋ค์ด์จ ํ์ ์ ์ฒ๋ฆฌํ ์ ์๋AuthenticationProvider๊ฐ ์์ผ๋ฉด
ํด๋นAuthenticationProvider์authenticate() ๋ฉ์๋๋ฅผ ์คํํฉ๋๋ค.
์๊น ์ UsernamePasswordAuthenticationFilter์์ Authentication ์ผ๋กUsernamePasswordAuthenticationToken๋ฅผ ์ ๊ณตํด ์ฃผ์์ต๋๋ค.
์ด๋ AbstractUserDetailsAuthenticationProvider๊ฐ ์ฒ๋ฆฌํฉ๋๋ค.
๋ ์ ํํ๋ AbstractUserDetailsAuthenticationProvider๋ฅผ ๊ตฌํํ DaoAuthenticationProvider๊ฐ ์ฒ๋ฆฌํฉ๋๋ค
์ฌ๊ธฐ์๋ ๋ณด์๋ฉด retrieveUser๋ฅผ ํตํด User ์ ๋ณด๋ฅผ ๊ฐ์ ธ์ค๋๋ฐ, ํด๋น ๋ฉ์๋๋ฅผ ์ดํด๋ณด๊ฒ ์ต๋๋ค.
protected abstract UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication)
throws AuthenticationException;
์๊น AbstractAuthenticationProcessingFilter์ ๋น์ทํ๊ฒ ๊ตฌํ ํด๋์ค์๊ฒ ๋ฉ์๋์ ๊ตฌํ์ ์ฑ ์์ ๋ฏธ๋ฃจ์์ต๋๋ค.
์ด์ ์ด๋ฅผ ๊ตฌํํ DaoAuthenticationProvider ๋ฅผ ์ดํด๋ณด๊ฒ ์ต๋๋ค.
์ฌ๊ธฐ์๋ UserDetailsService์ loadUserByUsername์ ์ฌ์ฉํ์ฌ ์ฌ์ฉ์ ์ ๋ณด๋ฅผ ๋ฐ์์ต๋๋ค.
๊ทธ๋ฆฌ๊ณ ํด๋น UserDetails ์ ๋ณด๋ฅผ ๋ฐํํ๋ฉด ๊ฒฐ๊ตญ AbstractUserDetailsAuthenticationProvider๊ฐ ๋ฐ์์ต๋๋ค.
์๋๋ ๋ค์ AbstractUserDetailsAuthenticationProvider์ authenticate ๋ฉ์๋์ ๋๋ค
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
..์๋ต
String username = this.determineUsername(authentication);
UserDetails user = this.userCache.getUserFromCache(username);
if (user == null) {
cacheWasUsed = false;
try {
user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication);
}
..์๋ต
Object principalToReturn = user;
if (this.forcePrincipalAsString) {
principalToReturn = user.getUsername();
}
return createSuccessAuthentication(principalToReturn, authentication, user);
}
AbstractUserDetailsAuthenticationProvider ๋ DaoAuthenticationProvider๊ฐ ๋ฐํํด์ค UserDetails๋ฅผ ์ฌ์ฉํด์
username,
UsernamePasswordAuthenticationToken (๋ฉ์๋์ ๋งค๊ฐ๋ณ์๋ก ๋ฐ์์จ authentication ๊ฐ์ฒด๋ฅผ ์ฌ์ฉ),
Userdetails,
๋ฅผ ๊ฐ์ง๊ณ Authentication ๊ฐ์ฒด๋ฅผ ๋ง๋ค์ด ๋ฐํํฉ๋๋ค.
(createSuccessAuthentication ๋ฉ์๋ ์ฌ์ฉ)
createSuccessAuthentication()์ ๋ณด์๋ฉด
principal์ User,
Authentication์ UsernamePasswordAuthenticationToken์ธ ๊ฒ์ ์ ์ ์์ต๋๋ค.
๊ทธ๋ฆฌ๊ณ ํด๋น ์ ๋ณด๋ฅผ ํ์ฉํด์ ์๋ก์ด UsernamePasswordAuthenticationToken ๊ฐ์ฒด๋ฅผ ๋ง๋ค์ด ๋ฆฌํดํฉ๋๋ค.
์ด๋ AbstractAuthenticationProcessingFilter์ ๋ฐํ๋๋ฉฐ ์ด๋ฅผ ๊ฐ์ง๊ณ ์ธ์ฆ ์ฑ๊ณต๊ณผ ์คํจ ์ฌ๋ถ๋ฅผ ๋ฐ์ง๋๋ค.
์คํจํ์ ๋๋ ์ฑ๊ณตํ์ ๋์ ๋น์ทํ๋ฏ๋ก ์ฑ๊ณตํ๋ ์์ฒญ๋ง ๋ณด๊ฒ ์ต๋๋ค.
์ฑ๊ณตํ๋ฉด SecurityContext์ ์ธ์ฆ ์ ๋ณด์ธ Authentication ๊ฐ์ฒด๋ฅผ ๋ด๊ณ ,
successHandler์ onAuthenticationSuccess ๋ฉ์๋๋ฅผ ๋ฐํํ๋ฉด์ ์ธ์ฆ์ด ์๋ฃ๋ฉ๋๋ค.
(์ดํ ๊ถํ์ ์ฒดํฌํ๋ AccessDecisionManager๋ฑ๋ ์์ง๋ง, ์ฐ์ ๋ก๊ทธ์ธ์ ์ปค์คํ ํ๋๋ฐ๋ ํ์๊ฐ ์๊ธฐ ๋๋ฌธ์ ์ดํด๋ณด์ง ์๊ฒ ์ต๋๋ค)
์ ๋ง ๊ธธ์์ต๋๋ค. ํ๋ฒ ๋ค์ ๊ฐ๋จํ๊ฒ ์์ฝํด๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
๋ก๊ทธ์ธ ๊ณผ์ ์์ฝ
/login, POST๋ก ์์ฒญ์ด ๋ค์ด์์ ๊ฒฝ์ฐ (FormLogin ์ฌ์ฉ ์)
1. AbstractAuthenticationProcessingFilter์ requiresAuthentication์ผ๋ก ์ธ์ฆ์ ์งํํ ์ง์ ๋ํ ์ฌ๋ถ๋ฅผ ํ๋จํ๋ค.
2. ์ธ์ฆ์ ์งํํด์ผ ํ๋ค๋ฉด attemptAuthentication() ๋ฉ์๋๊ฐ ์คํ๋๋ค.
์ด๋ ์ถ์ ๋ฉ์๋๋ก AbstractAuthenticationProcessingFilter์ ๊ตฌํํ UsernamePasswordAuthenticationFilter ์์ ์ฒ๋ฆฌํ๋ค.
3. UsernamePasswordAuthenticationFilter๋ ์์ฒญ์์ username๊ณผ password๋ฅผ ๊บผ๋ด์ด, UsernamePasswordAuthenticationToken์ด๋ผ๋ Authentication ๊ตฌํ์ฒด๋ฅผ ๋ง๋ค์ด
AuthenticationManager์ authenticate()์ ๋ฉ์๋๋ฅผ ํธ์ถํ๋ฉฐ, ์ธ์๋ก ๋๊ฒจ์ค๋ค.
4. AuthenticationManager๋ก๋ ProviderManager๊ฐ ์ฌ์ฉ๋๋ฉฐ,
๋ด๋ถ์ ์ผ๋ก ๊ฐ์ง ์ฌ๋ฌ Provider์ค AbstractUserDetailsAuthenticationProvider์ ๊ตฌํ์ฒด์ธ DaoAuthenticationProvider๋ฅผ ์ฌ์ฉํ์ฌ UsernamePasswordAuthenticationToken์ ๋ํ ์ธ์ฆ์ ์งํํ๋ค.
5. DaoAuthenticationProvider๋ UserDetailsService์ loadUserByUsername๋ฅผ ํตํด ์ ์ ์ ๋ณด๋ฅผ ๋ฐ์์ค๊ณ
์ด๋ AbstractUserDetailsAuthenticationProvider๊น์ง ๋ฐํ๋์ด
์ด๊ณณ์์ Authentication๊ฐ์ฒด๋ก ๋ณํ๋์ด ๋ค์ ๋ฐํ๋๋ค.
์ด๋ ์ต์ข ์ ์ผ๋ก ๋ฐํ๋๋ principal์ User, credentials๋ password๊ฐ ๋๋ค.
6. ์ด๋ AbstractAuthenticationProcessingFilter์ ๋ฐํ๋์ด ์ธ์ฆ์ ์ฑ๊ณต์ฌ๋ถ์ ์คํจ์ฌ๋ถ๋ฅผ ๋ฐ์ง๋ค.
์ฑ๊ณตํ์ ๊ฒฝ์ฐ SecurityContextHolder์ ์ธ์ฆ ์ ๋ณด(Authentication ๊ฐ์ฒด)๋ฅผ ์ ์ฅํ๊ณ successHander์ ์ฑ๊ณต ๋ฉ์๋๋ฅผ ํธ์ถํ๋ฉฐ ๋๋๋ค.
์ด์ ๋ค์๊ณผ ๊ฐ์ ๋ฐฉ์์ผ๋ก ๋ก๊ทธ์ธ ํํฐ๋ฅผ ์ปค์คํฐ๋ง์ด์ง ํ๊ฒ ์ต๋๋ค
AbstractAuthenticationProcessingFilter๋ฅผ ์์๋ฐ์ Filter๋ฅผ ๋ง๋ค์ด ์ธ์ฆ์ ์งํํ๊ฒ ์ต๋๋ค.
AuthenticationManager๋ก๋ ์คํ๋ง๊ณผ ๋๊ฐ์ด ProviderManager๋ฅผ ์ฌ์ฉํฉ๋๋ค.
๋ํ ProviderManager๊ฐ ๊ฐ์ง Provider ๋ํ ์คํ๋ง๊ณผ ๋์ผํ๊ฒ DaoAuthenticationProvider๋ฅผ ์ฌ์ฉํ๊ฒ ์ต๋๋ค.
UserDetailsService๋ก๋ JPA์ ์ฐ๋์ํจ LoginService๊ฐ ์์๋ฐ์ ๊ตฌํํ๋๋ก ํ๊ฒ ์ต๋๋ค.
MemberService๊ฐ ์๋ LoginService๋ฅผ ๋ฐ๋ก ์ ์ํ ์ด์ ๋ Login์ Member์ ์ผ๋ฐ์ ์ธ ๊ธฐ๋ฅ๋ณด๋ค๋ ํน์ํ ๊ธฐ๋ฅ์ด๋ผ ์๊ฐํ์ฌ ์ญํ ์ ๋ถ๋ฆฌ์์ผฐ์ต๋๋ค.
(์์กด์ฑ๋ฑ์ ์๊ฐํ๋๋ผ๋, MemberService๋ ์ํ๋ฆฌํฐ์ ์์กด๊ด๊ณ๋ฅผ ๋งบ์ ํ์๊ฐ ์๋ค๊ณ ์๊ฐํ์ฌ, LoginService๋ก ๋ฐ๋ก ๋ถ๋ฆฌ์์ผฐ์ต๋๋ค)
๋ก๊ทธ์ธ ์ฑ๊ณต ์ JWT ํ ํฐ์ ๋ฐ๊ธํ๋ AuthenticationSuccessHandler๋ฅผ ๋ง๋ค๊ฒ ์ต๋๋ค.
์ ์ฒด ์ฝ๋๋ ๊นํ๋ธ์์ ํ์ธํ์ค ์ ์์ต๋๋ค.
https://github.com/ShinDongHun1/SpringBoot-Board-API
๐ Reference
๊ฒ์ํ ์ฐธ๊ณ
SHA ์๊ณ ๋ฆฌ์ฆ ์ฐธ๊ณ
bcrypt ์ฐธ๊ณ