[Security] Spring Security-Flow
Categories: Spring
๐ ๊ฐ์ธ์ ์ธ ๊ณต๊ฐ์ผ๋ก ๊ณต๋ถ๋ฅผ ๊ธฐ๋กํ๊ณ ๋ณต์ตํ๊ธฐ ์ํด ์ฌ์ฉํ๋ ๋ธ๋ก๊ทธ์
๋๋ค.
์ ํํ์ง ์์ ์ ๋ณด๊ฐ ์์ ์ ์์ผ๋ ์ฐธ๊ณ ๋ฐ๋๋๋ค :๐ธ
[ํ๋ฆฐ ๋ด์ฉ์ ๋๊ธ๋ก ๋จ๊ฒจ์ฃผ์๋ฉด ๋ณต๋ฐ์ผ์ค๊ฑฐ์์]
Spring Security์ ํ๋ฆ
๋ณด์์ด ์ ์ฉ๋ ์น ์์ฒญ์ ์ผ๋ฐ์ ์ธ ์ฒ๋ฆฌ ํ๋ฆ
- (1)์์ ์ฌ์ฉ์๊ฐ ๋ณดํธ๋ ๋ฆฌ์์ค๋ฅผ ์์ฒญ
- (2)์์ ์ธ์ฆ ๊ด๋ฆฌ์ ์ญํ ์ ํ๋ ์ปดํฌ๋ํธ๊ฐ ์ฌ์ฉ์์ ํฌ๋ฆฌ๋ด์
(Credential)์ ์์ฒญ
- ์ฌ์ฉ์์ ํฌ๋ฆฌ๋ด์ (Credential) ์ด๋ ํด๋น ์ฌ์ฉ์๋ฅผ ์ฆ๋ช ํ๊ธฐ ์ํ ๊ตฌ์ฒด์ ์ธ ์๋จ์ ์๋ฏธ. ์ผ๋ฐ์ ์ผ๋ก๋ ์ฌ์ฉ์์ ํจ์ค์๋๊ฐ ํฌ๋ฆฌ๋ด์ ์ ํด๋น
- (3)์์ ์ฌ์ฉ์๋ ์ธ์ฆ ๊ด๋ฆฌ์์๊ฒ ํฌ๋ฆฌ๋ด์ (Credential)์ ์ ๊ณต
- (4)์์ ์ธ์ฆ ๊ด๋ฆฌ์๋ ํฌ๋ฆฌ๋ด์ ์ ์ฅ์์์ ์ฌ์ฉ์์ ํฌ๋ฆฌ๋ด์ ์ ์กฐํ
- (5)์์ ์ธ์ฆ ๊ด๋ฆฌ์๋ ์ฌ์ฉ์๊ฐ ์ ๊ณตํ ํฌ๋ฆฌ๋ด์ ๊ณผ ํฌ๋ฆฌ๋ด์ ์ ์ฅ์์ ์ ์ฅ๋ ํฌ๋ฆฌ๋ด์ ์ ๋น๊ตํด ๊ฒ์ฆ ์์ ์ ์ํ
- (6) ์ ํจํ ํฌ๋ฆฌ๋ด์ ์ด ์๋๋ผ๋ฉด Exception์ throw ํ๋ค.
- (7) ์ ํจํ ํฌ๋ฆฌ๋ด์ ์ด๋ผ๋ฉด (8)์์ ์ ๊ทผ ๊ฒฐ์ ๊ด๋ฆฌ์ ์ญํ ์ ํ๋ ์ปดํฌ๋ํธ๋ ์ฌ์ฉ์๊ฐ ์ ์ ํ ๊ถํ์ ๋ถ์ฌ๋ฐ์๋์ง ๊ฒ์ฆ
- (9) ์ ์ ํ ๊ถํ์ ๋ถ์ฌ๋ฐ์ง ๋ชปํ ์ฌ์ฉ์๋ผ๋ฉด Exception์ throwํ๋ค.
- (10) ์ ์ ํ ๊ถํ์ ๋ถ์ฌ๋ฐ์ ์ฌ์ฉ์๋ผ๋ฉด ๋ณดํธ๋ ๋ฆฌ์์ค์ ์ ๊ทผ์ ํ์ฉํ.
์น ์์ฒญ์์์ ์๋ธ๋ฆฟ ํํฐ์ ํํฐ ์ฒด์ธ์ ์ญํ
์๋ธ๋ฆฟ ํํฐ(Servlet Filter)
- ์๋ธ๋ฆฟ ํํฐ(Servlet Filter)๋ ์๋ธ๋ฆฟ ๊ธฐ๋ฐ ์ ํ๋ฆฌ์ผ์ด์ ์ ์๋ํฌ์ธํธ์ ์์ฒญ์ด ๋๋ฌํ๊ธฐ ์ ์ ์ค๊ฐ์์ ์์ฒญ์ ๊ฐ๋ก์ฑ ํ ์ด๋ค ์ฒ๋ฆฌ๋ฅผ ํ ์ ์๋๋ก ํด์ฃผ๋ Java์ ์ปดํฌ๋ํธ
์๋ธ๋ฆฟ ํํฐ๋ ์๋ฐ์์ ์ ๊ณตํ๋ API์ด๋ฉฐ, javax.servlet ํจํค์ง์ ์ธํฐํ์ด์ค ํํ๋ก ์ ์๋์ด ์์ต๋๋ค.
javax.servlet.Filter
์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ ์๋ธ๋ฆฟ ํํฐ๋ ์น ์์ฒญ(request)์ ๊ฐ๋ก์ฑ์ด ์ด๋ค ์ฒ๋ฆฌ(์ ์ฒ๋ฆฌ)๋ฅผ ํ ์ ์์ผ๋ฉฐ, ๋ํ ์๋ํฌ์ธํธ์์ ์์ฒญ ์ฒ๋ฆฌ๊ฐ ๋๋ ํ ์ ๋ฌ๋๋ ์๋ต(reponse)์ ํด๋ผ์ด์ธํธ์๊ฒ ์ ๋ฌํ๊ธฐ ์ ์ ์ด๋ค ์ฒ๋ฆฌ(ํ์ฒ๋ฆฌ)๋ฅผ ํ ์ ์๋ค.
Filter์์์ ์ฒ๋ฆฌ๊ฐ ๋ชจ๋ ์๋ฃ๋๋ฉด DispatcherServlet์์ ํด๋ผ์ด์ธํธ์ ์์ฒญ์ ํธ๋ค๋ฌ์ ๋งคํํ๊ธฐ ์ํ ๋ค์ ์์ ์ ์งํ
ํํฐ์ฒด์ธ
Spring Security์์ ๋ณด์์ ์ํ ์์ ์ ์ฒ๋ฆฌํ๋ ํํฐ์ ๋ชจ์
์๋ธ๋ฆฟ ํํฐ๋ ํ๋ ์ด์์ ํํฐ๋ค์ ์ฐ๊ฒฐํด ํํฐ ์ฒด์ธ(Filter Chain)์ ๊ตฌ์ฑ
- ๊ฐ์ ํํฐ๋ค์ด
doFilter()
๋ผ๋ ๋ฉ์๋๋ฅผ ๊ตฌํํด์ผ ํ๋ฉฐ,doFilter()
๋ฉ์๋ ํธ์ถ์ ํตํด ํํฐ ์ฒด์ธ์ ํ์ฑ- doFilter()๋ Filter์์ ์ด๋ค ์ฒ๋ฆฌ๋ฅผ ํ๋ ์์์
- Filter ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ ๋ค์์ Filter ํด๋์ค๋ฅผ ๊ตฌํํ๋ค๋ฉด ์๋ธ๋ฆฟ ํํฐ์์ ํน๋ณํ ์์ ์ ์ํํ ๋ค, HttpServlet์ ๊ฑฐ์ณ DispatcherServlet์ ์์ฒญ์ด ์ ๋ฌ๋๋ฉฐ, ๋ฐ๋๋ก DispatcherServlet์์ ์ ๋ฌํ ์๋ต์ ๋ํด ์ญ์ ํน๋ณํ ์์ ์ ์ํํ ์ ์๋ค.
- Servlet FilterChain์ ์์ฒญ URI path๋ฅผ ๊ธฐ๋ฐ์ผ๋ก HttpServletRequest๋ฅผ ์ฒ๋ฆฌ โ ๋ฐ๋ผ์ ํด๋ผ์ด์ธํธ๊ฐ ์๋ฒ ์ธก ์ ํ๋ฆฌ์ผ์ด์ ์ ์์ฒญ์ ์ ์กํ๋ฉด ์๋ธ๋ฆฟ ์ปจํ ์ด๋๋ ์์ฒญ URI์ ๊ฒฝ๋ก๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์ด๋ค Filter์ ์ด๋ค Servlet์ ๋งคํํ ์ง ๊ฒฐ์ ํ๋ค.
- Filter Chain์์ Filter์ ์์๋ ๋งค์ฐ ์ค์ํ๋ฉฐ Spring Boot์์ ์ฌ๋ฌ ๊ฐ์ Filter๋ฅผ ๋ฑ๋กํ๊ณ ์์๋ฅผ ์ง์ ํ๊ธฐ ์ํด์๋ ๋ค์๊ณผ ๊ฐ์ ๋ ๊ฐ์ง ๋ฐฉ๋ฒ์ ์ ์ฉ๊ฐ๋ฅ
- Spring Bean์ผ๋ก ๋ฑ๋ก๋๋ Filter์ย
@Order
์ ๋ํ ์ด์ ์ ์ถ๊ฐํ๊ฑฐ๋ยOrderd
์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํด์ Filter์ ์์๋ฅผ ์ง์ ํ ์ ์๋ค. FilterRegistrationBean
์ ์ด์ฉํด Filter์ ์์๋ฅผ ๋ช ์์ ์ผ๋ก ์ง์ ํ ์ ์๋ค.
- Spring Bean์ผ๋ก ๋ฑ๋ก๋๋ Filter์ย
Spring Security์์์ ํํฐ ์ญํ
- Spring Security์์ ์ง์ํ๋ Filter ์ข ๋ฅ๋ ๋ฌด์ํ ๋ง๋ค.
-
Spring Security ํํฐ ์ค ํน๋ณํ ํํฐ :
DelegatingFilterProxy
,FilterChainProxy
DelegatingFilterProxy
์FilterChainProxy
ํด๋์ค๋ Filter ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ๊ธฐ ๋๋ฌธ์ ์๋ธ๋ฆฟ ํํฐ๋ก์จ์ ์ญํ ์ ์ํํ๋ค.
1. DelegatingFilterProxy โญ
Spring Security๋ Spring์ ํต์ฌ์ธ ApplicationContext๋ฅผ ์ด์ฉ ํ๋ค.
์๋ธ๋ฆฟ ํํฐ์ ์ฐ๊ฒฐ๋๋ Spring Security๋ง์ ํํฐ๋ฅผ ApplicationContext์ Bean์ผ๋ก ๋ฑ๋กํ ํ์ ์ด Bean๋ค์ ์ด์ฉํด์ ๋ณด์๊ณผ ๊ด๋ จ๋ ์ฌ๋ฌ ๊ฐ์ง ์์
์ ์ฒ๋ฆฌํ๊ฒ ๋๋๋ฐ DelegatingFilterProxy
๊ฐ Bean์ผ๋ก ๋ฑ๋ก๋ Spring Security์ ํํฐ๋ฅผ ์ฌ์ฉํ๋ ์์์ ์ด๋ผ๊ณ ์๊ฐํ๋ฉด ๋๋ค.
๊ทธ๋ฐ๋ฐ DelegatingFilterProxy
๋ ๋ณด์๊ณผ ๊ด๋ จ๋ ์ด๋ค ์์
์ ์ฒ๋ฆฌํ๋ ๊ฒ์ด ์๋๋ผ ์๋ธ๋ฆฟ ์ปจํ
์ด๋ ์์ญ์ ํํฐ์ ApplicationContext์ Bean์ผ๋ก ๋ฑ๋ก๋ ํํฐ๋ค์ ์ฐ๊ฒฐํด ์ฃผ๋ ๋ธ๋ฆฌ์ง ์ญํ ์ ํ๋ค.
2. FilterChainProxy โญ
- Spring Security์ Filter๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํ ์ง์ ์
- FilterChainProxy๋ถํฐ Spring Security์์ ์ ๊ณตํ๋ ๋ณด์ ํํฐ๋ค์ด ํ์ํ ์์ ์ ์ํํ๋ค
- Spring Security์ Filter Chain์ URL ๋ณ๋ก ์ฌ๋ฌ ๊ฐ ๋ฑ๋กํ ์ ์์ผ๋ฉฐ, Filter Chain์ด ์์ ๋ ์ด๋ค Filter Chain์ ์ฌ์ฉํ ์ง๋ FilterChainProxy๊ฐ ๊ฒฐ์ ํ๋ฉฐ, ๊ฐ์ฅ ๋จผ์ ๋งค์นญ๋ Filter Chain์ ์คํ
/api/**
ํจํด์ Filter Chain์ด ์๊ณ ,/api/message
URL ์์ฒญ์ด ์ ์กํ๋ ๊ฒฝ์ฐ/api/**
ํจํด๊ณผ ์ ์ผ ๋จผ์ ๋งค์นญ๋๋ฏ๋ก, ๋ํดํธ ํจํด์ธ/**
๋ ์ผ์นํ์ง๋ง ๊ฐ์ฅ ๋จผ์ ๋งค์นญ๋๋/api/**
ํจํด๊ณผ ์ผ์นํ๋ Filter Chain๋ง ์คํํฉ๋๋ค.
- /message/** ํจํด์ Filter Chain์ด ์๋๋ฐ
/message/
URL ์์ฒญ์ ์ ์กํ๋ ๊ฒฝ์ฐ - ๋งค์นญ๋๋ Filter Chain์ด ์์ผ๋ฏ๋ก ๋ํดํธ ํจํด์ธ/**
ํจํด์ Filter Chain์ ์คํํฉ๋๋ค.
-
์์
- ์ฌ๊ธฐ์ ํ๋ํ๋๊ฐ ๋ค ํํฐ๊ฐ ๊ด๋ฆฌํ๊ณ ์๋ค. ๋ด๋ถ์ ์ผ๋ก ๊ฐ๊ฐ ํ๋์ ํํฐ์ ๋งตํ๋๋ค.
- SpringSecurity์์ ์ง์ํ๋ Filter์ ์ข ๋ฅ๊ฐ ์ ๋ง ๋ง๊ณ ๋ด๊ฐ ์๋กญ๊ฒ ๊ตฌํํ ์ผ์ ์ ์๋ค. โ ๋ฌธ์ ๊ฐ ์์ ๋ ์ด๋๋ถ๋ถ์ด ์๋ชป๋์๋์ง๋ง ์ฐพ์๋ณผ ์ ์์ผ๋ฉด ๋จ.
Filter ์ธํฐํ์ด์ค
filter๋ init์ผ๋ก ์ด๊ธฐํํ๊ณ ์ถ์๋ฉ์๋ ๊ตฌํํด์ผํ๋ค.
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
public class FirstFilter implements Filter {
// (1) ์ด๊ธฐํ ์์
public void init(FilterConfig filterConfig) throws ServletException {
}
// (2) ํด๋น Filter๊ฐ ์ฒ๋ฆฌํ๋ ์ค์ง์ ์ธ ๋ก์ง์ ๊ตฌํ
public void doFilter(ServletRequest request,
ServletResponse response,
FilterChain chain)
throws IOException, ServletException {
// (2-1) ์ด๊ณณ์์ request(ServletRequest)๋ฅผ ์ด์ฉํด ๋ค์ Filter๋ก ๋์ด๊ฐ๊ธฐ ์ ์ฒ๋ฆฌ ์์
์ ์ํํ๋ค.
// (2-2)
chain.doFilter(request, response);
// (2-3)์์ response๋ฅผ ์ด์ฉํด (2-2)์ chain.doFilter(request, response)๊ฐ ํธ์ถ๋ ์ดํ์ ํ ์ ์๋ ํ์ฒ๋ฆฌ ์์
์ ๋ํ ์ฝ๋๋ฅผ ๊ตฌํํ ์ ์
}
public void destroy() {
// (5) Filter๊ฐ ์ฌ์ฉํ ์์์ ๋ฐ๋ฉํ๋ ์ฒ๋ฆฌ
}
}
Spring Security์ ์ธ์ฆ ์ปดํฌ๋ํธ
UsernamePasswordAuthenticationFilter
์ผ๋ฐ์ ์ผ๋ก ๋ก๊ทธ์ธ ํผ์์ ์ ์ถ๋๋ Username๊ณผ Password๋ฅผ ํตํ ์ธ์ฆ์ ์ฒ๋ฆฌํ๋ Filter
- AbstractAuthenticationProcessingFilter๋ฅผ ์์
- 1 : ํด๋ผ์ด์ธํธ์ ๋ก๊ทธ์ธ ํผ์ ํตํด ์ ์ก๋๋ request parameter์ ๋ํดํธ name์
username
๊ณผpassword
- 2 :
AntPathRequestMatcher
๋ ํด๋ผ์ด์ธํธ์ URL์ ๋งค์น๋๋ ๋งค์ฒ โ ํด๋ผ์ด์ธํธ์ URL์ด โ/login
โ์ด๊ณ , HTTP Method๊ฐPOST
์ผ ๊ฒฝ์ฐ ๋งค์น๋ ๊ฑฐ๋ผ๋ ์ฌ์ค์ ์์ธก๊ฐ๋ฅ - AntPathRequestMatcher์ ๊ฐ์ฒด(
DEFAULT_ANT_PATH_REQUEST_MATCHER
)๋ ๋ ธ๋ ๋ฐ์ค์ ์๋ 4๋ฒ ์์ ์์ ํด๋์ค์ธ AbstractAuthenticationProcessingFilter ํด๋์ค์ ์ ๋ฌ๋์ด Filter๊ฐ ๊ตฌ์ฒด์ ์ธ ์์ ์ ์ํํ ์ง ํน๋ณํ ์์ ์์ด ๋ค๋ฅธ Filter๋ฅผ ํธ์ถํ ์ง ๊ฒฐ์ ํ๋ ๋ฐ ์ฌ์ฉ - ๋ถํ๋ฐ์ค์ ํด๋นํ๋ 5๋ฒ์
attemptAuthentication()
๋ฉ์๋๋ ๋ฉ์๋ ์ด๋ฆ์์๋ ์ ์ ์๋ฏ์ด ํด๋ผ์ด์ธํธ์์ ์ ๋ฌํ username๊ณผ password ์ ๋ณด๋ฅผ ์ด์ฉํด ์ธ์ฆ์ ์๋ํ๋ ๋ฉ์๋attemptAuthentication()
๋ฉ์๋๋ ์์ ํด๋์ค์ธ AbstractAuthenticationProcessingFilter์doFilter()
๋ฉ์๋์์ ํธ์ถ๋๋ค.- HTTP Method๊ฐ POST๊ฐ ์๋๋ฉด Exception์ throwํ๋ค
- username๊ณผ password ์ ๋ณด๋ฅผ ์ด์ฉํด
UsernamePasswordAuthenticationToken
์ ์์ฑ (UsernamePasswordAuthenticationToken
์ ์ธ์ฆ์ ํ๊ธฐ ์ํด ํ์ํ ์ธ์ฆ ํ ํฐ์ด์ง ์ธ์ฆ์ ์ฑ๊ณตํ ์ธ์ฆ ํ ํฐ๊ณผ๋ ์๊ด์ด ์๋ค)
- doFilter() ๋ ์์ํด๋์ค์ธAbstractAuthenticationProcessingFilter๊ฐ ํฌํจํ๊ณ ์๋ค.
AbstractAuthenticationProcessingFilter
HTTP ๊ธฐ๋ฐ์ ์ธ์ฆ ์์ฒญ์ ์ฒ๋ฆฌํ์ง๋ง ์ค์ง์ ์ธ ์ธ์ฆ ์๋๋ ํ์ ํด๋์ค์ ๋งก๊ธฐ๊ณ , ์ธ์ฆ์ ์ฑ๊ณตํ๋ฉด ์ธ์ฆ๋ ์ฌ์ฉ์์ ์ ๋ณด๋ฅผ SecurityContext์ ์ ์ฅํ๋ ์ญํ
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
public abstract class AbstractAuthenticationProcessingFilter extends
GenericFilterBean implements ApplicationEventPublisherAware, MessageSourceAware {
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
this.doFilter((HttpServletRequest)request, (HttpServletResponse)response, chain);
}
// (1)
private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
// (1-1) ์ธ์ฆ ์ฒ๋ฆฌ๋ฅผ ํด์ผ ํ๋์ง ์๋๋ฉด ๋ค์ Filter๋ฅผ ํธ์ถํ ์ง ์ฌ๋ถ๋ฅผ ๊ฒฐ์
if (!this.requiresAuthentication(request, response)) {
chain.doFilter(request, response);
} else {
try {
Authentication authenticationResult = this.attemptAuthentication(request, response);
// (1-2) ํ์ ํด๋์ค (UsernamePasswordAuthenticationFilter )์์ ์ธ์ฆ์ ์๋ํด ์ค ๊ฒ์ ์
if (authenticationResult == null) {
return;
}
this.sessionStrategy.onAuthentication(authenticationResult, request, response);
// Authentication success
if (this.continueChainBeforeSuccessfulAuthentication) {
chain.doFilter(request, response);
}
this.successfulAuthentication(request, response, chain, authenticationResult);
// (1-3) ์ธ์ฆ์ ์ฑ๊ณตํ๋ฉด ์ฒ๋ฆฌํ ๋์์ ์ํํ๊ธฐ ์ํด (3) successfulAuthentication() ๋ฉ์๋๋ฅผ ํธ์ถ
} catch (InternalAuthenticationServiceException var5) {
InternalAuthenticationServiceException failed = var5;
this.logger.error("An internal error occurred while trying to authenticate the user.", failed);
this.unsuccessfulAuthentication(request, response, failed);
// (1-4) ๋ง์ฝ ์ธ์ฆ์ ์คํจํ๋ค๋ฉด (4) unsuccessfulAuthentication() ๋ฉ์๋๋ฅผ ํธ์ถํด ์ธ์ฆ ์คํจ ์ ์ฒ๋ฆฌํ ๋์์ ์ํ
// Authentication failed
} catch (AuthenticationException var6) {
AuthenticationException ex = var6;
this.unsuccessfulAuthentication(request, response, ex);
}
}
}
// (2) requiresAuthenticationRequestMatcher ๊ฐ์ฒด๋ฅผ ํตํด **๋ค์ด์ค๋ ์์ฒญ์ด ์ธ์ฆ ์ฒ๋ฆฌ๋ฅผ ํด์ผ ํ๋์ง ์ฌ๋ถ๋ฅผ ๊ฒฐ์ **
protected boolean requiresAuthentication(HttpServletRequest request, HttpServletResponse response) {
if (this.requiresAuthenticationRequestMatcher.matches(request)) {
return true;
} else {
if (this.logger.isTraceEnabled()) {
this.logger.trace(LogMessage.format("Did not match request to %s", this.requiresAuthenticationRequestMatcher));
}
return false;
}
}
public abstract Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException;
// (3) ์ธ์ฆ์ ์ฑ๊ณตํ ์ดํ, **SecurityContextHolder๋ฅผ ํตํด ์ฌ์ฉ์์ ์ธ์ฆ ์ ๋ณด๋ฅผ SecurityContext์ ์ ์ฅํ ๋ค, SecurityContext๋ฅผ HttpSession์ ์ ์ฅ**
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
SecurityContext context = SecurityContextHolder.createEmptyContext();
context.setAuthentication(authResult);
SecurityContextHolder.setContext(context);
this.securityContextRepository.saveContext(context, request, response);
if (this.logger.isDebugEnabled()) {
this.logger.debug(LogMessage.format("Set SecurityContextHolder to %s", authResult));
}
this.rememberMeServices.loginSuccess(request, response, authResult);
if (this.eventPublisher != null) {
this.eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass()));
}
this.successHandler.onAuthenticationSuccess(request, response, authResult);
}
// (4) ์ธ์ฆ ์คํจ์ SeucurityContext๋ฅผ ์ด๊ธฐํํ๊ณ , AuthenticationFailureHandler๋ฅผ ํธ์ถ
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
SecurityContextHolder.clearContext();
this.logger.trace("Failed to process authentication request", failed);
this.logger.trace("Cleared SecurityContextHolder");
this.logger.trace("Handling authentication failure");
this.rememberMeServices.loginFail(request, response);
this.failureHandler.onAuthenticationFailure(request, response, failed);
}
...
...
}
- ์ฌ๊ธฐ์ this๋ ์๋
AbstractAuthenticationProcessingFilter
ํด๋์ค์ ํ์ฌ ์ธ์คํด์ค๋ฅผ ์ฐธ์กฐํ์ง๋ง, ์ถ์ํด๋์ค ์ด๊ธฐ ๋๋ฌธ์ ์ง์ ์ธ์คํด์คํ ๋์ง ์๊ณ ์ด๋ฅผ ์์๋ฐ์ ํ์ํด๋์ค์ ์ธ์คํด์ค๊ฐ ์์ฑ๋๋ค. ๋ฐ๋ผ์ ์์๋ฐ์ ํ์ํด๋์ค์ ํด๋นํ๋UsernamePasswordAuthenticationFilter
์ ์ธ์คํด์ค๋ฅผ ์ฐธ์กฐํ๊ฒ ๋๋ค. - ์ฆ, this๋
AbstractAuthenticationProcessingFilter
ํด๋์ค์ ํ์ํด๋์ค (UsernamePasswordAuthenticationFilter
)์ ํด๋นํจ.
UsernamePasswordAuthenticationToken
Spring Security์์ Username/Password๋ก ์ธ์ฆ์ ์ํํ๊ธฐ ์ํด ํ์ํ ํ ํฐ์ด๋ฉฐ, ๋ํ ์ธ์ฆ ์ฑ๊ณต ํ ์ธ์ฆ์ ์ฑ๊ณตํ ์ฌ์ฉ์์ ์ธ์ฆ ์ ๋ณด๊ฐ UsernamePasswordAuthenticationToken์ ํฌํจ๋์ด Authentication ๊ฐ์ฒด ํํ๋ก SecurityContext์ ์ ์ฅ๋๋ค.
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
public class UsernamePasswordAuthenticationToken extends AbstractAuthenticationToken {
...
private final Object principal;
private Object credentials;
...
...
// (1) **์ธ์ฆ์ ํ์ํ ์ฉ๋์ UsernamePasswordAuthenticationToken ๊ฐ์ฒด๋ฅผ ์์ฑ**
public static UsernamePasswordAuthenticationToken unauthenticated(Object principal, Object credentials) {
return new UsernamePasswordAuthenticationToken(principal, credentials);
}
// (2) **์ธ์ฆ์ ์ฑ๊ณตํ ์ดํ SecurityContext์ ์ ์ฅ๋ UsernamePasswordAuthenticationToken ๊ฐ์ฒด๋ฅผ ์์ฑ**
public static UsernamePasswordAuthenticationToken authenticated(Object principal, Object credentials,
Collection<? extends GrantedAuthority> authorities) {
return new UsernamePasswordAuthenticationToken(principal, credentials, authorities);
}
...
...
}
- UsernamePasswordAuthenticationToken์ AbstractAuthenticationToken์ ์์ํ๋ ํ์ฅํด๋์ค์ด์, Authentication ์ธํฐํ์ด์ค์ ๋ฉ์๋ ์ผ๋ถ๋ฅผ ๊ตฌํํ๋ ๊ตฌํ ํด๋์ค์ด๋ค.
Authentication
Spring Security์์์ ์ธ์ฆ ์์ฒด๋ฅผ ํํํ๋ ์ธํฐํ์ด์ค
1
2
3
4
5
6
7
8
9
public interface Authentication extends Principal, Serializable {
Collection<? extends GrantedAuthority> getAuthorities();
Object getCredentials();
Object getDetails();
Object getPrincipal();
boolean isAuthenticated();
void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException;
}
- Principal
Principal
์ ์ฌ์ฉ์๋ฅผ ์๋ณํ๋ ๊ณ ์ ์ ๋ณด
์ผ๋ฐ์ ์ผ๋ก Username/Password ๊ธฐ๋ฐ ์ธ์ฆ์์
Username
์ด Principal์ด ๋๋ฉฐ, ๋ค๋ฅธ ์ธ์ฆ ๋ฐฉ์์์๋UserDetails
๊ฐ Principal์ด ๋๋ค - Credentials
- ์ฌ์ฉ์ ์ธ์ฆ์ ํ์ํ Password๋ฅผ ์๋ฏธํ๋ฉฐ ์ธ์ฆ์ด ์ด๋ฃจ์ด์ง๊ณ ๋ ์งํ,
ProviderManager
๊ฐ ํด๋น Credentials๋ฅผ ์ญ์ ํ๋ค.
- ์ฌ์ฉ์ ์ธ์ฆ์ ํ์ํ Password๋ฅผ ์๋ฏธํ๋ฉฐ ์ธ์ฆ์ด ์ด๋ฃจ์ด์ง๊ณ ๋ ์งํ,
- Authorities
AuthenticationProvider
์ ์ํด ๋ถ์ฌ๋ ์ฌ์ฉ์์ ์ ๊ทผ ๊ถํ ๋ชฉ๋ก์ด๋ค. ์ผ๋ฐ์ ์ผ๋กGrantedAuthority
์ธํฐํ์ด์ค์ ๊ตฌํ ํด๋์ค๋SimpleGrantedAuthority
AuthenticationManager
์ฆ ์ฒ๋ฆฌ๋ฅผ ์ด๊ดํ๋ ๋งค๋์ ์ญํ ์ ํ๋ ์ธํฐํ์ด์ค
1
2
3
4
5
public interface AuthenticationManager {
//authenticate() ๋ฉ์๋ ํ๋๋ง ์ ์๋์ด ์๋ค.
Authentication authenticate(Authentication authentication) throws AuthenticationException;
}
- ๋์จํ ๊ฒฐํฉ์ ์ ์งํ๊ณ ์์ผ๋ฉฐ, ์ธ์ฆ์ ์ํ ์ค์ง์ ์ธ ๊ด๋ฆฌ๋ AuthenticationManager๋ฅผ ๊ตฌํํ๋ ๊ตฌํ ํด๋์ค๋ฅผ ํตํด ์ด๋ฃจ์ด ์ง๋ค.
ProviderManager
AuthenticationManager ์ธํฐํ์ด์ค์ ๊ตฌํ ํด๋์ค๋ผ๊ณ ํ๋ฉด ์ผ๋ฐ์ ์ผ๋ก ProviderManager
๋ฅผ ๊ฐ๋ฆฌํจ๋ค.
AuthenticationProvider๋ฅผ ๊ด๋ฆฌํ๊ณ , AuthenticationProvider์๊ฒ ์ธ์ฆ ์ฒ๋ฆฌ๋ฅผ ์์ํ๋ ์ญํ
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
public class ProviderManager implements AuthenticationManager, MessageSourceAware, InitializingBean {
...
...
// (1) ProviderManager ํด๋์ค๊ฐ Bean์ผ๋ก ๋ฑ๋ก ์, List<AuthenticationProvider> **๊ฐ์ฒด๋ฅผ DI ๋ฐ๋๋ค**๋ ๊ฒ์ ์ ์์๋ค.
public ProviderManager(List<AuthenticationProvider> providers, AuthenticationManager parent) {
Assert.notNull(providers, "providers list cannot be null");
this.providers = providers;
this.parent = parent;
checkState();
}
...
...
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
Class<? extends Authentication> toTest = authentication.getClass();
AuthenticationException lastException = null;
AuthenticationException parentException = null;
Authentication result = null;
Authentication parentResult = null;
int currentPosition = 0;
int size = this.providers.size();
// (2) DI ๋ฐ์ List<AuthenticationProvider>๋ฅผ ์ด์ฉํด (2)์ ๊ฐ์ด for๋ฌธ์ผ๋ก **์ ์ ํ AuthenticationProvider๋ฅผ ์ฐพ๋๋ค.**
for (AuthenticationProvider provider : getProviders()) {
if (!provider.supports(toTest)) {
continue;
}
if (logger.isTraceEnabled()) {
logger.trace(LogMessage.format("Authenticating request with %s (%d/%d)",
provider.getClass().getSimpleName(), ++currentPosition, size));
}
try {
result = provider.authenticate(authentication);
// (3) AuthenticationProvider๋ฅผ ์ฐพ์๋ค๋ฉด (3)๊ณผ ๊ฐ์ด ํด๋น **AuthenticationProvider์๊ฒ ์ธ์ฆ ์ฒ๋ฆฌ๋ฅผ ์์**
if (result != null) {
copyDetails(authentication, result);
break;
}
}
catch (AccountStatusException | InternalAuthenticationServiceException ex) {
prepareException(ex, authentication);
throw ex;
}
catch (AuthenticationException ex) {
lastException = ex;
}
}
...
...
if (result != null) {
if (this.eraseCredentialsAfterAuthentication && (result instanceof CredentialsContainer)) {
((CredentialsContainer) result).eraseCredentials();
// (4)์ธ์ฆ์ด ์ ์์ ์ผ๋ก ์ฒ๋ฆฌ๋์๋ค๋ฉด **์ธ์ฆ์ ์ฌ์ฉ๋ Credentials๋ฅผ ์ ๊ฑฐ**
}
if (parentResult == null) {
this.eventPublisher.publishAuthenticationSuccess(result);
}
return result;
}
...
...
}
...
...
}
AuthenticationProvider
AuthenticationManager๋ก๋ถํฐ ์ธ์ฆ ์ฒ๋ฆฌ๋ฅผ ์์๋ฐ์ ์ค์ง์ ์ธ ์ธ์ฆ ์ํ์ ๋ด๋นํ๋ ์ปดํฌ๋ํธ
Username/Password ๊ธฐ๋ฐ์ ์ธ์ฆ ์ฒ๋ฆฌ๋ DaoAuthenticationProvider
๊ฐ ๋ด๋นํ๊ณ ์์ผ๋ฉฐ, DaoAuthenticationProvider
๋ UserDetailsService๋ก๋ถํฐ ์ ๋ฌ๋ฐ์ UserDetails๋ฅผ ์ด์ฉํด ์ธ์ฆ์ ์ฒ๋ฆฌํ๋ค.
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
public class DaoAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
// (1) AbstractUserDetailsAuthenticationProvider๋ฅผ ์์
...
private PasswordEncoder passwordEncoder;
...
...
// (2) UserDetailsService๋ก๋ถํฐ UserDetails๋ฅผ ์กฐํํ๋ ์ญํ
// ์กฐํ๋ **UserDetails๋ ์ฌ์ฉ์๋ฅผ ์ธ์ฆํ๋ ๋ฐ ์ฌ์ฉ๋ ๋ฟ๋ง ์๋๋ผ ์ธ์ฆ์ ์ฑ๊ณตํ ๊ฒฝ์ฐ, ์ธ์ฆ๋ Authentication ๊ฐ์ฒด๋ฅผ ์์ฑํ๋ ๋ฐ ์ฌ์ฉ**
@Override
protected final UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication)
throws AuthenticationException {
prepareTimingAttackProtection();
try {
UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);
// (2-1) UserDetails๋ฅผ ์กฐํ
if (loadedUser == null) {
throw new InternalAuthenticationServiceException(
"UserDetailsService returned null, which is an interface contract violation");
}
return loadedUser;
}
catch (UsernameNotFoundException ex) {
mitigateAgainstTimingAttack(authentication);
throw ex;
}
catch (InternalAuthenticationServiceException ex) {
throw ex;
}
catch (Exception ex) {
throw new InternalAuthenticationServiceException(ex.getMessage(), ex);
}
}
// (3) PasswordEncoder๋ฅผ ์ด์ฉํด ์ฌ์ฉ์์ ํจ์ค์๋๋ฅผ ๊ฒ์ฆํ
@Override
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())) {
// (3-1) ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ์กฐํํ ํจ์ค์๋๊ฐ ์ผ์นํ๋์ง ๊ฒ์ฆํ๊ณ ์๋ ๊ฒ์ ํ์ธ
this.logger.debug("Failed to authenticate since password does not match stored value");
throw new BadCredentialsException(this.messages
.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
}
}
...
...
}
- AuthenticationProvider ์ธํฐํ์ด์ค์ ๊ตฌํ ํด๋์ค๋
AbstractUserDetailsAuthenticationProvider
์ด๊ณ , DaoAuthenticationProvider๋AbstractUserDetailsAuthenticationProvider
๋ฅผ ์์ํ ํ์ฅ ํด๋์ค์ด๋ค. - โญ ๋ฐ๋ผ์
AbstractUserDetailsAuthenticationProvider
์ถ์ ํด๋์ค์authenticate()
๋ฉ์๋์์๋ถํฐ ์ค์ง์ ์ธ ์ธ์ฆ ์ฒ๋ฆฌ๊ฐ ์์๋๋ค - DaoAuthenticationProvider์ AbstractUserDetailsAuthenticationProvider์ ์ฝ๋๋ฅผ ์ดํดํ๊ธฐ ์ํด์๋ ๋ฉ์๋๊ฐ ํธ์ถ๋๋ ์์๊ฐ ์ค์
- ๋ ํด๋์ค๊ฐ ๋ฒ๊ฐ์ ๊ฐ๋ฉด์ ํธ์ถ๋๊ธฐ ๋๋ฌธ์ ๋ก์ง์ ์ดํดํ๊ธฐ ์ฝ์ง ์์
AbstractUserDetailsAuthenticationProvider
์ authenticate() ๋ฉ์๋ ํธ์ถDaoAuthenticationProvider
์ retrieveUser() ๋ฉ์๋ ํธ์ถDaoAuthenticationProvider
์ additionalAuthenticationChecks() ๋ฉ์๋ ํธ์ถDaoAuthenticationProvider
์ createSuccessAuthentication() ๋ฉ์๋ ํธ์ถAbstractUserDetailsAuthenticationProvider
์ createSuccessAuthentication() ๋ฉ์๋ ํธ์ถ- ์ธ์ฆ๋ Authentication์
ProviderManager
์๊ฒ ๋ฆฌํด
- ๋ ํด๋์ค๊ฐ ๋ฒ๊ฐ์ ๊ฐ๋ฉด์ ํธ์ถ๋๊ธฐ ๋๋ฌธ์ ๋ก์ง์ ์ดํดํ๊ธฐ ์ฝ์ง ์์
UserDetails
๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ฑ์ ์ ์ฅ์์ ์ ์ฅ๋ ์ฌ์ฉ์์ Username๊ณผ ์ฌ์ฉ์์ ์๊ฒฉ์ ์ฆ๋ช
ํด ์ฃผ๋ ํฌ๋ฆฌ๋ด์
(Credential)
์ธ Password ๊ทธ๋ฆฌ๊ณ ์ฌ์ฉ์์ ๊ถํ ์ ๋ณด๋ฅผ ํฌํจํ๋ ์ปดํฌ๋ํธ์ด๋ฉฐ, AuthenticationProvider
๋ UserDetails
๋ฅผ ์ด์ฉํด ์๊ฒฉ ์ฆ๋ช
์ ์ํ
1
2
3
4
5
6
7
8
9
10
11
12
public interface UserDetails extends Serializable {
Collection<? extends GrantedAuthority> getAuthorities(); // (1) ๊ถํ ์ ๋ณด
String getPassword(); // (2) ํจ์ค์๋
String getUsername(); // (3) Username
boolean isAccountNonExpired(); // (4) ์ฌ์ฉ์ ๊ณ์ ์ ๋ง๋ฃ ์ฌ๋ถ
boolean isAccountNonLocked(); // (5) ์ฌ์ฉ์ ๊ณ์ ์ lock ์ฌ๋ถ
boolean isCredentialsNonExpired(); // (6) Credentials(Password)์ ๋ง๋ฃ ์ฌ๋ถ
boolean isEnabled(); // (7) ์ฌ์ฉ์์ ํ์ฑํ ์ฌ๋ถ
}
UserDetailsService
UserDetails๋ฅผ ๋ก๋(load)ํ๋ ํต์ฌ ์ธํฐํ์ด์ค
1
2
3
4
5
public interface UserDetailsService {
// ์ฌ์ฉ์์ ์ ๋ณด๋ฅผ ๋ก๋
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}
- ์ฌ์ฉ์์ ์ ๋ณด๋ฅผ ์ด๋์์ ๋ก๋ํ๋์ง๋ ์ ํ๋ฆฌ์ผ์ด์ ์์ ์ฌ์ฉ์์ ์ ๋ณด๋ฅผ ์ด๋์์ ๊ด๋ฆฌํ๊ณ ์๋์ง์ ๋ฐ๋ผ์ ๋ฌ๋ผ
- ์ฌ์ฉ์์ ์ ๋ณด๋ฅผ ๋ฉ๋ชจ๋ฆฌ์์ ๋ก๋ํ๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ๋ก๋ํ๋ Spring Security๊ฐ ์ดํดํ ์ ์๋ UserDetails๋ก ๋ฆฌํด ํด์ฃผ๊ธฐ๋ง ํ๋ฉด ๋๋ค
SecurityContext์ SecurityContextHolder
SecurityContext
๋ ์ธ์ฆ๋ Authentication ๊ฐ์ฒด๋ฅผ ์ ์ฅํ๋ ์ปดํฌ๋ํธ์ด๊ณ , SecurityContextHolder
๋ SecurityContext๋ฅผ ๊ด๋ฆฌํ๋ ์ญํ ์ ๋ด๋น
SecurityContextHolder
๋ฅผ ํตํด ์ธ์ฆ๋ Authentication์ SecurityContext์ ์ค์ ํ ์ ์๊ณ ๋ํSecurityContextHolder
๋ฅผ ํตํด ์ธ์ฆ๋ Authentication ๊ฐ์ฒด์ ์ ๊ทผํ ์ ์๋ค๋ ๊ฒ์ ์๋ฏธ- โญ Spring Security ์ ์ฅ์์๋ SecurityContextHolder์ ์ํด SecurityContext์ ๊ฐ์ด ์ฑ์์ ธ ์๋ค๋ฉด ์ธ์ฆ๋ ์ฌ์ฉ์๋ก ๊ฐ์ฃผ
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class SecurityContextHolder {
...
...
private static SecurityContextHolderStrategy strategy;
// (1) SecurityContextHolder์์ ์ฌ์ฉํ๋ ์ ๋ต์ ์๋ฏธํ๋ฉฐ,
// SecurityContextHolder ๊ธฐ๋ณธ ์ ๋ต์ ThreadLocalSecurityContextHolderStrategy
// ์ด ์ ๋ต์ ํ์ฌ ์คํ ์ค๋ ๋์ SecurityContext๋ฅผ ์ฐ๊ฒฐํ๊ธฐ ์ํด ThreadLocal์ ์ฌ์ฉํ๋ ์ ๋ต
...
// (2) ํ์ฌ ์คํ ์ค๋ ๋์์ SecurityContext๋ฅผ ์ป์ ์ ์๋ค
public static SecurityContext getContext() {
return strategy.getContext();
}
...
// (3) ํ์ฌ ์คํ ์ค๋ ๋์ SecurityContext๋ฅผ ์ฐ๊ฒฐ
// setContext()๋ ๋๋ถ๋ถ ์ธ์ฆ๋ Authentication์ ํฌํจํ SecurityContext๋ฅผ ํ์ฌ ์คํ ์ค๋ ๋์ ์ฐ๊ฒฐํ๋ ๋ฐ ์ฌ์ฉ
public static void setContext(SecurityContext context) {
strategy.setContext(context);
}
...
}
Spring Security์ ๊ถํ ๋ถ์ฌ ์ฒ๋ฆฌ ํ๋ฆ
- (1)์์ ์ฌ์ฉ์๊ฐ ๋ก๊ทธ์ธ์ ํ๋ฉด Spring Security๊ฐ ์ ์ฉ๋ ์ ํ๋ฆฌ์ผ์ด์ ์ Username(๋ก๊ทธ์ธ ID)๊ณผ Password๋ฅผ ํฌํจํ request๊ฐ ์ ์ก๋๋ค.
- ์ฌ์ฉ์์ ๋ก๊ทธ์ธ ์์ฒญ์ด Spring Security์ Filter Chain๊น์ง ๋ค์ด์ค๋ฉด ์ฌ๋ฌ Filter๋ค ์ค์์
UsernamePasswordAuthenticationFilter
๊ฐ ํด๋น ์์ฒญ์ ์ ๋ฌ๋ฐ๋๋ค.- ์ฌ์ฉ์์ ๋ก๊ทธ์ธ ์์ฒญ์ ์ฒ๋ฆฌํ๋ Spring Security Filter๋ UsernamePasswordAuthenticationFilter ์ด๋ค.
- spring Security์์์ ์ค์ง์ ์ธ ์ธ์ฆ ์ฒ๋ฆฌ๋ ์ง๊ธ๋ถํฐ ์์
- ๋ก๊ทธ์ธ ์์ฒญ์ ์ ๋ฌ๋ฐ์ UsernamePasswordAuthenticationFilter๋ Username๊ณผ Password๋ฅผ ์ด์ฉํด (2)์ ๊ฐ์ด
UsernamePasswordAuthenticationToken
์ ์์ฑ- UsernamePasswordAuthenticationToken์ Authentication ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ ๊ตฌํ ํด๋์ค์ด๋ฉฐ, ์ฌ๊ธฐ์์ Authentication์ โญ
์์ง ์ธ์ฆ์ด ๋์ง ์์ Authentication
์ด๋ค.
- UsernamePasswordAuthenticationToken์ Authentication ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ ๊ตฌํ ํด๋์ค์ด๋ฉฐ, ์ฌ๊ธฐ์์ Authentication์ โญ
- ์์ง ์ธ์ฆ๋์ง ์์ Authentication์ ๊ฐ์ง๊ณ ์๋ UsernamePasswordAuthenticationFilter๋ (3)๊ณผ ๊ฐ์ด ํด๋น Authentication์ AuthenticationManager์๊ฒ ์ ๋ฌ
- AuthenticationManager๋ ์ธ์ฆ ์ฒ๋ฆฌ๋ฅผ ์ด๊ดํ๋ ๋งค๋์ ์ญํ ์ ํ๋ ์ธํฐํ์ด์ค์ด๊ณ , AuthenticationManager๋ฅผ ๊ตฌํํ ๊ตฌํ ํด๋์ค๊ฐ ๋ฐ๋ก ProviderManager ์ด๋ค. ์ฆ, `ProviderManager๊ฐ ์ธ์ฆ์ด๋ผ๋ ์์ ์ ์ด๊ดํ๋ ์ค์ง์ ์ธ ๋งค๋์ ์ธ ๊ฒ
- (4)์ ๊ฐ์ด
ProviderManager
๋ก๋ถํฐ Authentication์ ์ ๋ฌ๋ฐ์AuthenticationProvider
๋ (5)์ ๊ฐ์ดUserDetailsService
๋ฅผ ์ด์ฉํดUserDetails
๋ฅผ ์กฐํUserDetails
๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ฑ์ ์ ์ฅ์์ ์ ์ฅ๋ ์ฌ์ฉ์์ Username๊ณผ ์ฌ์ฉ์์ ์๊ฒฉ์ ์ฆ๋ช ํด ์ฃผ๋ํฌ๋ฆฌ๋ด์ (Credential)
์ธ Password, ๊ทธ๋ฆฌ๊ณ ์ฌ์ฉ์์ ๊ถํ ์ ๋ณด๋ฅผ ํฌํจํ๊ณ ์๋ ์ปดํฌ๋ํธ
UserDetailsService
๋ (5)์์ ์ฒ๋ผ ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ฑ์ ์ ์ฅ์์์ ์ฌ์ฉ์์ ํฌ๋ฆฌ๋ด์ (Credential)์ ํฌํจํ ์ฌ์ฉ์์ ์ ๋ณด๋ฅผ ์กฐํ- ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ฑ์ ์ ์ฅ์์์ ์กฐํํ ์ฌ์ฉ์์ ํฌ๋ฆฌ๋ด์
(Credential)์ ํฌํจํ ์ฌ์ฉ์์ ์ ๋ณด๋ฅผ ๊ธฐ๋ฐ์ผ๋ก (7)๊ณผ ๊ฐ์ด
UserDetails
๋ฅผ ์์ฑํ ํ, ์์ฑ๋UserDetails
๋ฅผ ๋ค์AuthenticationProvider
์๊ฒ ์ ๋ฌ - UserDetails๋ฅผ ์ ๋ฌ๋ฐ์
AuthenticationProvider
๋ PasswordEncoder๋ฅผ ์ด์ฉํดUserDetails
์ ํฌํจ๋ ์ํธํ๋ Password์ ์ธ์ฆ์ ์ํAuthentication
์์ ํฌํจ๋ Password๊ฐ ์ผ์นํ๋์ง ๊ฒ์ฆ- ๊ฒ์ฆ์ ์ฑ๊ณต(9) โ ์ธ์ฆ๋ Authentication์ ์์ฑ (UserDetails๋ฅผ ํตํด).
- ์คํจ โ Exception์ ๋ฐ์์ํค๊ณ ์ธ์ฆ ์ฒ๋ฆฌ๋ฅผ ์ค๋จ
AuthenticationProvider
๋ ์ธ์ฆ๋ Authentication์ProviderManager
์๊ฒ ์ ๋ฌ(10).- (2)์์์
Authentication
์ ์ธ์ฆ์ ์ํด ํ์ํ ์ฌ์ฉ์์ ๋ก๊ทธ์ธ ์ ๋ณด ProviderManager
์๊ฒ ์ ๋ฌํ Authentication์ ์ธ์ฆ์ ์ฑ๊ณตํ ์ฌ์ฉ์์ ์ ๋ณด
- (2)์์์
- Authentication์ ์ ๋ฌ๋ฐ์ UsernamePasswordAuthenticationFilter๋ ๋ง์ง๋ง์ผ๋ก (12)์ ๊ฐ์ด
SecurityContextHolder
๋ฅผ ์ด์ฉํดSecurityContext
์ ์ธ์ฆ๋ Authentication์ ์ ์ฅ - โญ ๊ทธ๋ฆฌ๊ณ SecurityContext๋ ์ดํ์ Spring Security์ ์ธ์ ์ ์ฑ ์ ๋ฐ๋ผ์ HttpSession์ ์ ์ฅ๋์ด ์ฌ์ฉ์์ ์ธ์ฆ ์ํ๋ฅผ ์ ์งํ๊ธฐ๋ ํ๊ณ , HttpSession์ ์์ฑํ์ง ์๊ณ ๋ฌด์ํ๋ฅผ ์ ์งํ๊ธฐ๋ ํ๋ค.
Spring Security์ ์ปดํฌ๋ํธ๋ก ๋ณด๋ ๊ถํ ๋ถ์ฌ(Authorization) ์ฒ๋ฆฌ ํ๋ฆ
์๋๋ ์ฌ์ฉ์๊ฐ ๋ก๊ทธ์ธ ์ธ์ฆ์ ์ฑ๊ณตํ ์ดํ, Spring Security์์ ์ธ์ฆ๋ ์ฌ์ฉ์์๊ฒ ์ด๋ป๊ฒ ๊ถํ์ ๋ถ์ฌํ๋์ง ๊ทธ ์ฒ๋ฆฌ ํ๋ฆ์ ๋ํ๋ด๋ ๊ทธ๋ฆผ์ด๋ค.
- Spring Security Filter Chain์์ URL์ ํตํด ์ฌ์ฉ์์ ์ก์ธ์ค๋ฅผ ์ ํํ๋ ๊ถํ ๋ถ์ฌ Filter๋ ๋ฐ๋ก
AuthorizationFilter
์ด๋ค. AuthorizationFilter
๋ ๋จผ์ (1)๊ณผ ๊ฐ์ด SecurityContextHolder๋ก๋ถํฐ Authentication์ ํ๋ํ๋ค.- ๊ทธ๋ฆฌ๊ณ (2)์ ๊ฐ์ด SecurityContextHolder๋ก๋ถํฐ ํ๋ํ Authentication๊ณผ HttpServletRequest๋ฅผ
AuthorizationManager
์๊ฒ ์ ๋ฌํ๋ค. -
AuthorizationManager
๋ ๊ถํ ๋ถ์ฌ ์ฒ๋ฆฌ๋ฅผ ์ด๊ดํ๋ ๋งค๋์ ์ญํ ์ ํ๋ ์ธํฐํ์ด์ค์ด๊ณ ,RequestMatcherDelegatingAuthorizationManager
๋AuthorizationManager
๋ฅผ ๊ตฌํํ๋ ๊ตฌํ์ฒด ์ค ํ๋์ด๋ค.RequestMatcherDelegatingAuthorizationManager
๋ RequestMatcher ํ๊ฐ์์ ๊ธฐ๋ฐ์ผ๋ก ํด๋น ํ๊ฐ์์ ๋งค์น๋๋AuthorizationManager
์๊ฒ ๊ถํ ๋ถ์ฌ ์ฒ๋ฆฌ๋ฅผ ์์ํ๋ ์ญํ ์ ํ๋คโญ
RequestMatcherDelegatingAuthorizationManager
๊ฐ ์ง์ ๊ถํ ๋ถ์ฌ ์ฒ๋ฆฌ๋ฅผ ํ๋ ๊ฒ์ด ์๋๋ผRequestMatcher
๋ฅผ ํตํด ๋งค์น๋๋AuthorizationManager
๊ตฌํ ํด๋์ค์๊ฒ ์์๋ง ํ๋ค RequestMatcherDelegatingAuthorizationManager
๋ด๋ถ์์ ๋งค์น๋๋AuthorizationManager
๊ตฌํ ํด๋์ค๊ฐ ์๋ค๋ฉด ํด๋นAuthorizationManager
๊ตฌํ ํด๋์ค๊ฐ ์ฌ์ฉ์์ ๊ถํ์ ์ฒดํฌํ๋ค(3).- ์ ์ ํ ๊ถํ์ด๋ผ๋ฉด (4)์ ๊ฐ์ด ๋ค์ ์์ฒญ ํ๋ก์ธ์ค๋ฅผ ๊ณ์ ์ด์ด๊ฐ๋ค
- ๋ง์ฝ ์ ์ ํ ๊ถํ์ด ์๋๋ผ๋ฉด (5)์ ๊ฐ์ด
AccessDeniedException
์ด throw๋๊ณ ExceptionTranslationFilter๊ฐAccessDeniedException
์ ์ฒ๋ฆฌํ๊ฒ ๋๋ค.
Spring Security์ ๊ถํ ๋ถ์ฌ ์ปดํฌ๋ํธ
AuthorizationFilter
AuthorizationFilter
๋ URL์ ํตํด ์ฌ์ฉ์์ ์ก์ธ์ค๋ฅผ ์ ํํ๋ ๊ถํ ๋ถ์ฌ Filter์ด๋ฉฐ, Spring Security 5.5 ๋ฒ์ ๋ถํฐ FilterSecurityInterceptor๋ฅผ ๋์ฒดํ๋ค.
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
29
30
31
32
33
34
35
36
public class AuthorizationFilter extends OncePerRequestFilter {
private final AuthorizationManager<HttpServletRequest> authorizationManager;
...
...
// (1)
//AuthorizationFilter ๊ฐ์ฒด๊ฐ ์์ฑ๋ ๋, AuthorizationManager๋ฅผ DI ๋ฐ๋๋ค
//DI ๋ฐ์ AuthorizationManager๋ฅผ ํตํด ๊ถํ ๋ถ์ฌ ์ฒ๋ฆฌ๋ฅผ ์งํ
public AuthorizationFilter(AuthorizationManager<HttpServletRequest> authorizationManager) {
Assert.notNull(authorizationManager, "authorizationManager cannot be null");
this.authorizationManager = authorizationManager;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
AuthorizationDecision decision = this.authorizationManager.check(this::getAuthentication, request);
// (2) DI ๋ฐ์ AuthorizationManager์ check() ๋ฉ์๋๋ฅผ ํธ์ถํด ์ ์ ํ ๊ถํ ๋ถ์ฌ ์ฌ๋ถ๋ฅผ ์ฒดํฌ
// URL ๊ธฐ๋ฐ์ผ๋ก ๊ถํ ๋ถ์ฌ ์ฒ๋ฆฌ๋ฅผ ํ๋ **AuthorizationFilter**๋ AuthorizationManager์ ๊ตฌํ ํด๋์ค๋ก
// RequestMatcherDelegatingAuthorizationManager๋ฅผ ์ฌ์ฉ
this.eventPublisher.publishAuthorizationEvent(this::getAuthentication, request, decision);
if (decision != null && !decision.isGranted()) {
throw new AccessDeniedException("Access Denied");
}
filterChain.doFilter(request, response);
}
...
...
}
AuthorizationManager
์ด๋ฆ ๊ทธ๋๋ก ๊ถํ ๋ถ์ฌ ์ฒ๋ฆฌ๋ฅผ ์ด๊ดํ๋ ๋งค๋์ ์ญํ ์ ํ๋ ์ธํฐํ์ด์ค
1
2
3
4
5
6
7
8
9
10
11
12
@FunctionalInterface
public interface AuthorizationManager<T> {
...
...
@Nullable
AuthorizationDecision check(Supplier<Authentication> authentication, T object);
// AuthorizationManager ์ธํฐํ์ด์ค๋ check() ๋ฉ์๋ ํ๋๋ง ์ ์๋์ด ์์ผ๋ฉฐ,
// Supplier<Authentication>์ ์ ๋๋ฆญ ํ์
์ ๊ฐ์ฒด๋ฅผ ํ๋ผ๋ฏธํฐ๋ก ๊ฐ์ง
}
RequestMatcherDelegatingAuthorizationManager
RequestMatcherDelegatingAuthorizationManager
๋ AuthorizationManager์ ๊ตฌํ ํด๋์ค ์ค ํ๋์ด๋ฉฐ, ์ง์ ๊ถํ ๋ถ์ฌ ์ฒ๋ฆฌ๋ฅผ ์ํํ์ง ์๊ณ RequestMatcher
๋ฅผ ํตํด ๋งค์น๋๋ AuthorizationManager
๊ตฌํ ํด๋์ค์๊ฒ ๊ถํ ๋ถ์ฌ ์ฒ๋ฆฌ๋ฅผ ์์ํ๋ค.
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
29
30
31
32
33
public final class RequestMatcherDelegatingAuthorizationManager implements AuthorizationManager<HttpServletRequest> {
...
...
@Override
public AuthorizationDecision check(Supplier<Authentication> authentication, HttpServletRequest request) {
if (this.logger.isTraceEnabled()) {
this.logger.trace(LogMessage.format("Authorizing %s", request));
}
// (1) check() ๋ฉ์๋์ ๋ด๋ถ์์ (1)๊ณผ ๊ฐ์ด ๋ฃจํ๋ฅผ ๋๋ฉด์ RequestMatcherEntry ์ ๋ณด๋ฅผ ์ป์ ํ์
// (2)์ ๊ฐ์ด RequestMatcher ๊ฐ์ฒด๋ฅผ ์ป์ต๋๋ค
for (RequestMatcherEntry<AuthorizationManager<RequestAuthorizationContext>> mapping : this.mappings) {
RequestMatcher matcher = mapping.getRequestMatcher(); // (2)
MatchResult matchResult = matcher.matcher(request);
if (matchResult.isMatch()) {
// (3) MatchResult.isMatch()๊ฐ true์ด๋ฉด AuthorizationManager ๊ฐ์ฒด๋ฅผ ์ป์ ๋ค,
// AuthorizationManager ๊ตฌํ ํด๋์ค์๊ฒ ๊ถํ ๋ถ์ฌ ์ฒ๋ฆฌ๋ฅผ ์์ํ๋ค.
AuthorizationManager<RequestAuthorizationContext> manager = mapping.getEntry();
if (this.logger.isTraceEnabled()) {
this.logger.trace(LogMessage.format("Checking authorization on %s using %s", request, manager));
}
return manager.check(authentication,
new RequestAuthorizationContext(request, matchResult.getVariables()));
}
}
this.logger.trace("Abstaining since did not find matching RequestMatcher");
return null;
}
}
โญ ์ฌ๊ธฐ์์ RequestMatcher
๋ SecurityConfiguration์์ .antMatchers("/orders/**").hasRole("ADMIN")
์ ๊ฐ์ ๋ฉ์๋ ์ฒด์ธ ์ ๋ณด๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์์ฑ๋๋ค
Comment
๋๋ฌด ์ด๋ ค์์ ์ ๋ฆฌํ๋๋ฐ ํ์ฐธ ๊ฑธ๋ ธ๋ค,, ๊ทผ๋ฐ๋ ์ดํด๊ฐ ์๊ฐ๋ ๊ฒ์ ํจ์ ;; ์ด์ ์ดํดํด์ผํ๋๋ฐ ๋ต์ด์๋ค ์ ๋ง ใ ใ ใ
Leave a comment