Spring Security #3 ์ํคํ ์ฒ ์ดํดํ๊ธฐ, ์ธ์ฆ ๋ฐ ์ธ๊ฐ ์๋ฆฌ
by JiwonDevํํฐ๋ ์๋ฐ ์น ํ์ค์ธ Servlet 2.3๋ฒ์ ๋ถํฐ ์ถ๊ฐ๋ ๊ธฐ๋ฅ์ด๋ค.
์คํ๋ง ์ํ๋ฆฌํฐ๋ ์๋ธ๋ฆฟ ํํฐ ๊ธฐ๋ฐ์ผ๋ก ๋์ํ๋ ํ๋ ์์ํฌ์ด๋ค.
2022.03 ์ถ๊ฐ(๋ฆฌ๋ทฐ๋ด์ฉ)
Provider๋ฅผ ๋ฐ๋ก ๋ง๋ค์ง ์๊ณ , ๊ทธ๋ฅ ๊ธฐ๋ณธ AuthenticationManager.authenticate(new token())์ ํธ์ถํด๋ฒ๋ฆฐ๊ฒฝ์ฐ
๋จ์ํ ํจ์ค์๋๋ฅผ ๋น๊ตํ๋ ๋ก์ง์ด๋ผ ๋ฐ๋ก Provider๋ฅผ ๋ง๋ค์ง ์๊ณ , ์ํ๋ฆฌํฐ์ ๊ธฐ๋ณธ ํ๋ก๋ฐ์ด๋๋ก ์ฒ๋ฆฌํ์ต๋๋ค.
๋์ค์ ์ถ๊ฐ์ ์ธ ์ธ์ฆ์ด ํ์ํ๊ฒ ๋๋ฉด
public class CustomAuthenticationProvider implements AuthenticationProvider ๊ฐ์ ๊ฑธ ์ถ๊ฐํ๋๋ก ํ๊ฒ ์ต๋๋ค ๐
์ํ๋ฆฌํฐ๊ฐ ์์์ ํด์ค๋ค๋ผ๊ณ ์ค๋ช ํ๋ ์กฐ๊ธ ๋ถ์ํด์, ์ง์ ์ฝ๋๋ฅผ ๋ฏ์ด๋ดค๋๋ฐ
1. ๋ก๊ทธ์ธ ๊ณผ์ ์์ ์๋ ์ฝ๋๋ฅผ ์คํํฉ๋๋ค.
- authenticationManager์ ๊ตฌํ์ฒด์ธ ProviderManager๋ฅผ ํตํด ํด๋น ํ ํฐ์ ์ฒ๋ฆฌํ ์ ์๋ ํ๋ก๋ฐ์ด๋๋ฅผ ์ฐพ์ต๋๋ค.
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(username, password)
);
2. ๋ฐ๋ก ๋ฑ๋กํด๋ ํ๋ก๋ฐ์ด๋๊ฐ ์๊ธฐ ๋๋ฌธ์, ๊ธฐ๋ณธ ํ๋ก๋ฐ์ด๋์ธ DaoAuthenticationProvider ๊ฐ ๋์ํฉ๋๋ค.
- ์ด ํ๋ก๋ฐ์ด๋๋ ๋งค๋์ ์ ๋ฑ๋ก๋ UserDetailsService์ PasswordEncoder๋ฅผ ์ฌ์ฉํฉ๋๋ค.
// SecurityConfig.java
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(accountDetailsService).passwordEncoder(passwordEncoder());
auth.authenticationProvider(jwtAuthenticationProvider); // ์ด๊ฑด JwtAuthToken ์ ์ฒ๋ฆฌํ๋ Provider
}
- DaoAuthenticationProvider ๋ ์๋ ํด๋์ค๋ฅผ ์์ํ๋ฉฐ, UsernamePasswordAuthenticationToken ๋ฅผ support ํฉ๋๋ค.
public abstract class AbstractUserDetailsAuthenticationProvider{
... ์๋ต ...
@Override
public boolean supports(Class<?> authentication) {
return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication));
}
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
...
try {
this.preAuthenticationChecks.check(user);
additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication);
}
catch (AuthenticationException ex) {
...
}
return createSuccessAuthentication(principalToReturn, authentication, user);
}
}
3. ์ฆ, Provider.authenticate(...)๊ฐ ์คํ๋๋ฉด, ์๋์ ๋ก์ง์ด ์คํ๋ฉ๋๋ค.
public class DaoAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
... ์๋ต ...
protected void additionalAuthenticationChecks(UserDetails userDetails,
UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
if (authentication.getCredentials() == null) {
this.logger.debug("Failed to authenticate since no credentials provided");
throw new BadCredentialsException(this.messages
.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
}
String presentedPassword = authentication.getCredentials().toString();
if (!this.passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {
this.logger.debug("Failed to authenticate since password does not match stored value");
throw new BadCredentialsException(this.messages
.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
}
}
...
}
-------------------------------------------------------------
๐ญ ์คํ๋ง ์ํ๋ฆฌํฐ์ ๋์๊ณผ์ (๋ณต์ต)
๊ธฐ๋ณธ์ ์ผ๋ก ์๋ฐ ์๋ธ๋ฆฟ ํํฐ์ ๊ฒฝ์ฐ, FilterChain์ ๊ตฌ์ฑํ์ฌ ์๋ธ๋ฆฟ์ด ์คํ๋๊ธฐ ์ ํํฐ๋ฅผ ๊ฑฐ์น๊ฒ ๋๋ค.
์คํ๋ง ์ํ๋ฆฌํฐ์ ๊ฒฝ์ฐ ์๋ธ๋ฆฟ ํํฐ์ฒด์ธ์ DelegatingFilterProxy๋ฅผ ๋ฑ๋กํ๋ค. ์ด๋ ์๋ธ๋ฆฟ ํํฐ์ ๊ตฌํ์ฒด์ด๋ค.
๊ทธ๋ฆฌ๊ณ ํด๋น ํํฐ๊ฐ SecurityFilterChain์ ํธ์ถํ๊ณ , ์คํ๋ง์ ๋ฑ๋ก๋ ๋น์ ๊ฐ์ ธ์์ ์์กด์ฑ์ ์ฃผ์ ํ๊ฒ ๋๋ค.
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
// Lazily get Filter that was registered as a Spring Bean
// For the example in DelegatingFilterProxy delegate is an instance of Bean Filter0
Filter delegate = getFilterBean(someBeanName);
// delegate work to the Spring Bean
delegate.doFilter(request, response);
}
DelegatingFilterProxy ๋ ์คํ๋ง ๋น์ด ์๋๋ค. ์๋ธ๋ฆฟ ํํฐ๋ผ์ ์ค์ ๋์์ FilterChainProxy(์คํ๋ง Bean)์ ์์ํ๋ค.
์ฆ DelegatingFilter๋ ๋ณด์๊ณผ ๊ด๋ จ๋ ์ฒ๋ฆฌ๋ฅผ ํ์ง ์๋๋ค.
- FilterChainProxy๋ ์์ฒญ๊ณผ ์๋ต์ ์คํ๋ง ์ํ๋ฆฌํฐ ํํฐ ์ฒด์ธ์ ์์ํ๋ ์ญํ ์ ๋ด๋นํ๋ค.
[์๋ธ๋ฆฟ โก ์คํ๋ง ์ํ๋ฆฌํฐ์ ์ง์ ์ ]์ด๋ฉฐ, ์๋ธ๋ฆฟ์์ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ค๋ฉด ํด๋น ๊ฐ์ฒด์์ ์์ธ๊ฐ ๋ฐ์ํ๋ค.
- FilterChainProxy๋ ์ฑ๋น ํ๋์ง๋ง, SecurityFilterChain์ ์ฌ๋ฌ ๊ฐ๋ก ๊ตฌ์ฑํ ์๋ ์๋ค.
์ฆ ์๋ ๊ทธ๋ฆผ์ฒ๋ผ DelegatingFilterProxy(ํํฐ)์ ์๋ FilterChainProxy(์คํ๋ง ๋น)์์
HTTP URL์ ๋ฐ๋ผ ๋ค๋ฅธ ์คํ๋ง ์ํ๋ฆฌํฐ ์ฒด์ธ์ด ์๋ํ๋๋ก ๊ตฌ์ฑํ ์๋ ์๋ค.
๐ ์ฌ์ฉํ๋ ์ ์ฅ์์์ ๋์๊ณผ์
์ฌ์ฉํ๋ ์ ์ฅ์์ ๊ทธ๋ฆผ์ผ๋ก ๊ทธ๋ ค๋ณด๋ฉด ์๋์ ๊ฐ๋ค.
WebSecurityConfigurerAdpater๋ฅผ ํตํด HttpSecurity๋ฅผ ์์ฑํ๋ค.
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@Configuration
@EnableWebSecurity
@Order(0)
public class SecurityConfig1 extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/admin/**")
.authorizeRequests()
.anyRequest().authenticated()
.and()
.httpBasic(); // HTTP BASIC ๋ฐฉ์
}
}
//.. ๋ค๋ฅธ ํจํค์ง ..//
@Configuration
@Order(1) // ์ด๊ฑฐ ์์ผ๋ฉด IllegalStateException: @Order on WebSecurityConfigurers must be unique. ๋ฐ์
public class SecurityConfig2 extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().permitAll()
.and()
.formLogin(); // FORM LOGIN ๋ฐฉ์
}
}
์์ ์ฒ๋ผ ์ฌ๋ฌ๊ฐ์ ํํฐ ์ฒด์ธ์ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ, Order(~) ์์๋ ๋์ ๋ฒ์์ ๋ณด์์ค์ ์ ๋จผ์ ์ ์ฉํด์ฃผ๋๊ฒ ์ข๋ค.
์ซ์๊ฐ ๋ฎ์ ์๋ก Order(0) ์ฐ์ ์์๊ฐ ๋๋ค.
์ค์ ์ฝ๋๋ก ์ดํด๋ณด๊ธฐ (FilterChainProxy)
FilterChainProxy ๊ฐ์ฒด๋ ๋ฑ๋ก๋ ์ํ๋ฆฌํฐ ํํฐ์ฒด์ธ์ filters๋ผ๋ ๋ฆฌ์คํธ๋ก ๊ด๋ฆฌํ๋ค.
๋ฆฌ์คํธ์ ์๋ ์ํ๋ฆฌํฐ ํํฐ๋ค์ ๋ฐ๋ณต๋ฌธ์ผ๋ก ํ๋์ฉ request์ ๋น๊ต(matches)ํ๋ค.
๊ทธ ์ค ์ ์ผ ๋จผ์ ์ฐพ์ ํํฐ๋ฅผ ์ฌ์ฉํ๋๋ฐ,
๋น๊ตํ๋๋ฐ ์ฌ์ฉ๋ matches ๋ฉ์๋๋ฅผ ๊น๋ณด๋ฉด ํด๋น ์ํ๋ฆฌํฐ ํํฐ ์ฒด์ธ์ requestMatcher ๊ฐ์ฒด๋ฅผ ์ด์ฉํ๋ค.
๐ ๊ทธ๋์ ์ฌ๋ฌ๊ฐ์ ์คํ๋ง์ํ๋ฆฌํฐ ์ค์ ์ ๋ง๋ค ๋(=์ฒด์ธ์ด ์ฌ๋ฌ ๊ฐ์ผ ๋) @Order๊ฐ ํ์์ธ ๊ฒ์ด๋ค.
URL ์์ฒญ์ด ๊ฐ๋ค๋ฉด @Order์ ๋ฑ๋ก๋ ์์๋๋ก ๋จผ์ ํํฐ๊ฐ ๋งค์นญ๋๋ค.
์์์ ๋ฐฐ์ ์ง๋ง ๊ฐ๊ฐ์ ์คํ๋ง ์ํ๋ฆฌํฐ ํํฐ์ฒด์ธ์, ๋ฐ๋ก ๋ฑ๋กํ์ง ์์๋ ๋ง์ ๊ธฐ๋ณธ ํํฐ๊ฐ ์ ์ฉ๋์ด์๋ค.
์ด ํํฐ๋ค๋ ์๋ธ๋ฆฟ ํํฐ( chain.doFilter() )์ฒ๋ผ ๋์ํ๋ฉฐ ์์๊ฐ ์๋ค. ์ ์ฉ๋๋ ์์์ ์ข ๋ฅ๋ ๊ณต์๋ฌธ์๋ฅผ ์ฐธ๊ณ ํ์.
์๊ณ ์์ผ๋ฉด ์ข๊ธดํ๋ฐ, ์๋ ์ข ๋ฅ๊ฐ ๋ง๊ธฐ๋ํ๊ณ ๋ชจ๋ฅธ๋ค๊ณ ํด์ ์ฌ์ฉํ๋๋ฐ ๋ฌธ์ ๊ฐ ์๊ธฐ์ง๋ ์๋๋ค.
๐ญ ์ธ์ฆ(Authentication)
์ธ์ฆ์ ๋ก๊ทธ์ธ์ฒ๋ผ, ๋ด๊ฐ ๋๊ตฌ์ธ์ง ์ฆ๋ช ํ๋ ๊ฒ์ด๋ค.
์ธ์ฆ ์ id์ password๋ฅผ HTTP์์ฒญ์ ๋ด๊ณ , ์๋ฒ์ ์ ๋ฌ๋๋ค.
์๋ฒ์์ ์ธ์ฆ ํ, ์ธ์ฆ ๊ฐ์ฒด(User + ๊ถํ์ ๋ณด)์ ๋ด์ SecurityContext์ ๋ด๊ณ ์ ์ญ์ ์ผ๋ก ์ฌ์ฉํ ์ ์๊ฒ ์ ๊ณตํด์ค๋ค.
Authentication authentication = SecurityContexHolder.getContext().getAuthentication()
์ด ๊ฐ์ฒด์์๋ ์๋์ ๊ฐ์ ์ ๋ณด๋ฅผ ์ป์ ์ ์๋ค.
- principal : ์ฌ์ฉ์ ์์ด๋ ํน์ User ๊ฐ์ฒด๋ฅผ ์ ์ฅ
- credentials : ์ฌ์ฉ์ ๋น๋ฐ๋ฒํธ (๋ณด์์ ์ธ์ฆ์ดํ ์ธ์ฆ๊ฐ์ฒด์๋ ์ฌ์ฉํ ์ ์๊ฒ ์์ ๋น์๋๊ธฐ๋ ํจ)
- authorities : ์ธ์ฆ๋ ์ฌ์ฉ์์ ๊ถํ ๋ชฉ๋ก
- details : ์ธ์ฆ ๋ถ๊ฐ ์ ๋ณด (๋์ค์ ์ธ๊ธ)
- Authenticated : ์ธ์ฆ ์ฌ๋ถ
์ธ์ฆ์ ๋ณด๋ SecurityContext์ ๋ด๊ณ ์ ์ญ์ ์ผ๋ก ์ฌ์ฉํ ์ ์๊ฒ ์ ๊ณตํด์ค๋ค. ์ด๊ฒ ์ด๋ป๊ฒ ๊ฐ๋ฅํ๊ฑธ๊น?
์ ํํ๋ ํ์ฌ ์ค๋ ๋์ ThreadLocal ๋ฉ๋ชจ๋ฆฌ์ SecurityContextHolder๋ฅผ ๋๊ณ static ๋ฉ์๋๋ก SecurityContext๋ฅผ ๊บผ๋ธ๋ค.
๊ทธ๋ฆฌ๊ณ ์ธ์ฆ์ด ์๋ฃ๋๋ฉด HttpSession์ ์ธ์ฆ๊ฐ์ฒด๋ฅผ ์ ์ฅํด์ ์ค๋ ๋๊ฐ ์์ด์ง๋๋ผ๋ ์ ์ญ์ ์ผ๋ก ์ฌ์ฉํ ์ ์๋ค.
โจ HttpSession์ด ๋ญ์๋๋ผ..?
HTTP๋ ๊ธฐ๋ณธ์ ์ผ๋ก ๋น์ฐ๊ฒฐ์ฑ์ ์งํฅํฉ๋๋ค.
์ฆ ํญ์ ํด๋ผ์ด์ธํธ-์๋ฒ๊ฐ ์ฐ๊ฒฐ๋์ด์๋๊ฒ ์๋๋ผ, ์์ฒญ์ ๋ฐ๋ฅธ ์๋ต ์ดํ์๋ ์ฐ๊ฒฐ์ด ๋์ด์ง๊ฒ ๋ฉ๋๋ค.
๊ทธ๋์ ๋ด๊ฐ ์์ฒญ์ 3๋ฒ ๋ณด๋ด๋๋ผ๋ ์๋ฒ ์
์ฅ์์๋ ๋ค๋ฅธ์ฌ๋ 3๋ช
์ด ๊ฐ๊ฐ ๋ณด๋ด๋ ๊ฒ๊ณผ ๊ฐ์ต๋๋ค. ์ํ๊ฐ ์์ด์.
HTTP๋ฅผ ์ด์ฉํ ์น์์ ๋ก๊ทธ์ธ์ ๊ตฌํ๋ฐฉ๋ฒ์ ์ฌ๋ฌ๊ฐ์ง๋ง, ๋ํ์ ์ผ๋ก HttpSession ๊ฐ์ฒด๋ฅผ ์ด์ฉ ํ ์ ์์ต๋๋ค.
์๋ฒ(WAS์ ์๋ธ๋ฆฟ์ปจํ
์ด๋)์์๋ ์์ฒญ์ ๋ณด๋ด๋ ๋ธ๋ผ์ฐ์ ๋จ์๋ก Session ๋ฉ๋ชจ๋ฆฌ๋ฅผ ์์ฑํฉ๋๋ค.
๊ทธ๋ฆฌ๊ณ ๋ธ๋ผ์ฐ์ ์์ฒญ์ด ๋ค์ด์ค๋ฉด ์๋ธ๋ฆฟ ์ปจํ
์ด๋๋ request ์์ HttpSession ๊ฐ์ฒด๋ฅผ ์์ฑํ๊ณ ,
* ์ฐธ๊ณ ๋ก session์ ์์ฒญํ์ง ์์ผ๋ฉด ์๋ธ๋ฆฟ ์ปจํ ์ด๋์์ HttpSession ๊ฐ์ฒด๋ฅผ ๋ง๋ค์ง ์๋๋ก ์ต์ ํ๋์ด์์.
ํ์ฌ ์๋ฒ ์ธ์ ๋ฉ๋ชจ๋ฆฌ์ ์๋ ์ธ์ ์ JSESSIONID ๋ก ์ฐพ์์ ์ฐ๊ฒฐ์์ผ์ค๋๋ค.
์กฐ๊ธ ์ ๋ฆฌํด๋ณด์๋ฉด
1. ์๋ธ๋ฆฟ ์ปจํ
์ด๋๊ฐ Request๋ง๋ค HttpSession ๊ฐ์ฒด๋ฅผ ๋ง๋ญ๋๋ค.
์ ํํ๋ httpServletRequest.getSession() ๋ฉ์๋๊ฐ ํธ์ถ๋๋ฉด HttpSession ๊ฐ์ฒด๋ฅผ ๋ง๋ค์ด์ ๋ฐํํฉ๋๋ค.
1-1. ์ฐธ๊ณ ๋ก ์คํ๋ง์์๋ HttpSession์ ๊ทธ๋๋ก ๋ฐ์์์ ์ฌ์ฉํฉ๋๋ค.
๋ค๋ง HttpSession์ @Autowired๋ก ์ฌ์ฉํ๋ฉด HttpSession ๊ฐ์ฒด๋ฅผ ํ๋ก์๋ก ๋ง๋ค์ด์ Lazyํ๊ฒ ๋ถ๋ฌ์ต๋๋ค.
์ฆ httpSession.setAttribute() ๋ฉ์๋๋ฅผ ํธ์ถํ๋ ์์ ์ ์๋ธ๋ฆฟ ์ปจํ
์ด๋์์ ์์ฒญํ๊ณ ์์ฑ๋ฉ๋๋ค.
๋ฌผ๋ก ์ปจํธ๋กค๋ฌ ๋ฉ์๋ ์ธ์๋ก ๋ฐ๋ก ๋ฐ์์ค๋ฉด method(HttpSession session) ์ฆ์ HttpSession์ ๋ฐ์์ต๋๋ค.
2. HttpSession ๊ฐ์ฒด๋ฅผ ํตํด ์๋ธ๋ฆฟ ์ปจํ
์ด๋ ์์ ์ธ์
๋ฉ๋ชจ๋ฆฌ์ ์ ๊ทผ ๊ฐ๋ฅํฉ๋๋ค.
์ธ์
๋ฉ๋ชจ๋ฆฌ๋ ๋ธ๋ผ์ฐ์ ๋จ์๋ก ๋ง๋ค์ด์ง๋ฉฐ, JSESSIONID์ ๊ธฐ์ค์ผ๋ก ๊ฐ ์ธ์
๋ฉ๋ชจ๋ฆฌ๋ฅผ ๊ตฌ๋ถํด์ ์ฌ์ฉํฉ๋๋ค.
(์ค์ ํฐ์บฃ์ ์ธ์
์์ฑ ์ฝ๋๊ฐ ๊ถ๊ธํ๋ค๋ฉด https://semtax.tistory.com/92 ์ฐธ๊ณ )
๋ฌผ๋ก ์ธ์
๋ฉ๋ชจ๋ฆฌ๋ ์๋ฒ์ ๋ถ๋ด์ด ๋๊ธฐ๋๋ฌธ์, ๋ณดํต์ ๋ง์ง๋ง ์์ฒญ์์๋ถํฐ 30~60๋ถ์ ๋๋ง ์ ์ง๋๋๋ก ๋ง๋ค์ด๋ก๋๋ค.
๐ SecurityContextHolder๋
๐SecurityContext
- ๊ฐ์ฒด๊ฐ ์ ์ฅ๋๋ ๋ณด๊ด์๋ก ํ์ ์ ์ธ์ ๋ ์ง Authentication ๊ฐ์ฒด๋ฅผ ๊บผ๋ด์ด ์ธ ์ ์๋๋ก ์ ๊ณต๋๋ ํด๋์ค
- ThreadLocal ์ ์ ์ฅ๋์ด ์๋ฌด ๊ณณ์์๋ ์ฐธ์กฐ๊ฐ ๊ฐ๋ฅํ๋๋ก ์ค๊ณํจ
- ์ธ์ฆ์ด ์๋ฃ๋๋ฉด ์ธ์ ์ ์ฅ์(HttpSession) ์ ์ ์ฅ๋์ด ์ดํ๋ฆฌ์ผ์ด์ ์ ๋ฐ์ ๊ฑธ์ณ ์ ์ญ์ ์ธ ์ฐธ์กฐ๊ฐ ๊ฐ๋ฅํ๋ค
๐SecurityContextHolder
SecurityContext ์ ์ฅ์์ด๋ค. Http ์ธ์ ์ ์๋ S.C๋ฅผ ๊บผ๋ด Holder์ ์ ์ฅํด๋๋ค.
์คํ๋ง ์ํ๋ฆฌํฐ์ ์๋ Holder ๊ฐ์ฒด๋ฅผ ์ด์ฉํด์ ์์น์ ์๊ด์์ด ์ ์ญ์ ์ผ๋ก SecurityContext์ ์ ๊ทผ์ด ๊ฐ๋ฅํ๋ค.
SecurityContext์ ๋ํด ์๋์ ๊ฐ์ด 3๊ฐ์ง ์ ์ฅ๋ฐฉ๋ฒ์ ์ ๊ณตํ๋ค.
import org.springframework.security.core.context.SecurityContextHolder;
SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHEADLOCAL);
- MODE_THREADLOCAL(๊ธฐ๋ณธ๊ฐ) : ์ค๋ ๋๋น SecurityContext ๊ฐ์ฒด๋ฅผ ํ ๋น
- MODE_INHERITABLETHREADLOCAL : ๋ฉ์ธ ์ค๋ ๋์ ์์ ์ค๋ ๋์ ๊ดํ์ฌ ๋์ผํ SecurityContext ๋ฅผ ์ ์ง
- MODE_GLOBAL : ์์ฉ ํ๋ก๊ทธ๋จ์์ ๋จ ํ๋์ SecurityContext๋ฅผ ์ ์ฅํ๋ค (โก Static ๋ณ์ ์ฌ์ฉ)
MODE_INHERITABLETHREADLOCAL๋ ๋ถ๋ชจ์ค๋ ๋, ์์์ค๋ ๋๋ ์์จ๋ดค๋ค๋ฉด ์์ํ ์๋ ์๋๋ฐ
ํ ์ค๋ ๋(๋ถ๋ชจ)์์์ ์๋ก์ด ์ค๋ ๋(์์)์ ๋ง๋๋ ๊ฒฝ์ฐ๋ฅผ ๋งํ๋ค. ThreadLocal์ ์ค๋ ๋๋ง๋ค ์ฃผ์ด์ง๋ ๋ฉ๋ชจ๋ฆฌ์ด๋ฏ๋ก, ๋น์ฐํ ๋ณ๋์ ์ค์ ์์ด๋ ๋ถ๋ชจ์ ์์์ SecurityContext๊ฐ ๊ณต์ ๋์ง ์๋๋ค.
@GetMapping("/home")
public String helloHome(){
// ์์ ์ค๋ ๋ ์์ฑ
new Thread(
new Runnable(){
@Override
public void run(){
// ์๋ ๋ฉ์๋๋ ๋ค๋ฅธ Context๋ฅผ ๋ฐํํ๋ค. ํ์ฌ ์ค๋ ๋์ ๊ณต์ ๋์ง ์๋๋ค.
// ์ฆ ๋ฐฉ๊ธ ๋ก๊ทธ์ธ์ ํด์ ์ธ์
์ ์ ์ฅ๋์์ด๋ ์ฌ๊ธฐ์์๋ ์ฌ์ฉํ ์ ์๋ค.
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
}
}
).start();
}
์ด๋ฅผ ๊ทธ๋ฆผ์ผ๋ก ๊ทธ๋ ค๋ณด๋ฉด, ์๋์ ๊ฐ๋ค.
์์์๋ ์ธ๊ธํ์ง๋ง, SecurityContextHolder์์ ์๋ฌด๋์๋ ์ธ์ฆ๊ฐ์ฒด๋ฅผ ๊บผ๋ด์ฌ ์ ์๋ค.
Authentication authentication = SecurityContextHolder.getContext().getAuthentication()
์ด๋ฆ ๊ทธ๋๋ก Security Context ์ ์ฅ์์ด๋ฉฐ, ์๋์ ๊ฐ์ด clear๋ฉ์๋๋ก ํ์ฌ ์ปจํ ์คํธ๋ฅผ ๋ ๋ ค๋ฒ๋ฆด ์๋ ์๋ค.
SecurityContextHolder.clearContext() // SecurityContext ๊ธฐ์กด ์ ๋ณด ์ด๊ธฐํ
์ต๋ช ์ฌ์ฉ์
- ์๋ก์ด SecurityContext๋ฅผ ์์ฑํ์ฌ Holder์ ์ ์ฅํ๋ค.
- AnonymousAuthentication-Filter์์ AnonymousAuthentication-Token ๊ฐ์ฒด๋ฅผ SecurityContext์ ์ ์ฅํ๋ค.
์ธ์ฆ ์
- ์๋ก์ด SecurityContext๋ฅผ ์์ฑํ์ฌ Holder์ ์ ์ฅํ๋ค.
- UsernamePasswordAuthenticationFilter์์ ์ธ์ฆ ์ฑ๊ณต ํ, S.C์ usernmaePasswordAuthentication์ ์ ์ฅ
- ์ธ์ฆ์ด ์ต์ข ์๋ฃ๋๋ฉด S.C PesistenceFilter๊ฐ ์ธ์ ์ S.C๋ฅผ ์ ์ฅํ๋ค. (SPRING_SECURITY_CONTEXT : S.C ๊ฐ์ฒด)
์ธ์ฆ ํ
- ์ธ์ ์์ S.C๋ฅผ ๊บผ๋ด์ด Holder์ ์ ์ฅํ๋ค.
- S.C์์์ Authentication ๊ฐ์ฒด๊ฐ ์กด์ฌํ๋์ง ํ๋จํ๊ณ , ์๋ค๋ฉด ์ธ์ฆ ๊ณผ์ ์์ด ์ธ์ฆ์ ์ ์งํ๋ค.
์ฐธ๊ณ ๋ก MODE_GLOBAL ๋ชจ๋๊ฐ ์๋๋ผ๋ฉด, Http ์๋ต์ดํ์๋ ํ์์์ผ๋ฏ๋ก Holder์์ S.C๋ฅผ ์ญ์ ํ๋ค.
SecurityContextHolder.clearContext() // ์ธ์
์๋ ๋จ์์์. ํ์ฌ ์ค๋ ๋ ๋ฉ๋ชจ๋ฆฌ ์ญ์
๊ทธ๋์ ์ค์ ํํฐ์์๋ 2๋ฒ์งธ์ ์์น๋์ด์๋ค.
๐ ์ธ์ฆ(Authentication) Flow
- ๋ชจ๋ ์์ฒญ์ Manager๋ฅผ ํตํด ์งํ๋๋ค. ํ์ง๋ง Manager๋ ์ผ์ข ์ ์ปจํธ๋กค๋ฌ์ด๊ณ , ์ค์ ์ธ์ฆ์ Provider์ ์์ํ๋ค.
- Provider๋ ์ธ์ฆ๊ณผ์ ์ ๋ด๋นํ๋ฉฐ, ์ ์ ๋ฐ์ดํฐ ์กฐํ๊ฐ ํ์ํ๋ค๋ฉด UserDetailsService์ UserDeatils๊ฐ์ฒด๋ฅผ ์์ฒญํ๋ค.
- UserDetailsService๋ Repository์๊ฒ ๋ฐ์ดํฐ๋ฅผ ์์ฒญํ๋ ์๋น์ค๊ฐ์ฒด์ด๋ฉฐ, User๋ฅผ UserDetails๋ก ๊ฐ์ธ ๋ฐํํ๋ค.
- ์ด๋ ๊ฒ Provider๊ฐ UserDetails๋ฅผ ๋ฐ์ ์ธ์ฆ๊ณผ์ ์ ๊ฑฐ์ณ, ์ธ์ฆ์ ์คํจํ๋ค๋ฉด ์ ์ ํ ์์ธ๋ฅผ ๋ฐ์์ํจ๋ค.
- ์ธ์ฆ์ ์ฑ๊ณตํ๋ค๋ฉด Provider๋ UserDetails์ ์ธ๊ฐ์ ๋ณด(authorities)๋ฅผ ํ ๊ฐ์ฒด Authentication์ผ๋ก ๋ง๋ค์ด ๋ฐํํ๊ณ
- ์ต์ข ์ ์ผ๋ก Manager๊ฐ Filter์ Authentication๊ฐ์ฒด๋ฅผ ๋ฐํํ๋ฉด, ํํฐ์์ S.C์ ์ธ์ฆ ๊ฐ์ ์ต์ข ์ ์ผ๋ก ์ ์ฅํ๋ค.
์ด๋ ๊ฒ ์ธ์ฆ์ด ์๋ฃ๋ Autentication์ ํ์ฌ Holder์ S.C์ ์ ์ฅ๋๊ณ , S.C๋ ์ธ์ ์ ์ ์ฅ๋๋ค.
์ดํ ์๋ก์ด ์์ฒญ์ด ๋ค์ด์ฌ ๋, Holder๊ฐ S.C๋ฅผ ์ธ์ ์์ ๊บผ๋ด์์ ์ฌ์ฉํ๊ณ , ์๋ต์ดํ S.C๋ฅผ clearํ๋ค.
์ฆ S.C๋ Http ์์ฒญ-์๋ต๊ณผ ๊ฐ์ ์๋ช ์ฃผ๊ธฐ๋ฅผ ๊ฐ์ง๋ฉฐ ์๋ฒ์ ์๋ Holder๋ฅผ ํตํด ๊ฐ์ ธ์ค๊ณ , ์ ์ฅํ๋ค.
์ฐธ๊ณ ๋ก AuthenticationProvider์์ ์ ์ ๊ฒ์ฆ ์คํจ์, ์๋์ ๊ฐ์ ์์ธ๋ฅผ ๋ฐ์์ํจ๋ค.
๐ ์ธ์ฆ ๋งค๋์ (AuthenticationManager)
๋งค๋์ ๋ ์ผ์ข ์ ์ปจํธ๋กค๋ฌ๋ผ๊ณ ํ๋ค. ์ค์ ๋์์ ProviderManager๋ฅผ ํตํด ์งํ๋๋ค.
์๋ฅผ ๋ค์ด Form ์ธ์ฆ์์ฒญ์ด ๋ค์ด์๋ค๋ฉด, ์ด ์ธ์ฆ์ด ๊ฐ๋ฅํ Provider๊ฐ์ฒด(Dao..Provider)๋ฅผ ์ฐพ์์ ์ธ์ฆ์ ์ฒ๋ฆฌํ๋ค.
์ ํ๋ Provider ๊ฐ์ฒด๊ฐ ์ธ์ฆ์ ์ฑ๊ณตํ๋ฉด ๋งค๋์ ์๊ฒ Authentication ๊ฐ์ฒด์๊ฒ ๋ฐํํ๋ ํ์์ด๋ค.
ProviderManager์๋ parent ์์ฑ ๊ฐ์ ๋ฃ์ ์ ์๋ค. (*์์ ์๋)
์ด๋ ํ์ฌ ProviderManager์ ์ฒ๋ฆฌ๊ฐ๋ฅํ Provider๊ฐ ์๋ค๋ฉด, ํด๋น parent๊ฐ์ ์๋ ProvierManager์๊ฒ ์์ฒญ์ ํ๋ค.
์ฐธ๊ณ ๋ก ์คํ๋ง ์ํ๋ฆฌํฐ ์ด๊ธฐํ ๊ณผ์ ์์, ๊ธฐ๋ณธ๊ฐ์ ๋ชจ๋ provider๋ฅผ ํ์ํ์ง ์๋๋ก ๋ง๋ค์ด์ ธ์๋ค.
AuthenticationManagerBuilder๋ฅผ ์ฌ์ฉํด์ ์ด๋ฅผ ๋ณ๊ฒฝํ๋ฉด parent๊ฐ ์ฐ๊ฒฐ๋์ด ๋ชจ๋ Provider๋ฅผ ํ์ํ๋๋ก ๋ฐ๊ฟ ์ ์๋ค.
๐ ์ธ์ฆ ์ฒ๋ฆฌ์ (AuthenticationProvider)
Filter โก Manager โก Provider.support(~) ๋ฅผ ํตํด ์ ์ ํ ๊ฐ์ฒด๋ฅผ ์ฐพ์๋ค๋ฉด provider์์๋ ์๋์ ๊ฐ์ด ๋์๋๋ค.
ํน๋ณํ๊ฑด ์๊ณ , Provider๋ User๊ฐ์ฒด๋ฅผ ์ง์ ๋ค๋ฃจ๋๊ฒ ์๋๋ผ, UserDetails ๊ฐ์ฒด๋ก ๋ค๋ฃจ๊ฒ ๋๋ค.
๋ชจ๋ ๊ฒ์ฆ์ด ์๋ฃ๋๋ฉด Authentication ๊ฐ์ฒด๋ฅผ ๋ง๋ค์ด ์ ์ ์ ๋ณด(Principal)์ ๊ถํ์ ๋ณด๋ฅผ ๋ด์ ๋ฐํํ๋ค.
๐ญ ์ธ๊ฐ(Authorization)
์ธ์ฆ์ด ๋ด๊ฐ ๋๊ตฌ์ธ์ง ํ์ธํ๋ ๊ณผ์ ์ด์๋ค๋ฉด,
์ธ๊ฐ๋ ๋๊ตฌ์ธ์ง ํ์ธ์ ํ์ผ๋ ๊ถํ์ด ์๋์ง ํ์ธํ๋ ๊ณผ์ ์ด๋ค.
์คํ๋ง ์ํ๋ฆฌํฐ๋ ํฌ๊ฒ 3๊ฐ์ง ๊ณ์ธต์ผ๋ก Authorization์ ์ ๊ณตํด์ค๋ค.
- ์น ๊ณ์ธต์ ์ฐ๋ฆฌ๊ฐ ํํ ์ฌ์ฉํ๋ URL, ์ฆ ํ๋ฉด ๋จ์์ ๊ถํ์ ์ง์ ํ๋ ๊ฒ์ ๋งํ๋ค.
- ์๋น์ค ๊ณ์ธต์ AOP์ ์ธํฐ์ ํฐ๋ฅผ ์ด์ฉํ ๊ธฐ๋ฅ ๋จ์์ ๋ณด์๊ธฐ๋ฅ์ด๋ค. โก ์๋น์ค ๋ฉ์๋์ ๊ถํ์ ์ค์ ํ ์ ์๋ค.
- ๋๋ฉ์ธ ๊ณ์ธต์ ๊ฐ์ฒด๋จ์์ ๋ณด์ ๊ธฐ๋ฅ์ด๋ค. โก File, DB์ ์ ๊ทผ๊ถํ์ ์ค์ ํ ์ ์๋ค.
์ธ๊ฐ๋ ์คํ๋ง ์ํ๋ฆฌํฐ ํํฐ์ฒด์ธ์ค, ๊ฐ์ฅ ๋ง์ง๋ง ํํฐ์ธ FilterSecurityInterceptor ํํฐ์์ ์ด๋ฃจ์ด์ง๋ค.
- [์์ ํํฐ๋ค์์ ์ธ์ฆ๋ ์ฌ์ฉ์]์ ๋ํ์ฌ ํน์ ์์ฒญ์ ์น์ธ/๊ฑฐ๋ถ ์ฌ๋ถ๋ฅผ ์ต์ข
์ ์ผ๋ก ๊ฒฐ์
โก ์ธ์ฆ ๊ฐ์ฒด ์์ด ๋ณดํธ์์์ ์ ๊ทผ์ ์๋ํ ๊ฒฝ์ฐ AuthenticationException ์ ๋ฐ์
โก ์ธ์ฆ ํ ์์์ ์ ๊ทผ ๊ฐ๋ฅํ ๊ถํ์ด ์กด์ฌํ์ง ์์ ๊ฒฝ์ฐ AccessDeniedException ์ ๋ฐ์ - ๊ถํ ์ ์ด ๋ฐฉ์ ์ค HTTP ์์์ ๋ณด์์ ์ฒ๋ฆฌํ๋ ํํฐ
- ๊ถํ ์ฒ๋ฆฌ๋ฅผ AccessDecisionManager์๊ฒ ๋งก๊น
๐ FilterSecurityInterceptor ์ ๋์
์์์ ์ธ์ฆ๊ฐ์ฒด๊ฐ ๋ง๋ค์ด์ก๋ค๋ฉด, SecurityMetadataSource ๊ฐ์ฒด๋ฅผ ํตํด ํ์ํ ๊ถํ์ ๋ณด๋ฅผ ๊ฐ์ ธ์จ๋ค.
๊ทธ๋ฆฌ๊ณ AccessDecisionManager์๊ฒ ๊ถํ์ ๋ณด๋ฅผ ์ฃผ๊ฒ๋๋ฉด, ํ์ฌ ์ธ์ฆ๊ฐ์ฒด๊ฐ ๊ถํ์ด ์๋์ง๋ฅผ ํ๋จํ๋ค.
๋ฌผ๋ก ์ธ์ฆ๊ณผ ๋น์ทํ๊ฒ ๋งค๋์ ๋ ์ปจํธ๋กค๋ฌ ์ญํ ์ด๋ฉฐ, ์ค์ ์ธ๊ฐ๋ AccessDecisionVoter ๊ฐ์ฒด๊ฐ ๋ด๋นํ๋ค.
์ ๊ทผ์ด ๊ฑฐ๋ถ๋๋ฉด FilterSecurityInterceptor๋ฅผ ํธ์ถํ ExceptionTranslationFilter์ ์์ธ๋ฅผ ๋ฐํํ๋ค.
๐ ์ธ๊ฐ๋งค๋์ (AccessDecisionManager)
ํ๋์ ๋งค๋์ ๋ ์ฌ๋ฌ๊ฐ์ Voter ๊ฐ์ฒด๋ฅผ ๊ฐ์ง๋ฉฐ, ์ธ๊ฐ(์ ๊ทผํ์ฉ, ๊ฑฐ๋ถ, ๋ณด๋ฅ)์ ํด๋นํ๋ ๋ฆฌํด ๊ฐ์ ๋ฐ๊ณ ์ต์ข ํ๋จํจ.
๐ AccessDecisionVoter
์๋์ ์ ๋ณด๋ฅผ ๋ฐํ์ผ๋ก ๊ถํ์ ํ๋จํ๋ฉฐ, ์์๊ฐ์ ๋ฐํํจ (์ ๊ทผํ์ฉ1, ์ ๊ทผ๊ฑฐ๋ถ0, ์ ๊ทผ๋ณด๋ฅ-1)
- Authentication ์ธ์ฆ ์ ๋ณด( user )
- FilterInvocatior ์์ฒญ ์ ๋ณด( antMatcher("/user") )
- ConfigAttributes ๊ถํ ์ ๋ณด ( hasRole("USER") )
์ฐธ๊ณ ๋ก ์๋์ ๊ฐ์ ์์ ๊ฐ์ ๊ฐ์ง๊ณ ์์.
- ACCESS_GRANTED (1) ์น์ธ
- ACCESS_DENIED (0) ๊ฑฐ์
- ACCESS_ABSTAIN (-1) ๋ณด๋ฅ
๋ณด๋ฅ๋ ํด๋น Voter๊ฐ ํ๋จ์ ๋ด๋ฆด ์ ์๋ ๊ฒฝ์ฐ ์ฌ์ฉํจ
antMatcher(String antPattern)// HttpSecurity์ ๊ณต๋ ant ํจํด๊ณผ ์ผ์นํ ๋๋ง ํธ์ถ๋๋๋ก ๊ตฌ์ฑํ ์ ์์ต๋๋ค.
mvcMatcher(String mvcPattern)// HttpSecurity์ ๊ณต๋ Spring MVC ํจํด๊ณผ ์ผ์นํ ๋๋ง ํธ์ถ๋๋๋ก ๊ตฌ์ฑํ ์ ์์ต๋๋ค.
MVC Matcher(์คํ๋ง 4.1.1)๋ SpringMVC์ HandlerMappingIntrosepctor๋ฅผ ์ฌ์ฉํฉ๋๋ค. a
Ant Matcher(์คํ๋ง 3.1)๋ ์ํ์น์ Ant ๋ฌธ๋ฒ์ ์ฌ์ฉํฉ๋๋ค. Ant ์๋ ๋น๋๋๊ตฌ์ด๊ณ , bulid.xml์ ์ฌ์ฉ๋๋ ๋ฌธ๋ฒ์
๋๋ค.
์ ๋๊ฐ์ Matcher๋ RequestMatcher ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ๊ณ ์์ต๋๋ค.
์ผ๋ฐ์ ์ผ๋ก Mvc Matcher๊ฐ ๋ ์์ ํฉ๋๋ค. API ์๋ฒ๊ฐ ์๋๋ผ๋ฉด Mvc Matcher๋ฅผ ์ฌ์ฉํ๋ฉด ๋ฉ๋๋ค.
antMatchers("/secured") // ํด๋น URL๊ณผ ์ ํํ ์ผ์นํด์ผ ํฉ๋๋ค.
mvcMatchers("/secured") // /secured, /secured/ /secured.html /secured.xyz...๋ฅผ ํฌํจํฉ๋๋ค
// ์ด๋ ์คํ๋ง MVC์ @RequestMapping(....)๊ณผ ๊ฐ์ URL ๊ท์น์
๋๋ค.
antMatcher("/users/**") //matches any path starting with /users
antMatchers("/users") //matches only the exact /users URL
mvcMatchers("/users") //matches /users, /users/, /users.html
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/users/movie/**") // matches any path starting with /users/movie
.hasRole("ADMIN") ...
}
}
๐ญ ์คํ๋ง ์ํ๋ฆฌํฐ ์ํคํ ์ฒ ์ ๋ฆฌ
๋ง์ง๋ง FilterSecurityInterceptor์์ ๊ถํ(์ธ๊ฐ)๊น์ง ์์ธ์์ด ์๋ฃ๋๋ฉด ์ ์์ ์ธ ์ ๊ทผ์ด๋ผ๊ณ ์๊ฐํ๋ฉด ๋๋ค.
'๐ฑ Spring Framework > Spring Security' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
Spring Security #2 ๋ค์ํ ์ฌ์ฉ๋ฒ - ์ต๋ช ์ฌ์ฉ์, ์ธ์ ์ ์ด, ์์ธ์ฒ๋ฆฌ (0) | 2021.11.23 |
---|---|
Spring Security #1 ๊ธฐ๋ณธ๋์ (0) | 2021.11.22 |
Spring Security๋ ๋ฌด์์ผ๊น (1) | 2021.09.23 |
๋ธ๋ก๊ทธ์ ์ ๋ณด
JiwonDev
JiwonDev