๐ํ๋ก๊ทธ๋๋จธ์ค ๋ฐฑ์๋ ๋ฐ๋ธ์ฝ์ค 4๊ธฐ ๊ต์ก๊ณผ์ ์ ๋ฃ๊ณ ์ ๋ฆฌํ ๊ธ์
๋๋ค.๐
Architecture
๊ทธ๋ฆผ ์ถ์ฒ
- AuthenticationManager: ์ฌ์ฉ์ ์ธ์ฆ ๊ด๋ จ ์ฒ๋ฆฌ
- AccessDecisionManager: ์ฌ์ฉ์๊ฐ ๋ณดํธ๋ฐ๋ ๋ฆฌ์์ค์ ์ ๊ทผํ ์ ์๋ ์ ์ ํ ๊ถํ์ด ์๋์ง ํ์ธ
์ฌ์ฉ์๋ฅผ ์ธ์ฆํ๊ณ , ์ธ์ฆ๋ ์ฌ์ฉ์๊ฐ ๋ฆฌ์์ค์ ์ ๊ทผํ ์ ์๋ ์ ์ ํ ๊ถํ์ด ์๋์ง ํ์ธํ๊ณ , ๋ง์ฝ ์ฌ์ฉ์๊ฐ ์ ์ ํ ์ธ๊ฐ๊ฐ ์์ผ๋ฉด AccessDecisionManager์์ 403 Forbidden ์๋ฌ๋ฅผ ๋ธ๋ค.
FilterChainProxy
๊ทธ๋ฆผ ์ถ์ฒ
- Spring Security์ ์ค์ ์ ์ธ ๊ตฌํ์ ์๋ธ๋ฆฟ ํํฐ(javax.servlet.Filter)๋ฅผ ํตํด ์ด๋ฃจ์ด์ง
- FilterChainProxy ์ธ๋ถ ๋ด์ฉ์ SecurityFilterChain ๋น์ ํตํด ์ค์
- ์น ์์ฒญ์ ์ด๋ฌํ FilterChain์ ์ฐจ๋ก๋ก ํต๊ณผํ๋ฉด์ ์ ์ ํ๊ฒ ๋์ํจ
- ๊ฐ ํํฐ๋ ์น ์์ฒญ์ ๋ฐ๋ผ ๋์ ์ฌ๋ถ๋ฅผ ๊ฒฐ์ ํ ์ ์๊ณ , ๋์์ด ํ์ ์๋ค๋ฉด ๋ค์ ํํฐ๋ก ๋๊น
- ์์ฒญ์ ์ฒ๋ฆฌํ๊ณ ์๋ต์ ๋ฐํํ๋ฉด ํํฐ ์ฒด์ธ ํธ์ถ ์คํ์ ๋ชจ๋ ํํฐ์ ๋ํด ์ญ์์ผ๋ก ์งํ
์น ์์ฒญ์ ์ด๋ป๊ฒ FilterChainProxy๋ก ์ ๋ฌ๋ ๊น?
DelegatingFilterProxy
- ์น ์์ฒญ์ ์์ ํ ์๋ธ๋ฆฟ ์ปจํ
์ด๋๋ ํด๋น ์์ฒญ์ DelegatingFilterProxy๋ก ์ ๋ฌํจ
- DelegatingFilterProxy Bean์ SecurityFilterAutoConfiguration ํด๋์ค์์ ์๋์ผ๋ก ๋ฑ๋ก๋จ
- DelegatingFilterProxy๋ ์ค์ ์ ์ผ๋ก ์น ์์ฒญ์ ์ฒ๋ฆฌํ Target Filter Bean์ FilterChainProxy๋ก ์ง์ ํจ
์ฆ, ์น ์์ฒญ์ FilterChain์ ํํฐ๋ค์ ํต๊ณผํ๋ค๊ฐ DelegatingFilterProxy์ ๋๋ฌํ๋ฉด, FilterChainProxy๋ก ์ ๋ฌ๋์ด ์ฒ๋ฆฌ๋๋ ๊ฒ์ด๋ค.
FilterChainProxy๋ฅผ ๊ตฌ์ฑํ๋ Filter ๋ชฉ๋ก
RequestCacheAwareFilter
- ์ธ์ฆ ์์ฒญ์ ์ํด ๊ฐ๋ก์ฑ์ด์ง ์๋ ์์ฒญ์ผ๋ก ์ด๋ํ๋ ํํฐ
์ํฉ
- ์ต๋ช
์ฌ์ฉ์๊ฐ ๋ณดํธ(์ธ์ฆ, ์ธ๊ฐ) ๋ฐ๋ ๋ฆฌ์์ค์ ์ ๊ทผ
- ์ ๊ทผ ๊ถํ์ด ์๊ธฐ ๋๋ฌธ์ FilterSecurityInterceptor์์ ์ ๊ทผ ๊ฑฐ๋ถ ์์ธ๊ฐ ๋ฐ์
- ExceptionTranslationFilter๊ฐ ๋ฐ์ํ ์ ๊ทผ ๊ฑฐ๋ถ ์์ธ๋ฅผ ์ฒ๋ฆฌ
- ํ์ฌ ์ฌ์ฉ์๊ฐ ์ต๋ช
์ฌ์ฉ์๋ผ๋ฉด, ํด๋น ์์ฒญ์ ์บ์์ฒ๋ฆฌํ๊ณ , ๋ก๊ทธ์ธ ํ์ด์ง๋ก ์ด๋ ์ํด
- ๋ก๊ทธ์ธ ํ์ด์ง์์ ์ ์ ๋ก๊ทธ์ธ
- RequestCacheAwareFilter๊ฐ ์บ์๋ ์์ฒญ์ ์ฒ๋ฆฌ
- ์บ์๋ ์์ฒญ์ด ์๋ค๋ฉด ์บ์๋ ์์ฒญ์ ์ฒ๋ฆฌํ๊ณ , ์บ์๋ ์์ฒญ์ด ์๋ค๋ฉด ํ์ฌ ์์ฒญ์ ์ฒ๋ฆฌ
- ex) ์ด์ ์ ์์ฒญํ๋ ํ์ด์ง๋ก redirect ๋๋ API ํธ์ถ
ChannelProcessingFilter
- ์ ์ก ๋ ์ด์ด ๋ณด์์ ์ํด SSL ์ธ์ฆ์๋ฅผ ์์ฑํ๊ณ , ์ด๋ฅผ Spring Boot ์น ์ดํ๋ฆฌ์ผ์ด์
์ ์ ์ฉํ๋ ํํฐ
HTTPS
- ํด๋ผ์ด์ธํธ์ ์๋ฒ๊ฐ ์ฃผ๊ณ ๋ฐ๋ ๋ชจ๋ ๋ฐ์ดํฐ๋ฅผ ์ํธํ
- ๋ฐ์ดํฐ ์ํธํ๋ฅผ ์ํด SSL์ ์ฌ์ฉ
- SSL ์ํธํ๋ฅผ ์ํด SSL ์ธ์ฆ์๊ฐ ํ์ํจ
HTTPS ๋์
- ํด๋ผ์ด์ธํธ๊ฐ ์๋ฒ์ ์ ์ ์, ์๋ฒ๋ ์ ๋ขฐํ ์ ์๋ ์ธ์ฆ๊ธฐ๊ด์ผ๋ก๋ถํฐ ๋ฐ๊ธ๋ SSL/TLS ์ธ์ฆ์๋ฅผ ์ ๊ณตํ๊ณ , ์ด ์ธ์ฆ์์๋ ์๋ฒ์ ๊ณต๊ฐํค์ ์๋ฒ ์ ๋ณด๊ฐ ํฌํจ๋จ
- ํด๋ผ์ด์ธํธ๋ ์๋ฒ๊ฐ ์ ๊ณตํ ์ธ์ฆ์๋ฅผ ํ์ธํ๊ณ , ์ธ์ฆ์์ ํฌํจ๋ ๊ณต๊ฐํค๋ฅผ ์ฌ์ฉํ์ฌ ์๋ฒ๋ฅผ ๊ฒ์ฆํ๋ฉฐ, ์ด๋ฌํ ๊ณผ์ ์ SSL/TLS ํธ๋์
ฐ์ดํฌ๋ผ๊ณ ํจ
- ํด๋ผ์ด์ธํธ๋ ์๋ฒ์ ๊ณต๊ฐํค๋ฅผ ์ฌ์ฉํ์ฌ ๋๋คํ ๋์นญ ์ํธํ ํค(์ธ์
ํค)๋ฅผ ์ํธํํ์ฌ ์๋ฒ๋ก ์ ์ก
- ์๋ฒ๋ ์์ ์ ๊ฐ์ธํค๋ฅผ ์ฌ์ฉํ์ฌ ํด๋ผ์ด์ธํธ๋ก๋ถํฐ ์ ์ก๋ ์ํธํ๋ ๋์นญ ์ํธํ ํค(์ธ์
ํค)๋ฅผ ๋ณตํธํ
- ์ด๋ ๊ฒ ์๋ฒ์ ํด๋ผ์ด์ธํธ๋ ์๋ก ์ฝ์ํ ๋์นญ ์ํธํ ํค(์ธ์
ํค)๋ฅผ ์ฌ์ฉํ์ฌ ํต์ ์ ์ํธํํจ
SSL ์ธ์ฆ์ ์์ฑ
- SSL ์ธ์ฆ์๋ฅผ ๋ฐ๊ธ๋ฐ๊ธฐ ์ํด์๋ ๋๋ฉ์ธ๊ณผ ๋ฐ๊ธ ๋น์ฉ์ด ๋ค๊ธฐ๋๋ฌธ์ ํ
์คํธ ํ๊ฒฝ(๊ฐ๋ฐ ํ๊ฒฝ)์์๋ java์ keytool ๋๊ตฌ๋ฅผ ์ฌ์ฉํด์ ์์๋ก ์์ฑ
- ์ค์ ์๋น์ค์๋ ์ฌ์ฉํ ์ ์์
3๊ฐ์ง ์์
์ด ํ์
- keystore ๋ง๋ค๊ธฐ: keytool -genkey -alias prgrms_keystore -keyalg RSA -storetype PKCS12 -keystore prgrms_keystore.p12
- keystore์์ ์ธ์ฆ์ ์ถ์ถํ๊ธฐ: keytool -export -alias prgrms_keystore -keystore prgrms_keystore.p12 -rfc -file prgrms.cer
- trust-store ๋ง๋ค๊ธฐ: keytool -import -alias prgrms_truststore -file prgrms.cer -keystore prgrms_truststore.p12
SSL ์ธ์ฆ์ ์ ์ฉ
server:
port: 443
ssl:
enabled: true
key-alias: prgrms_keystore
key-store: classpath:prgrms_keystore.p12
key-store-password: ์ค์ ํ ๋น๋ฐ๋ฒํธ
key-password: ์ค์ ํ ๋น๋ฐ๋ฒํธ
trust-store: classpath:prgrms_truststore.p12
trust-store-password: ์ค์ ํ ๋น๋ฐ๋ฒํธ
- ์์์ ์ง์ ๋ง๋ prgrms_keystore.p12, prgrms_truststore.p12, 2๊ฐ ํ์ผ์ resources ๋๋ ํ ๋ฆฌ๋ก ๋ณต์ฌ ํ application.yml ์ค์
Spring Security ์ค์
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.requiresChannel(auth -> auth
.anyRequest().requiresSecure())
.build();
}
- ๊ธฐ์กด SecurityFilterChain ๋น์ ChannelProcessingFilter ์ค์
- http.requiresChannel()๋ URL์ด ์์ฒญ๋ ํ๋กํ ์ฝ(HTTP ๋๋ HTTPS)๊ณผ ์ผ์นํ๋์ง ๊ฒ์ฌ
AnonymousAuthenticationFilter
- ์ต๋ช
์ฌ์ฉ์๋ฅผ ๋ํ๋ด๋ ์ธ์ฆ ๊ฐ์ฒด๋ฅผ ์์ฑํ๋ ํํฐ
- ํด๋น ํํฐ์ ์์ฒญ์ด ๋๋ฌํ ๋๊น์ง ์ฌ์ฉ์๊ฐ ์ธ์ฆ๋์ง ์์๋ค๋ฉด, ์ฌ์ฉ์๋ฅผ null ๋์ Anonymous ์ธ์ฆ ํ์
์ผ๋ก ํํ
- ์ฌ์ฉ์๊ฐ null์ธ์ง ํ์ธํ๋๊ฒ๋ณด๋ค ๊ตฌ์ฒด์ ์ธ ํ์
์ผ๋ก ํ์ธํ ์ ์๋๋กํจ
- ์ธ์ฆ๋์ง ์์ ์ฌ์ฉ์๋ฉด, "principal"๊ณผ "authorities"๋ก ์ธ์ฆ๋์ง ์์ ์ฌ์ฉ์๋ฅผ ๋ํํ๋ ์ธ์ฆ ๊ฐ์ฒด๋ฅผ ์์ฑ
Spring Security ์ค์
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.anonymous(auth -> auth
.principal("thisIsAnonymousUser")
.authorities("ROLE_ANONYMOUS", "ROLE_UNKNOWN"))
.build();
}
ExceptionTranslationFilter
- SecurityFilterChain์์ FilterSecurityInterceptor ๋ฐ๋ก ์์ ์์นํ๋ฉฐ, ์ธ์ฆ ๊ณผ์ (FilterSecurityInterceptor ์คํ)์์ ๋ฐ์ํ๋ ์์ธ๋ฅผ ์ฒ๋ฆฌ
- AuthenticationException(์ธ์ฆ ๊ด๋ จ ์์ธ)์ด๋ฉฐ, ์ด ์์ธ๋ฅผ ์ก์ ์ฌ์ฉ์๋ฅผ ๋ก๊ทธ์ธ ํ์ด์ง๋ก ๋ณด๋
- AccessDeniedException(์ ๊ทผ ๊ฑฐ๋ถ ์์ธ)์ด๋ฉฐ, ์ด ์์ธ๋ฅผ ์ก์ ์ ๊ทผ ๊ฑฐ๋ถ ํ์ด์ง๋ฅผ ๋ณด์ฌ์ฃผ๊ฑฐ๋ ์ฌ์ฉ์๋ฅผ ๋ก๊ทธ์ธ ํ์ด์ง๋ก ๋ณด๋
- ExceptionTranslationFilter๋ SecurityFilterChain์์ ์๊ธฐ ์๋์ ์ค๋ ํํฐ๋ค์์ ๋ฐ์ํ๋ ์์ธ๋ค์ ๋ํด์๋ง ์ฒ๋ฆฌ
- ๋ฐ๋ผ์ ์ปค์คํ
ํํฐ๋ฅผ ์ถ๊ฐํด์ผ ํ๋ ๊ฒฝ์ฐ ์ด๋ฅผ ๊ณ ๋ คํด์ผํจ
AccessDeniedException ํธ๋ค๋ฌ ์ค์
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.exceptionHandling(auth -> auth
.accessDeniedHandler(accessDeniedHandler()))
.build();
}
@Bean
public AccessDeniedHandler accessDeniedHandler() {
return (request, response, accessDeniedException) -> {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
Object principal = authentication != null ? authentication.getPrincipal() : null;
log.warn("{} is denied", principal);
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
response.setContentType("text/plain");
response.getWriter().write("## ACCESS DENIED ##");
response.getWriter().flush();
response.getWriter().close();
};
}
- ๊ธฐ๋ณธ์ ์ผ๋ก AccessDeniedException ์์ธ๋ฅผ ์ฒ๋ฆฌํ๊ธฐ ์ํ ํธ๋ค๋ฌ๊ฐ ๊ตฌํ๋์ด ์์
- org.springframework.security.web.access.AccessDeniedHandlerImpl ํด๋์ค
- ํ์ง๋ง ์์ ์ฝ๋์ ๊ฐ์ด ์ฌ์ฉ์ ์ ์ AccessDeniedHandler ์ค์ ๊ฐ๋ฅํจ
- ๊ธฐ์กด SecurityFilterChain ๋น์ ExceptionTranslationFilter ์ค์
- http.exceptionHandling()๋ ์์ธ ์ฒ๋ฆฌ๋ฅผ ์ค์