์น ๋ธ๋ผ์ฐ์ ์์ ์ธ์ฆ/์ธ๊ฐ ๋ฐฉ์ ํ์ต์ ์ํ ํํ ๋ฆฌ์ผ ์ฝ๋์ ๋๋ค.
sequenceDiagram
participant Browser
participant API Server
Browser->>API Server: ํ์๊ฐ์
์์ฒญ
API Server-->>API Server: ์ด๋ฆ ์ค๋ณตํ์ธ, ํ์๋ฑ๋ก
API Server->>Browser: ํ์๊ฐ์
์๋ต
sequenceDiagram
participant Browser
participant API Server
Browser->>API Server: ๋ก๊ทธ์ธ ์์ฒญ
API Server-->>API Server: ๊ณ์ ์ ๋ณด ์ธ์ฆ
API Server->>Browser: ๋ก๊ทธ์ธ ์๋ต (์ฟ ํค์ JWT ํ ํฐ ๋ฐ๊ธ)
sequenceDiagram
participant Browser
participant API Server
participant Oauth2 API Server
Browser->>API Server: OAuth 2.0 ๋ก๊ทธ์ธ ์์ฒญ
API Server->>Oauth2 API Server: OAuth 2.0 ์ธ์ฆ ์์ฒญ
Oauth2 API Server->>API Server: ์ธ์ฆ ์ฝ๋ ๋ฐ๊ธ ๋ฆฌ๋ค์ด๋ ํธ
API Server->>Oauth2 API Server: ์ ์ ์ ๋ณด ์์ฒญ
Oauth2 API Server->>API Server: ์ ์ ์ ๋ณด ์๋ต
API Server->>Browser: ๋ก๊ทธ์ธ ์๋ต (์ฟ ํค์ JWT ํ ํฐ ๋ฐ๊ธ)
sequenceDiagram
participant Browser
participant API Server
Browser->>API Server: ํ์์ ๋ณด ์์ฒญ
API Server-->>API Server: jwt ๊ฒ์ฆ & ํ์์ ๋ณด ์กฐํ
API Server->>Browser: ํ์์ ๋ณด ์๋ต
- ๋ก๊ทธ์ธ ์์ฒญ
- ํ์๊ฐ์ ํ์ด์ง ์ด๋
- ํ์๊ฐ์ ์์ฒญ
- ๋ก๊ทธ์์ ์์ฒญ
- ํ์์ ๋ณด ์์ฒญ
- ์๋ฒ์ธก์์ OAuth2 ๋ก๊ทธ์ธ ์ฑ๊ณต ๋ฆฌ๋ค์ด๋ ํธ
- jwt๋ฅผ ์ฟ ํค ํํ๋ก ์ ๋ฌ
๊ธฐ๋ฅ | Method | URL |
---|---|---|
ํ์๊ฐ์ | POST | /api/v1/users |
๋ก๊ทธ์ธ | POST | /api/v1/auth/login |
๋ก๊ทธ์์ | POST | /api/v1/auth/logout |
๋ณธ์ธ ์์ด๋ ์กฐํ | GET | /api/v1/auth/whoami |
ํ์์ ๋ณด ์์ฒญ | GET | /api/v1/users/{userId} |
ํ์๊ฐ์
- [POST] /api/v1/users
- ์์ฒญ
HTTP Body { "name" : string, "password" : string }
- ์๋ต
HTTP Body { "status": 201, "message": "Join completed.", "data": null, "timestamp": "yyyy-mm-ddThh:mm:ssZ" }
HTTP Body { "status": 400, "message": "Invalid value", "data": null, "timestamp": "yyyy-mm-ddThh:mm:ssZ" }
HTTP Body { "status": 409, "message": "Duplicated name", "data": null, "timestamp": "yyyy-mm-ddThh:mm:ssZ" }
HTTP Body { "status": 500, "message": "Server error", "data": null, "timestamp": "yyyy-mm-ddThh:mm:ssZ" }
๋ก๊ทธ์ธ - [POST] /api/v1/auth/login
- ์์ฒญ
HTTP Body { "name" : string, "password" : string }
- ์๋ต
Cookie "Authorization": "Bearer {JWT}" HTTP Body { "status": 200, "message": "Login completed successfully.", "data": null, "timestamp": "yyyy-mm-ddThh:mm:ssZ" }
HTTP Body { "status": 401, "message": "Authentication failed.", "data": null, "timestamp": "yyyy-mm-ddThh:mm:ssZ" }
HTTP Body { "status": 500, "message": "Server error", "data": null, "timestamp": "yyyy-mm-ddThh:mm:ssZ" }
๋ก๊ทธ์์ - [POST] /api/v1/auth/logout
- ์์ฒญ
Cookie "Authorization": "Bearer {JWT}"
- ์๋ต
HTTP Body { "status": 200, "message": "Logout completed successfully", "data": null, "timestamp": "yyyy-mm-ddThh:mm:ssZ" }
HTTP Body { "status": 401, "message": "Authentication failed", "data": null, "timestamp": "yyyy-mm-ddThh:mm:ssZ" }
HTTP Body { "status": 500, "message": "Server error", "data": null, "timestamp": "yyyy-mm-ddThh:mm:ssZ" }
๋ณธ์ธ ์์ด๋ ์กฐํ - [GET] /api/v1/auth/whoami
- ์์ฒญ
Cookie "Authorization": "Bearer {JWT}"
- ์๋ต
{ "status": 200, "message": "OK", "data": { "userId": number }, "timestamp": "yyyy-mm-ddThh:mm:ssZ" }
{ "status": 401, "message": "Authentication failed.", "data": null, "timestamp": "yyyy-mm-ddThh:mm:ssZ" }
{ "status": 500, "message": "Server error", "data": null, "timestamp": "yyyy-mm-ddThh:mm:ssZ" }
ํ์์ ๋ณด ์กฐํ - [GET] /api/v1/users/{userId}
-
์์ฒญ
Cookie "Authorization": "Bearer {JWT}"
-
์๋ต
{ "status": 200, "message": "OK", "data": { "id": number, "name": string, "provider": string, }, "timestamp": "yyyy-mm-ddThh:mm:ssZ" }
{ "status": 401, "message": "Authentication failed.", "data": null, "timestamp": "yyyy-mm-ddThh:mm:ssZ" }
{ "status": 404, "message": "User with [id] not found.", "data": null, "timestamp": "yyyy-mm-ddThh:mm:ssZ" }
{ "status": 500, "message": "Server error", "data": null, "timestamp": "yyyy-mm-ddThh:mm:ssZ" }
์ผ๋ฐ ๋ก๊ทธ์ธ
- ์ผ๋ฐ ๋ก๊ทธ์ธ ์์ฒญ
- ๋ฑ๋กํ ์ํ๋ฆฌํฐ ์ค์ ํด๋์ค์์,
UsernamePasswordAuthenticationFilter
๋ฅผ ์์๋ฐ์LoginFilter
์ ์ค์ ํ URL๋ก ์ธํดLoginFilter.attemptAuthentication()
ํธ์ถ- ์์ฒญ ๋ฐ๋์ ์๋ ์ด๋ฆ๊ณผ ๋น๋ฐ๋นํธ ->
UsernamePasswordAuthenticationToken
์์ฑ - ์์ฑํ ์ธ์ฆ ํ ํฐ ์ธ์๋ก ์ ๋ฌ๋ฐ๋
AuthenticationManager.authenticate()
ํธ์ถ
- ์์ฒญ ๋ฐ๋์ ์๋ ์ด๋ฆ๊ณผ ๋น๋ฐ๋นํธ ->
UserDetailsService
๋ฅผ ๊ตฌํํ ํด๋์ค์์loadUserByUsername()
ํธ์ถํ์ฌ ์ค์ ์ ์ ๋ก๋ฉํ๊ณ ๋ด๋ถ์ ์ผ๋ก ์ธ์ฆ ์๋LoginFilter
์์ ๊ฒฐ๊ณผ ์๋ต- ์ธ์ฆ์ ์ฑ๊ณตํ๋ค๋ฉด,
successfulAuthentication()
ํธ์ถํ์ฌ jwt ๋ด์ ์ฟ ํค ์๋ต - ์ธ์ฆ์ ์คํจํ๋ค๋ฉด,
unsuccessfulAuthentication()
ํธ์ถํ์ฌ ์คํจ ์๋ต
- ์ธ์ฆ์ ์ฑ๊ณตํ๋ค๋ฉด,
OAuth2 ๋ก๊ทธ์ธ
- ์คํ๋ง ์ํ๋ฆฌํฐ๊ฐ ์ ๊ณตํ๋ ์์
๋ก๊ทธ์ธ ์ธ์ฆ ํ์ด์ง ์์ฒญ
http(s)://{BACKEND IP:PORT}/oauth2/authorization/{์๋น์ค๋ช }/{client_id}/{redirect_url}
- ์์ ๋ก๊ทธ์ธ ์์ฒญ
- ์์
๋ก๊ทธ์ธ ์ธ์ฆ ์๋ฒ์์ ๋ก๊ทธ์ธ ์ฑ๊ณต ๋ฆฌ๋ค์ด๋ ํธ ์ฒ๋ฆฌ
- ์์ ๋ก๊ทธ์ธ ์๋น์ค ๋ฑ๋ก๊ณผ ์คํ๋ง ๋ถํธ ์ค์ ํ์ผ์ ์์ฑํ ๋ฆฌ๋ค์ด๋ ํธ url๋ก ์ฒ๋ฆฌ ์งํ
http://{BACKEND IP:PORT}/login/oauth2/code/{์๋น์ค๋ช }
-> ๊ด๋ก์ ์ธ URL ๋ค์ด๋ฐ
- ๋ฆฌ๋ค์ด๋ ํธ๋ก ๋ค์ด์จ code๋ฅผ ์ฌ์ฉํ์ฌ
OAuth2AuthorizationRequestRedirectFilter
์OAuth2LoginAuthenticationFilter
๋ฅผ ํตํด ์์ ๋ก๊ทธ์ธ ์ธ์ฆ ์๋ฒ์๊ฒ access ํ ํฐ ์์ฒญ OAuth2LoginAuthenticationProvider
๋ฅผ ํตํด, ๋ฐ์ access ํ ํฐ์ผ๋ก ์์ ๋ก๊ทธ์ธ ๋ฆฌ์์ค ์๋ฒ๋ก๋ถํฐ ์ ์ ์ ๋ณด ํ๋DefaultOAuth2UserService
๋ฅผ ์์ํCustomOAuth2UserService
ํด๋์ค์์loadUser()
ํธ์ถํ์ฌ ํด๋น ์ ์ ์ ๋ณด ๋ก๋ฉ- ๋ฑ๋กํ
OAuth2LoginSuccessHandler
๋ฅผ ํตํด ์ธ์ฆ ์ฑ๊ณต ์ฒ๋ฆฌ- jwt ์ธ์ฆ ์ฟ ํค ์๋ต
- ํ์ดํผ ๋งํฌ๋ก ์์ฒญํ๊ธฐ ๋๋ฌธ์ ๋ฐ๋ก ํด๋น ์์ ๋ก๊ทธ์ธ ํญ์ ์์๋ก ๋ซ๊ฑฐ๋ ์๋ต์ ์กฐํํ ์ ์์ผ๋ฏ๋ก ์ฟ ํค ๋ฐฉ์์ผ๋ก ์ ๋ฌํ ๊ฒ์ด๊ณ , ํด๋น ํญ์ ์ปจํธ๋กคํ๊ธฐ ์ํด ์ฝ๋ฐฑ์ฉ ์ปดํฌ๋ํธ๋ฅผ ๋ง๋ค๊ณ ๋ฆฌ๋ค์ด๋ ํธ ์ํด์ผ๋ก์จ ์์ ๋ก๊ทธ์ธ ์๋ฃ ํ ํญ ๋ซ๊ธฐ
์ธ์ฆ API ํธ์ถ
- ํ์์ ๋ณด ์์ฒญ API ํธ์ถ with
jwt ๋ด์ ์ฟ ํค
- ์ํ๋ฆฌํฐ ์ค์ ํ์ผ์ ์ง์ ํ
requestMatchers().authenticated()
์ ๋ฐ๋ผ์ ์ธ์ฆ ์ ์ฉ- ์ธ์ฆ์ด ์ ์ฉ๋ URL์
SecurityContextHolder
๋ด๋ถ์ ์๋Authentication
๊ฐ์ฒด์ ์ธ์ฆ ์ฌ๋ถ ํ์ธ
- ์ธ์ฆ์ด ์ ์ฉ๋ URL์
JwtFilter
์์ ์ฟ ํค์ ๋ด๊ธด jwt ๊ฒ์ฆ- ๊ฒ์ฆ์ ์ฑ๊ณตํ๋ค๋ฉด,
Authentication
๊ฐ์ฒด๋ฅผ ์์ฑํ์ฌSecurityContextHolder
์ ์ธํ ํ ํํฐ ์งํ - ๊ฒ์ฆ์ ์คํจํ๋ค๋ฉด ํน์ ์ธ์ฆ ์ ์ฉ URL์ด ์๋๋ผ์ ํ ํฐ์ด ์๋ค๋ฉด, ์๋ฌด๋ฐ ๋์๋ ํ์ง ์๊ณ ํํฐ ์งํ
- ๊ฒ์ฆ์ ์ฑ๊ณตํ๋ค๋ฉด,