๐ ๋ฌธ์ ์ํฉ
๋ฐฐ๊ฒฝ
ํ๋ก์ ํธ์์ Request์์ RTT๊ฐ ์ค๋ ๊ฑธ๋ฆฌ๋ API๊ฐ ์์ด์
Request๊ฐ ๋ค์ด์ค๊ณ ํด๋น ์ฐ๋ ๋์์ ๋น๋๊ธฐ๋ก ์๋ก์ด ์ฐ๋ ๋๋ฅผ ๋ง๋ค์ด์ ์์ ์ ์์ํ๊ณ
Response๋ฅผ ๋ฐ๋ก ๋ด๋ฆฌ๋ ํ๋ก์ธ์ค๊ฐ ์์๋ค.
Spring Security๋ก Requestํ ์ ์ ธ์ ์ ๋ณด๋ฅผ Thread ๋จ์๋ก ๊ด๋ฆฌํ๊ณ ์์ด์
SecurityContextHolder.MODE_INHERITABLETHREADLOCAL ๋ก ์ค์ ํ์ฌ ๋ถ๋ชจ ์ฐ๋ ๋์์ ํ์๋ ์์ ์ฐ๋ ๋์ SecurityContext๊ฐ ์ ํ๋ ์ ์๋๋ก ์ค์ ํ์๋ค
์ํฉ - ๋น๋๊ธฐ ์ฐ๋ ๋์ SecurityContext(์ธ์ฆ์ ๋ณด)๊ฐ ์์ ๊ผฌ์ ๐
Local์์ ๊ฐ๋ฐํ ๋๋ Requestํ ์ ์ ธ์ ์ธ์ฆ์ ๋ณด๊ฐ ์๋ SecurityContext๊ฐ ๋น๋๊ธฐ ์ฐ๋ ๋๋ก ๋์ด๊ฐ๋๋ฐ
๊ฐ๋ฐ ์๋ฃํ๊ณ , Dev ์๋ฒ์ ๋ฐฐํฌํ๋ฉด ๋น๋๊ธฐ ์ฐ๋ ๋์ SecurityContext๊ฐ ๋ค๋ฅธ ์ ์ ธ์ธ ๊ฒ์ด๋ค!
์ฌ์ง์ด ๋งค๋ฒ ๊ฐ์๊ณ์ ์ผ๋ก Request ์ฌ๋ฌ๋ฒ ํ ๋๋ง๋ค ์ฌ๋ฌ๋ฒ ๋๋ค์ ์ ์ ธ๊ฐ ๋น๋๊ธฐ ์ฐ๋ ๋์ SecurityContext์์ ๋ฐ๊ฒฌ๋์๋ค!!!
(JVM์ธ๋ฐ Local์์๋ ๋๊ณ , Dev์์๋ ์๋๋ค?? ๊ฐ ์ฌ์ค ๋ง์ด ์๋๋ ๊ฑด๋ฐ ๋ค์์ ๋งํ์ง๋ง, ์ฌ์ค ์์ง์์ ์์๋ค)
๐ ๋ฌธ์ ์ Config
์์ ๊ฐ์ ๋ฌธ์ ์ ์ํฉ์์ ์ฐ๋ ๋ ๊ด๋ จ Config๋ฅผ ์ดํด๋ณด๋ฉด
Spring Security Mode
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SpringSecurityConfig {
@PostConstruct
public void init() {
SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);
}
...์๋ต
}
Spring Security๋ ์ธ์ฆ/์ธ๊ฐ ์ ๋ณด๋ฅผ SecurityContext์ Thread ๋ณ๋ก ๊ฐ์ง๊ฒ๋๋ค
์ข ๋ ์์ธํ ๋งํ๋ฉด
SecurityContext๋ SecurityContextStrategy๋ก Wrapping ๋์ด์ strategy ์์ ThreadLocal ํ์ ์ผ๋ก ์กด์ฌํ๋ค
Strategy๋ 4๊ฐ์ ๊ตฌํ์ฒด๊ฐ ์๋๋ฐ
Spring config ์์ ์ MODE๋ฅผ ํ๋ ์ ํํ๋ฉด ์ ๋ตํจํด์ผ๋ก strategy๊ฐ ์ ์ ์ด๋๋ค
๊ฐ strategy ๊ธฐ๋ฐ์ผ๋ก SecurityContext๋ฅผ ๊ด๋ฆฌํ๋ ๋ฐฉ์์ด ๋ค๋ฅด๋ค
- MODE_THREADLOCAL : ํ์ฌ ์ฐ๋ ๋์ ์ข ์๋ ThreadLocal ๊ฐ์ฒด์ SecurityContext ์ ์ฅ (default)
- MODE_INHERITABLETHREADLOCAL : ์์ฑ๋๋ ์์ ์ฐ๋ ๋๊ฐ ๋ถ๋ชจ ์ฐ๋ ๋์ SecurityContext ์์ ๋ฐ์ ์ ์์
- MODE_GLOBAL : ์ฑ ์ ์ฒด์ ํ๊ฐ์ SecurityContext๋ฅผ ์์ฑ ๊ณต์ for ๋จ์ผ ์ ์ ธ
- MODE_PRE_INITIALIZED : ์ฑ ์์ ๋ SecurityContext ๋ฏธ๋ฆฌ ์ด๊ธฐํ
-> ํ๋ก์ ํธ๋ ์์์ฐ๋ ๋๊ฐ ๋ถ๋ชจ ์ฐ๋ ๋์ SecurityContext๋ฅผ ๋ฐ์์ ๋น๋๊ธฐ ์์ ์ ํด์ผํ์ด์ MODE_INHERITABLETHREADLOCAL์ ์ ํํ๋ค
Async Config
@EnableAsync
@Configuration
public class AsyncConfig implements AsyncConfigurer {
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
int corePoolSize = Runtime.getRuntime().availableProcessors(); // CPU ์ฝ์ด ์์ ๋ง์ถฐ ์ค๋ ๋ ํ ํฌ๊ธฐ ์ค์
int maxPoolSize = corePoolSize * 2; // ์ต๋ ํ ํฌ๊ธฐ - CPU ์ฝ์ด ์์ ๋ ๋ฐฐ
int queueCapacity = 500; // ํ ์ฉ๋ ์ค์
executor.setCorePoolSize(corePoolSize);
executor.setMaxPoolSize(maxPoolSize);
executor.setQueueCapacity(queueCapacity);
executor.setThreadNamePrefix("AsyncExecutor-");
... ์ค๋ต
executor.initialize();
return executor
}
}
@Async ์ด๋ ธํ ์ด์ ์ด ๋ถ์ ๋ฉ์๋๊ฐ ํธ์ถ ์
์๋์ ์คํ์ Thread Pool์์
- Core Pool Size : ํญ์ ์ ์ง ๋๋ ์ต์ ์ค๋ ๋ ๊ฐ์ - CPU ์ฝ์ด์์ ๋ง๊ฒ
- ์์ ์ด ์๋๋ผ๋ ์ด ์ซ์ ๋งํผ์ ํญ์ ์์ฑ๋์ด ๋๊ธฐ ์ํ ์ ์ง
- Max Pool Size : ์ต๋ ์ฐ๋ ๋ ๊ฐ์ - corerPoolSize ์ 2๋ฐฐ
- Queue Capacity : ์์
ํ์ ์ฌ์ด์ฆ - 500
- Core Pool Size๊ฐ ๋์ด๊ฐ๋ฉด Queue์ ๋ค์ด๊ฐ๊ฒ ๋๊ณ ,
- Queue๊ฐ ๊ฝ์ฐจ๋ฉด MaxPoolSize ๋งํผ ์ฐ๋ ๋๊ฐ ํ์ฅ
Pool์ Thread๊ฐ ์์ฑ์๋์ด์์ผ๋ฉด ์์ฑ / ์์ฑ๋์ด์์ผ๋ฉด ์ฌ์ฌ์ฉ ํ๊ฒ ๋๋ค
โ ์ฌํํด๋ณด๊ธฐ
์ฌ์ค ์ ์ผ ์ดํด๊ฐ ์๋์๋ ๋ถ๋ถ์ Local ํ๊ฒฝ์์๋ ๋๊ณ , Dev ํ๊ฒฝ์์๋ ์๋๋ ๊ฒ์ด ์ ์ผ ์ด์ํ๋ค
Thread๋ ๊ฒฐ๊ตญ ๊ฐ์ ๋จธ์ ์์์ JVM์ ์ํด ๋ง๋ค์ด์ง๋๋ฐ, OS ์ํฅ์ ๋ฐ์ง ์์ ๊ฒ์ด๊ธฐ ๋๋ฌธ์ด๋ค
Spring Security์ ๋ํด์ ์ข ๋ deep diveํ ํ์
๋ก๊ทธ๋ฅผ ์ฐ์ด๋ณด๋ฉฐ ์ฐจ๊ทผํ ๋ค์ ์ดํด๋ณด๊ธฐ๋ก ํ์๋ค
์คํํ๊ฒฝ
๊ธฐ์กด Async Config๋ก๋ Thread Pool ์ ์ฐ๋ ๋ ๊ฐ์๊ฐ ๋ง์์ ๋ก๊ทธ ์ฐ๊ธฐ๊ฐ ์ด๋ ค์ธ๊ฑฐ ๊ฐ์์ ์๋์ ๊ฐ์ด ์ธํ ํ๋ค
- Core Pool Size : 2๊ฐ
- Max Pool Size : 2๊ฐ
์ฆ ๊ทธ๋ฅ ์ฐ๋ ๋ 2๊ฐ๋ก๋ง ์คํํด๋ณด์๋ค
ํ๋ก์ฐ๋ ์๋์ ๊ฐ๊ณ
Request -> ๋ถ๋ชจ์ฐ๋ ๋(Servelet Request๋ก ์์ฑ๋ ์ฐ๋ ๋) -> ๋ถ๋ชจ์ฐ๋ ๋์์ ๋น๋๊ธฐ ๋ฉ์๋ ํธ์ถ -> ๋ถ๋ชจ์ฐ๋ ๋ Response ๋์์ ์์ ๋น๋๊ธฐ ์ฐ๋ ๋ ์คํ
Request -> ๋ถ๋ชจ์ฐ๋ ๋(Servelet Request๋ก ์์ฑ๋ ์ฐ๋ ๋) -> ๋ถ๋ชจ์ฐ๋ ๋์์ ๋น๋๊ธฐ ๋ฉ์๋ ํธ์ถ -> ๋ถ๋ชจ์ฐ๋ ๋ Response ๋์์ ์์ ๋น๋๊ธฐ ์ฐ๋ ๋ ์คํ
API ํธ์ถ์ ์ ์ ธ ๋ก๊ทธ์ธ์ ๋ฐ๊ฟ๊ฐ๋ฉฐ ์๋์ ์์๋ก 4๋ฒ ํธ์ถํ์ฌ 4๊ฐ์ ๋ถ๋ชจ์์ ์ฐ๋ ๋ ์ ๋ณด๋ฅผ ์ป์ ์ ์์๋ค
- ์ง์A ๊ณ์ ์ผ๋ก API ํธ์ถ
- ์ง์B ๊ณ์ ์ผ๋ก API ํธ์ถ
- ์ง์A ๊ณ์ ์ผ๋ก API ํธ์ถ
- ์ง์A ๊ณ์ ์ผ๋ก API ํธ์ถ
2025-01-10 18:40:04.215 [http-nio-8080-exec-2] - [test] -----------PARENT Thread----------- [test]
2025-01-10 18:40:04.215 [http-nio-8080-exec-2] - [test] parent-thread-id=33
2025-01-10 18:40:04.215 [http-nio-8080-exec-2] - [test] parent-thread-name=http-nio-8080-exec-2
2025-01-10 18:40:04.215 [http-nio-8080-exec-2] - [test] parent-thread-state=RUNNABLE
2025-01-10 18:40:04.229 [http-nio-8080-exec-2] - [test] SecurityContext=SecurityContextImpl [Authentication=์ง์A]
2025-01-10 18:40:04.229 [http-nio-8080-exec-2] - [test] Locale=ko_KR
2025-01-10 18:40:04.229 [http-nio-8080-exec-2] - [test] --------------------------- [test]
2025-01-10 18:40:04.242 [AsyncExecutor-1] - [test] -----------Child @ASYNC Thread----------- [test]
2025-01-10 18:40:04.242 [AsyncExecutor-1] - [test] child-thread-id=53
2025-01-10 18:40:04.242 [AsyncExecutor-1] - [test] child-thread-name=AsyncExecutor-1
2025-01-10 18:40:04.242 [AsyncExecutor-1] - [test] child-thread-state=RUNNABLE
2025-01-10 18:40:04.242 [AsyncExecutor-1] - [test] SecurityContext=SecurityContextImpl [Authentication=์ง์A]
2025-01-10 18:40:04.242 [AsyncExecutor-1] - [test] Locale=null
2025-01-10 18:40:04.242 [AsyncExecutor-1] - [test] --------------------------- [test]
2025-01-10 18:40:11.153 [http-nio-8080-exec-3] - [test] -----------PARENT Thread----------- [test]
2025-01-10 18:40:11.153 [http-nio-8080-exec-3] - [test] parent-thread-id=34
2025-01-10 18:40:11.153 [http-nio-8080-exec-3] - [test] parent-thread-name=http-nio-8080-exec-3
2025-01-10 18:40:11.153 [http-nio-8080-exec-3] - [test] parent-thread-state=RUNNABLE
2025-01-10 18:40:11.153 [http-nio-8080-exec-3] - [test] SecurityContext=SecurityContextImpl [Authentication=์ง์B]
2025-01-10 18:40:11.153 [http-nio-8080-exec-3] - [test] Locale=ko_KR
2025-01-10 18:40:11.153 [http-nio-8080-exec-3] - [test] --------------------------- [test]
2025-01-10 18:40:11.153 [AsyncExecutor-2] - [test] -----------Child @ASYNC Thread----------- [test]
2025-01-10 18:40:11.153 [AsyncExecutor-2] - [test] child-thread-id=58
2025-01-10 18:40:11.153 [AsyncExecutor-2] - [test] child-thread-name=AsyncExecutor-2
2025-01-10 18:40:11.153 [AsyncExecutor-2] - [test] child-thread-state=RUNNABLE
2025-01-10 18:40:11.153 [AsyncExecutor-2] - [test] SecurityContext=SecurityContextImpl [Authentication=์ง์B]
2025-01-10 18:40:11.153 [AsyncExecutor-2] - [test] Locale=null
2025-01-10 18:40:11.153 [AsyncExecutor-2] - [test] --------------------------- [test]
2025-01-10 18:40:12.153 [http-nio-8080-exec-4] - [test] -----------PARENT Thread----------- [test]
2025-01-10 18:40:12.153 [http-nio-8080-exec-4] - [test] parent-thread-id=35
2025-01-10 18:40:12.153 [http-nio-8080-exec-4] - [test] parent-thread-name=http-nio-8080-exec-3
2025-01-10 18:40:12.153 [http-nio-8080-exec-4] - [test] parent-thread-state=RUNNABLE
2025-01-10 18:40:12.153 [http-nio-8080-exec-4] - [test] SecurityContext=SecurityContextImpl [Authentication=์ง์A]
2025-01-10 18:40:12.153 [http-nio-8080-exec-4] - [test] Locale=ko_KR
2025-01-10 18:40:12.153 [http-nio-8080-exec-4] - [test] --------------------------- [test]
2025-01-10 18:40:12.153 [AsyncExecutor-1] - [test] -----------Child @ASYNC Thread----------- [test]
2025-01-10 18:40:12.153 [AsyncExecutor-1] - [test] child-thread-id=53
2025-01-10 18:40:12.153 [AsyncExecutor-1] - [test] child-thread-name=AsyncExecutor-1
2025-01-10 18:40:12.153 [AsyncExecutor-1] - [test] child-thread-state=RUNNABLE
2025-01-10 18:40:12.153 [AsyncExecutor-1] - [test] SecurityContext=SecurityContextImpl [Authentication=์ง์A]
2025-01-10 18:40:12.153 [AsyncExecutor-1] - [test] Locale=null
2025-01-10 18:40:12.153 [AsyncExecutor-1] - [test] --------------------------- [test]
2025-01-10 18:40:13.153 [http-nio-8080-exec-5] - [test] -----------PARENT Thread----------- [test]
2025-01-10 18:40:13.153 [http-nio-8080-exec-5] - [test] parent-thread-id=36
2025-01-10 18:40:13.153 [http-nio-8080-exec-5] - [test] parent-thread-name=http-nio-8080-exec-5
2025-01-10 18:40:13.153 [http-nio-8080-exec-5] - [test] parent-thread-state=RUNNABLE
2025-01-10 18:40:13.153 [http-nio-8080-exec-5] - [test] SecurityContext=SecurityContextImpl [Authentication=์ง์A]
2025-01-10 18:40:13.153 [http-nio-8080-exec-5] - [test] Locale=ko_KR
2025-01-10 18:40:13.153 [http-nio-8080-exec-5] - [test] --------------------------- [test]
2025-01-10 18:40:13.153 [AsyncExecutor-2] - [test] -----------Child @ASYNC Thread----------- [test]
2025-01-10 18:40:13.153 [AsyncExecutor-2] - [test] child-thread-id=58
2025-01-10 18:40:13.153 [AsyncExecutor-2] - [test] child-thread-name=AsyncExecutor-2
2025-01-10 18:40:13.153 [AsyncExecutor-2] - [test] child-thread-state=RUNNABLE
2025-01-10 18:40:13.153 [AsyncExecutor-2] - [test] SecurityContext=SecurityContextImpl [Authentication=์ง์B]
2025-01-10 18:40:13.153 [AsyncExecutor-2] - [test] Locale=null
2025-01-10 18:40:13.153 [AsyncExecutor-2] - [test] --------------------------- [test]
๐จ๐จ๐จ๐จ๐จ๐จ๐จ
4๋ฒ์งธ ํธ์ถ์ ๋ณด๋ฉด
์ง์B ๊ณ์ ์ผ๋ก API ํธ์ถํ๋๋ฐ,
๋น๋๊ธฐ ์์ ์ฐ๋ ๋์ SecurityContext๊ฐ ์ง์B ์ธ ๊ฒ์ ํ์ธํ ์ ์๋ค.
๐จ๐จ๐จ๐จ๐จ๐จ๐จ
๋ถ๋ช Spring Security์์ ์์ ์ฐ๋ ๋๋ก SecuritContext ์ ํํ๋ ์ค์ ์ ํ๋๋ฐ????????
์ฌ๊ธฐ์ ๋ฉ๋ถ์ด ์์ ํ๋์ ๋ง์ ์๋ฃ๋ค์ ์ฐพ์๋ณด๊ณ ์ ์๊ณผ ๋ค์ํ ํ ๋ก ์ ํ์๋ค
๐ค MODE_INHERITABLETHREADLOCAL์ ๋ํ ์์ฌ ๊ทธ๋ฆฌ๊ณ Thread Pool
์๋ฃ๋ฅผ ๋ ์ฐพ์๋ณด๊ณ , ๋๋ฒ๊น ์ ํด๋ณด๋
Thread๊ฐ ์์ฑ๋๋ ์์ ์ SecurityContext๋ฅผ ๋ฃ์ด์ฃผ๋ ๊ฒ๊น์ง ํ์ธ์ํ๋ค
์ค์ํ๊ฑด Thread๊ฐ ์์ฑ๋๋ ์์ ์ด์๋ค
์ ์คํ์์ Thread Pool์์ ์ฌ์ฉํ ์ ์๋ ๋น๋๊ธฐ ์ฐ๋ ๋๋ 2๊ฐ๋ค
์ฒ์์
Spring init ์์ ์ Executor๊ฐ Thread Pool์ CoreSize ๋งํผ ์ฐ๋ ๋๋ฅผ ๋ง๋ค์ด๋๋๋ค?
๊ณ ์๊ฐํ๋ค
ํ์ง๋ง ์ค์ ๋ฐํ์์์ ๋๋ฒ๊น ์ ํด๋ณด๋
- ์ง์A ๊ณ์ ์ผ๋ก API ํธ์ถ -> ๋น๋๊ธฐ @Async ๋ฉ์๋ ํธ์ถ -> AsyncExecutor-1 ์ฐ๋ ๋ ์์ฑ(์ง์A) -> Pool์ ๋ฐ๋ฉ
- ์ง์B ๊ณ์ ์ผ๋ก API ํธ์ถ -> ๋น๋๊ธฐ @Async ๋ฉ์๋ ํธ์ถ -> AsyncExecutor-2 ์ฐ๋ ๋ ์์ฑ(์ง์B) -> Pool์ ๋ฐ๋ฉ
- ์ง์A ๊ณ์ ์ผ๋ก API ํธ์ถ -> ๋น๋๊ธฐ @Async ๋ฉ์๋ ํธ์ถ -> AsyncExecutor-1 ์ฐ๋ ๋ ์ฌ์ฌ์ฉ (์ง์A) -> Pool์ ๋ฐ๋ฉ
- ์ง์A ๊ณ์ ์ผ๋ก API ํธ์ถ -> ๋น๋๊ธฐ @Async ๋ฉ์๋ ํธ์ถ -> AsyncExecutor-2 ์ฐ๋ ๋ ์ฌ์ฌ์ฉ (์ง์B) -> Pool์ ๋ฐ๋ฉ
(์คํ์ ์ํด ์์ 4๊ฐ์ ํธ์ถ์ Localํ๊ฒฝ์์ ์๊ฐ์ฐจ๋ฅผ ๋๊ณ 4๋ฒ ํธ์ถ)
JVM๋ง๋ค ์ค์ ์ด ๋ค๋ฅผ ์ ์์ง๋ง (์ถํ ๊ณต๋ถ๊ฑฐ๋ฆฌ๋ค)
- Spring์ด initํ ๋๋ ThreadPool์ด ๋น์ด์๋ค๊ฐ
- ๋น๋๊ธฐ ๋ฉ์๋ ์ฒซ ํธ์ถ ๊ธฐ์ ์ผ๋ก ์ฐ๋ ๋๋ฅผ ์์ฑํด์ Pool์ ๋ฃ์ด์ฃผ๊ธฐ ์์
- MODE_INHERITABLETHREADLOCAL์ ์์ฑ! ์์ ์ Thread์ SecurityContext ๋ฃ์ด์ฃผ๋ ๊ฒ์ด๊ธฐ ๋๋ฌธ์
- 3,4๋ฒ ํธ์ถ ๋๋ Thread ์ฌ์ฌ์ฉ์ด๋ฏ๋ก ์ ์ฉ๋์ง ์์
๊ทธ๋ ๋ค๋ฉด 3,4๋ฒ ํธ์ถ ๋๋ ๋น๋๊ธฐ ์ฐ๋ ๋๋ค์ SecurityContext๊ฐ null์ด์ด์ผํ์ง ์์๊ฐ? ์ถ์๋ฐ
- 2๋ฒ AsyncExecutor-2๊ฐ ์ข ๋ฃ๋ ๋ ๋จ์์๋ ์ง์ B ์ธ์ฆ์ ๋ณด๊ฐ
- 4๋ฒ ํธ์ถ์ผ ๋ AsyncExecutor-2๊ฐ ์ฌ์ฌ์ฉ๋๋ฉด์
- ๋ถ๋ชจ์ฐ๋ ๋์ ์์๋น๋๊ธฐ์ฐ๋ ๋์ SecurityContext๊ฐ ๋ถ์ผ์น
๋น๋๊ธฐ ์ฐ๋ ๋๊ฐ Pool์ ๋ฐํ๋ ๋ SecurityContext๋ฅผ ์ง์์ฃผ์ง ์๋๋ค!!
์๋ธ๋ฆฟ Request๋ก ์์ฑ๋ ๋ถ๋ชจ์ฐ๋ ๋์์๋ SecurityContext๊ฐ
์์ ์ฐ๋ ๋ SecurityContext์ ๋ค๋ฅด๋ค๋ฉด ๊ฐ ์ฐ๋ ๋์ ์ฐจ์ด์ ์ ๋ฌด์์ด๊ณ ๊ฐ ์ฐ๋ ๋์์์ ์๋ช ์ฃผ๊ธฐ๋ ๋ฌด์์ธ์ง ๊ณต๋ถํด๋ด์ผํ๋ค
๐ค SecurityContext์ ์๋ช ์ฃผ๊ธฐ
๊ฒฐ๋ก ๋ถํฐ ๋งํ์๋ฉด, Servelet Request๋ก ์์ฑ๋ ์ฐ๋ ๋ ๋ด์์๋ง Spring Security๊ฐ ์๋์ผ๋ก ์ ํจํ๊ฒ ๊ด๋ฆฌํด์ค๋ค
Architecture :: Spring Security
The Security Filters are inserted into the FilterChainProxy with the SecurityFilterChain API. Those filters can be used for a number of different purposes, like exploit protection,authentication, authorization, and more. The filters are executed in a speci
docs.spring.io
Spring Security๋ ์ธ์ฆ/์ธ๊ฐ๋ฅผ ์ฒ๋ฆฌํ๊ณ SecurityContext๋ฅผ ์ธํ ํ๋ ๋ฐ์ Filter๋ฅผ ์ฌ์ฉํ๋ค
HTTP Request๊ฐ ์๋์ ๊ฐ์ด Filter๋ฅผ ๊ปด์ ์งํ์ด ๋๋๋ฐ
- ํด๋ผ์ด์ธํธ๊ฐ HTTP Request๋ฅผ ์ ์กํ๋ฉด, Servelet ์ปจํ ์ด๋(Tomcat ๋ฑ)๊ฐ ์์ฒญ ์์
- Servelet ์ปจํ ์ด๋๋ ๋ด๋ถ์ ์ผ๋ก ์ฐ๋ ๋ Pool์ ์ ์งํ๊ณ Pool์์ ์ฐ๋ ๋๋ฅผ ๊บผ๋ด์ ์์ฒญ์ ํ ๋น
- Servlet ์ปจํ ์ด๋๊ฐ ์์ฒญ์ Filter์ ์ ๋ฌ
- ๊ฐ Filter๋ฅผ ์ง๋๋ฉด์ Filter์ ํ ๋น๋ ์์ ์ด Servelet Request/Response ๊ธฐ๋ฐ์ผ๋ก ์ฒ๋ฆฌ
- ๋ชจ๋ Filter ์ง๋๋ฉด Spring Controller์ ์ ๋ฌ
- ์๋ต์ ์์ฒญ์ ๊ฑฐ์ณค๋ Filter๋ค์ ์ญ์์ผ๋ก ์ง๋์ Servelet ์ปจํ ์ด๋๋ก ๋ฐํ
- Servelet ์ปจํ ์ด๋๋ ์๋ต์ Serializeํ์ฌ HTTP Response๋ก ํด๋ผ์ด์ธํธ์๊ฒ ๋ฐํ
Spring Bean๊ณผ Servelet Filter๋ฅผ ์ฐ๊ฒฐํด์ฃผ๋ DelegatingFilterProxy๊ฐ ๊ฐ์ง๊ณ ์๋
Spring Security์ FilterChainProxy๊ฐ ์ดํด๋ด์ผํ ๋ฉ์ธ์ด๋ค
FilterChainProxy๋ฅผ ๊น๋ณด๋ฉด,
- ํ๋ก์ ํธ์์ ๋น์ผ๋ก ๋ฑ๋กํ SecurityFilter ๋ชฉ๋ก์ ๊ฐ์ง
- SecurityFilter ๋ชฉ๋ก์ chain์ผ๋ก ๋ฆฌ์คํธ๋ก ๊ฐ์ ธ์, ์์ฒญ์ด SecurityFilter๋ค์ ํต๊ณผํ๋๋ก ๊ด๋ฆฌ
- ๊ฐ๋ฐ์๊ฐ ์ปค์คํ ํ ์ธ์ฆ SecurityFilter ๋น์ ํต๊ณผํ๋ฉด์ securityContext๊ฐ ์ธํ ๋จ
๊ทธ๋ฆฌ๊ณ Response๊ฐ ๋ด๋ ค๊ฐ ๋
securityContext๋ฅผ clearํด์ฃผ๋ฉด์
Thread ๋ฐํํ๊ธฐ์ ์ SecurityContext๊ฐ ์๋ ThreadLocal์ด ๊น๋ํด์ง๋ ๊ฒ์ด๋ค!
- Servlet Filter๋ฅผ Spring Security๊ฐ SecurityContext ์์ฑ/์๋ช ์ํค๋๋ฐ์ ์ฌ์ฉ
- Servelet Request๋ก ์์ฑ๋ ์ฐ๋ ๋ ๋ด์์๋ SecurityContext๋ Spring Security FilterChainProxy์ ์ํด ์๋์ผ๋ก ์์ฑ -> ์๋ฉธ
๐ฎ ๊ทธ๋ ๋ค๋ฉด @Async๋ก ํธ์ถํ๋ ๋น๋๊ธฐ ์ฐ๋ ๋๋?
๋น๋๊ธฐ ์ฐ๋ ๋๋ Servelet Request๋ก ๋ง๋ค์ด์ ธ์ Filter๋ฅผ ํ์ง ์๋
๊ฐ๋ฐ์๊ฐ ์ปค์คํ ํ Thread Pool์์ ๊ด๋ฆฌ๋๋ Thread์ด๋ค
์ฆ, ๋น๋๊ธฐ ์ฐ๋ ๋๋ Servlet Request/Response๋ ์๋๊ณ Filter๋ฅผ ํ๋ ๊ฒ๋ ์๋๋ค
์ฐ๋ ๋๋ฅผ Pool์ ๋ฐํ์ ์ ์๋์ผ๋ก SecurityContext๋ฅผ clearํด์ฃผ๋ FilterChainProxy ๋ ์ ํ๋ค๋ ๊ฒ์ด๋ค
๊ทธ๋์
- ์ง์A ๊ณ์ ์ผ๋ก API ํธ์ถ -> ๋น๋๊ธฐ @Async ๋ฉ์๋ ํธ์ถ -> AsyncExecutor-1 ์ฐ๋ ๋ ์์ฑ(์ง์A) -> Pool์ ๋ฐ๋ฉ
- ์ง์B ๊ณ์ ์ผ๋ก API ํธ์ถ -> ๋น๋๊ธฐ @Async ๋ฉ์๋ ํธ์ถ -> AsyncExecutor-2 ์ฐ๋ ๋ ์์ฑ(์ง์B) -> Pool์ ๋ฐ๋ฉ
- ์ง์A ๊ณ์ ์ผ๋ก API ํธ์ถ -> ๋น๋๊ธฐ @Async ๋ฉ์๋ ํธ์ถ -> AsyncExecutor-1 ์ฐ๋ ๋ ์ฌ์ฌ์ฉ (์ง์A) -> Pool์ ๋ฐ๋ฉ
- ์ง์A ๊ณ์ ์ผ๋ก API ํธ์ถ -> ๋น๋๊ธฐ @Async ๋ฉ์๋ ํธ์ถ -> AsyncExecutor-2 ์ฐ๋ ๋ ์ฌ์ฌ์ฉ (์ง์B) -> Pool์ ๋ฐ๋ฉ
4๋ฒ ํธ์ถ ๋, ์ฐ๋ ๋ ์ฌ์ฌ์ฉ์ด๋ฏ๋ก SpringSecurity๋ SecurityContext๋ฅผ ๋๊ฒจ์ฃผ์ง ์์๊ณ
2๋ฒ์์ ์ฐ์ธ AsyncExecutor-2์ ๋จ์์๋ ์ง์B๊ฐ ๊ทธ๋๋ก ์ฐ์ธ๊ฒ์ด๋ค
๊ทธ๋ ๋ค๋ฉด 3๋ฒ ํธ์ถ๋๋ ์ฌ์ค ์ด์ด ์ข์์ ๋ถ๋ชจ์ฐ๋ ๋๋ ์์์ฐ๋ ๋์ SecurityContext๊ฐ ์ผ์นํ๊ฒ์ด๋ค
๐ฎ ์ ๊ทธ๋์?? Local ๊ฐ๋ฐ ํ๊ฒฝ์์ ๋ฏธ๋ฆฌ ๋ชป์ก์๋?
Local ๊ฐ๋ฐํ๊ฒฝ์์๋ ํต์์ ์ผ๋ก
- ํ์ฌ๋์ ๊ณ์ ์ผ๋ก๋ง ๊ฐ์ง๊ณ API ํ ์คํธ
- ๋น๋๊ธฐ ์ฐ๋ ๋์ ๋ชจ๋ ๊ฐ์ ์ฌ๋์ SecurityContext๊ฐ ์์ฌ
- ์ด์ฐจํผ ํ์ฌ๋์ ๊ณ์ ๋ง ์ฐ๋ฏ๋ก ๋ถ๋ชจ์ฐ๋ ๋์ ์์ ๋น๋๊ธฐ ์ฐ๋ ๋๊ฐ ์ธ์ฆ์ ๋ณด๊ฐ ๋ฌ๋ผ์ง ์ผ์ด์ค ๋ฐ๊ฒฌ ๋ชปํจ
Dev ์๋ฒ๋ก ๋ฐฐํฌํ์๋
- ์ฌ๋ฌ ํ ์คํฐ๋ค์ด ๋ค์์ ๊ณ์ ์ผ๋ก API ํ ์คํธํ๋ฉด์ ์์ ๋ฌธ์ ๋ก ์ฅ์ ๋ฐ์
๊ทธ๋์ Local ๊ฐ๋ฐํ๊ฒฝ์์๋ ๋๊ณ , Dev ์๋ฒ์์๋ ์๋๋ค๊ณ ์๊ฐํ๊ฑด๋ฐ
์๊ณ ๋ณด๋๊น ํ์์ ๊ฐ์๊ณ , ์ฌ๋ฌ ๊ณ์ ์ ๊ณ ๋ คํ๋ฉฐ Local์์ ํ ์คํธํ์ง ์์๊ธฐ ๋๋ฌธ์ ์๊ธด์ผ์ด์๋ค
๐คฃ ์์ธ ์ ๋ฆฌ
์์ธ์ ๊ทธ๋์ ๋ค์ ์์ฝํด๋ณด์๋ฉด
๐ ThreadPool์ ์กด์ฌํ๋ Thread ๊ฐ์๊ฐ Core Pool Size ๋ฏธ๋ง์ธ ๊ฒฝ์ฐ
- Thread๊ฐ ์ง์ `์์ฑ`๋๋ฏ๋ก, Spring Security Mode ์ค์ ์ ๋ฐ๋ผ ๋ถ๋ชจ SecurityContext ์ ๋ฌ
- ์์ ๋น๋๊ธฐ ์ฐ๋ ๋์์ ๋ถ๋ชจ ์ฐ๋ ๋์ ์ธ์ฆ์ ๋ณด๋ฅผ ๊ฐ์ง๊ณ ์ ์ ๋์
- ๋น๋๊ธฐ ์ฐ๋ ๋์ด๋ฏ๋ก, ServeletFilter๋ฅผ ํ์ง ์์ -> Thread ๋ฐํ์์ ์ SecutiryContext๊ฐ clear๋์ง ์์
๐ ThreadPool์ ์กด์ฌํ๋ Thread ๊ฐ์๊ฐ Core Pool Size๋ฅผ ๋ฌ์ฑํ๊ฑฐ๋, Max Pool Size ๋งํผ ์ปค์ง ๊ฒฝ์ฐ
- ThreadPool ์ ๋ต/์ํฉ์ ์ํด Thread๊ฐ ์ฌ์ฌ์ฉ๋ ์ ์์
- Thread๊ฐ `์ฌ์ฌ์ฉ`๋๋ฏ๋ก, Spring Security Mode(`์์ฑ`์์ ์ ์ ์ฉ)๊ฐ ์ ์ฉ์๋์ด ๋ถ๋ชจ SecurityContext ์ ๋ฌ ์๋จ
- ๋น๋๊ธฐ ์ฐ๋ ๋์ด๋ฏ๋ก, ServeletFilter๋ฅผ ํ์ง ์์ -> Thread ๋ฐํ์์ ์ SecutiryContext๊ฐ clear๋์ง ์์ -> ์ฒ์ ์ฐ๋ ๋ ์์ฑ ์์ SecurityContext๊ฐ ๊ณ์ ์์ฌ
- ์ด์ ์ฌ์ฉ์์ SecurityContext๊ฐ clear๋์ง ์์์ผ๋ฏ๋ก, ๋จ์์๋ SecurityContext์ฌ์ฉํ์ฌ ๋ก์ง ์คํ
๐ก ํด๊ฒฐ์ฑ
ํด๊ฒฐํด์ผ๋ ์ง์ ๋ ๋๊ฐ์ง์๋ค
- Thread Pool์์ ๋น๋๊ธฐ Thread ์ฌ์ฌ์ฉ ์์ Spring Security๊ฐ SecurityContext ๋ชป๋๊ฒจ์ฃผ๋ ์ง์
- ๋น๋๊ธฐ Thread๊ฐ Pool๋ก ๋ฐํ๋ ๋, ํด๋น ์ฐ๋ ๋์ ThreadLocal์ธ SecurityContext๊ฐ clear ์๋๋ ์ง์
์ผ๋จ ThreadPool์ ๋ง๋ค๊ณ ๋น๋๊ธฐ ์ฐ๋ ๋๋ค์ ๊ด๋ฆฌํ๋ Executor์ ์กฐ์น๋ฅผ ํด์ค์ผ๊ฒ ๋ค๊ณ ์๊ฐํด์ ์์นญํ๋๋
๐ DelegatingSecurityContextExecutor ๋น ๋ฑ๋ก
Executor๋ฅผ wrappingํด์ DelegatingSecurityContextExecutor๋ฅผ ๋น์ผ๋ก ๋ฑ๋กํด์ฃผ๋ฉด
Proxy ์ญํ ์ ํด์
- Thread๊ฐ ์์ฑ๋๊ณ , ์ฌ์ฌ์ฉ๋๋ ์์ ์ ๋ถ๋ชจ ์ฐ๋ ๋์ SecurityContext์ strategy๋ฅผ ์์ ๋น๋๊ธฐ ์ฐ๋ ๋์ ์ธํ
- Thread๊ฐ Pool์ ๋ฐํ๋๋ ์์ ์ ์ฐ๋ ๋์ SecurityContext๋ฅผ clearํ๋ค
@EnableAsync
@Configuration
public class AsyncConfig implements AsyncConfigurer {
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
int corePoolSize = Runtime.getRuntime().availableProcessors(); // CPU ์ฝ์ด ์์ ๋ง์ถฐ ์ค๋ ๋ ํ ํฌ๊ธฐ ์ค์
int maxPoolSize = corePoolSize * 2; // ์ต๋ ํ ํฌ๊ธฐ - CPU ์ฝ์ด ์์ ๋ ๋ฐฐ
int queueCapacity = 500; // ํ ์ฉ๋ ์ค์
executor.setCorePoolSize(corePoolSize);
executor.setMaxPoolSize(maxPoolSize);
executor.setQueueCapacity(queueCapacity);
executor.setThreadNamePrefix("AsyncExecutor-");
... ์ค๋ต
executor.initialize();
return new DelegatingSecurityContextExecutor(executor); // ์์ !!
}
}
๐ MODE_INHERITABLETHREADLOCAL -> MODE_THREADLOCAL ๋ก ์์
DelegatingSecurityContextExecutor ๊ฐ Thread ์์ฑ/์ฌํ์ฉ ์์ SecurityContext ์ ๋ฌ์ ๋ชจ๋ ์ปค๋ฒํ๊ณ ์๋ค๋ฉด,
SecurityContext strategy๋ฅผ ๊ตณ์ด MODE_INHERITALBETHREADLOCAL๋ก ์ค์ ํ์ฌ ์์ฑ ์์ SecurityContext ์ ๋ฌ์ ์ค๋ณต์ฒ๋ฆฌ ํ ํ์๊ฐ ์๋ค
๋ ๋์๊ฐ ๊ฐ์ฒด์ ์ฑ ์ ์ ์ฅ์์ ๋ณธ๋ค๋ฉด,
์์ ๋น๋๊ธฐ ์ฐ๋ ๋๊ฐ ์์ ์ SecurityContext์ ๋ํ ๊ฒฐ์ ์ ๋ถ๋ชจ ์ฐ๋ ๋์ Config๊ฐ ๊ฒฐ์ ํด์ฃผ๋ ๊ฒ์ด ์๋๋ผ
์์ ๋น๋๊ธฐ ์ฐ๋ ๋ ์์ ์ Config ์ฆ, AsyncConfig์์ ์ ํ๋ ๊ฒ์ด ๊ฐ์ฒด์งํฅ์ ์ผ๋ก๋, ๊ด๋ฆฌ ํฌ์ธํธ ์ ์ผ๋ก๋ ๋ ์ณ์๊ฑฐ ๊ฐ๋ค
๊ทธ๋์ SecurityContext๊ฐ ์์ฑ๋ ์ฐ๋ ๋ ์์์๋ง ์ ํ ๋ฒ์๋ฅผ ํ์ ํ๋ MODE_THREADLOCAL๋ก ์์ ํด์ฃผ์๋ค
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SpringSecurityConfig {
@PostConstruct
public void init() {
SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_THREADLOCAL); // ์์ !!
}
...์๋ต
}
๊ทธ๋ฌํ ์ด์ ์์ Spring Security๋ MODE_THREADLOCAL์ default๊ฐ์ผ๋ก ์ค์ ํ๊ณ ์๋๊ฑฐ ๊ฐ๋ค
์ฆ, PostConstruct๋ ์ํด๋ ๋๋ค๋ ๊ฒ!
๐ ๋ง์น๋ฉฐ
์ด๋ฒ Spring Security ์ด์ ํด๊ฒฐ์ ํตํด์ Filter, Thread Pool ๋์์๋ฆฌ / Servelet ์ปจํ ์ด๋์ ๊ธฐ์ฌ ์์ ๋ฑ์ ์ถ๊ฐ๋ก ์๊ฒ๋๊ฑฐ ๊ฐ๋ค.
๊ทธ๋ฆฌ๊ณ ๊ฒฐ๊ตญ Spring Security๋ ์ด๋ฐ๊ฒ๋ค ๊ธฐ๋ฐ์ผ๋ก ๋ง๋ค์ด์ง ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ผ ๋ฟ
Low level์ ๊ธฐ๋ฐ๊ธฐ์ ์ ๋ํด์ ์ข ๋ ํ์ตํ๋ ์๊ฐ์ ๊ฐ์ ธ์ผ๊ฒ ๋ค๊ณ ์๊ฐํ๋ค
๋ค์์๋ Thread Pool์ ๊ด๋ฆฌํ๋ Executor Service ๊ตฌ์กฐ์ ์๋ฆฌ
Servlet์ด HTTP request๋ฅผ ์ด๋ป๊ฒ ์ฒ๋ฆฌํ๋์ง์ ์ ๋ํด์ ์ข๋ ๊น๊ฒ ๊ณต๋ถํด๋ณด๊ณ ์ ํ๋ค