Skip to content

This repository contains tutorial code for implementing authentication and authorization with Spring Boot and React.

Notifications You must be signed in to change notification settings

BenchPress200/auth-tutorial

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

24 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

๐Ÿ” auth-tutorial

์›น ๋ธŒ๋ผ์šฐ์ €์—์„œ ์ธ์ฆ/์ธ๊ฐ€ ๋ฐฉ์‹ ํ•™์Šต์„ ์œ„ํ•œ ํŠœํ† ๋ฆฌ์–ผ ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค.


์‚ฌ์šฉ ๊ธฐ์ˆ 

TypeScript React
Java Spring Boot Spring Security
JWT



๐Ÿš€ Sequence Diagram

ํšŒ์›๊ฐ€์ž…

sequenceDiagram
  participant Browser
  participant API Server

  Browser->>API Server: ํšŒ์›๊ฐ€์ž… ์š”์ฒญ
  API Server-->>API Server: ์ด๋ฆ„ ์ค‘๋ณตํ™•์ธ, ํšŒ์›๋“ฑ๋ก

  API Server->>Browser: ํšŒ์›๊ฐ€์ž… ์‘๋‹ต
Loading

๋กœ๊ทธ์ธ

sequenceDiagram
  participant Browser
  participant API Server

  Browser->>API Server: ๋กœ๊ทธ์ธ ์š”์ฒญ
  API Server-->>API Server: ๊ณ„์ •์ •๋ณด ์ธ์ฆ

  API Server->>Browser: ๋กœ๊ทธ์ธ ์‘๋‹ต (์ฟ ํ‚ค์— JWT ํ† ํฐ ๋ฐœ๊ธ‰)
Loading

OAuth 2.0 ๋กœ๊ทธ์ธ

  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 ํ† ํฐ ๋ฐœ๊ธ‰)
Loading

ํšŒ์›์ •๋ณด ์กฐํšŒ

sequenceDiagram
  participant Browser
  participant API Server

  Browser->>API Server: ํšŒ์›์ •๋ณด ์š”์ฒญ
  API Server-->>API Server: jwt ๊ฒ€์ฆ & ํšŒ์›์ •๋ณด ์กฐํšŒ

  API Server->>Browser: ํšŒ์›์ •๋ณด ์‘๋‹ต
Loading



๐ŸŒ Browser Client

Login Page

  • ๋กœ๊ทธ์ธ ์š”์ฒญ
  • ํšŒ์›๊ฐ€์ž… ํŽ˜์ด์ง€ ์ด๋™

Join Page

  • ํšŒ์›๊ฐ€์ž… ์š”์ฒญ

Main Page

  • ๋กœ๊ทธ์•„์›ƒ ์š”์ฒญ
  • ํšŒ์›์ •๋ณด ์š”์ฒญ

OAuth2Callback Page

  • ์„œ๋ฒ„์ธก์—์„œ OAuth2 ๋กœ๊ทธ์ธ ์„ฑ๊ณต ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ
  • jwt๋ฅผ ์ฟ ํ‚ค ํ˜•ํƒœ๋กœ ์ „๋‹ฌ



๐Ÿ“„ Backend API

๊ธฐ๋Šฅ 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"
    } 



๐Ÿ”Ž Details

์ผ๋ฐ˜ ๋กœ๊ทธ์ธ

  1. ์ผ๋ฐ˜ ๋กœ๊ทธ์ธ ์š”์ฒญ
  2. ๋“ฑ๋กํ•œ ์‹œํ๋ฆฌํ‹ฐ ์„ค์ • ํด๋ž˜์Šค์—์„œ, UsernamePasswordAuthenticationFilter๋ฅผ ์ƒ์†๋ฐ›์€ LoginFilter์— ์„ค์ •ํ•œ URL๋กœ ์ธํ•ด LoginFilter.attemptAuthentication() ํ˜ธ์ถœ
    1. ์š”์ฒญ ๋ฐ”๋””์— ์žˆ๋Š” ์ด๋ฆ„๊ณผ ๋น„๋ฐ€๋นˆํ˜ธ -> UsernamePasswordAuthenticationToken ์ƒ์„ฑ
    2. ์ƒ์„ฑํ•œ ์ธ์ฆ ํ† ํฐ ์ธ์ž๋กœ ์ „๋‹ฌ๋ฐ›๋Š” AuthenticationManager.authenticate() ํ˜ธ์ถœ
  3. UserDetailsService ๋ฅผ ๊ตฌํ˜„ํ•œ ํด๋ž˜์Šค์—์„œ loadUserByUsername() ํ˜ธ์ถœํ•˜์—ฌ ์‹ค์ œ ์œ ์ € ๋กœ๋”ฉํ•˜๊ณ  ๋‚ด๋ถ€์ ์œผ๋กœ ์ธ์ฆ ์‹œ๋„
  4. LoginFilter์—์„œ ๊ฒฐ๊ณผ ์‘๋‹ต
    • ์ธ์ฆ์— ์„ฑ๊ณตํ•œ๋‹ค๋ฉด, successfulAuthentication() ํ˜ธ์ถœํ•˜์—ฌ jwt ๋‹ด์€ ์ฟ ํ‚ค ์‘๋‹ต
    • ์ธ์ฆ์— ์‹คํŒจํ•œ๋‹ค๋ฉด, unsuccessfulAuthentication() ํ˜ธ์ถœํ•˜์—ฌ ์‹คํŒจ ์‘๋‹ต

OAuth2 ๋กœ๊ทธ์ธ

  1. ์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ๊ฐ€ ์ œ๊ณตํ•˜๋Š” ์†Œ์…œ ๋กœ๊ทธ์ธ ์ธ์ฆ ํŽ˜์ด์ง€ ์š”์ฒญ
    • http(s)://{BACKEND IP:PORT}/oauth2/authorization/{์„œ๋น„์Šค๋ช…}/{client_id}/{redirect_url}
  2. ์†Œ์…œ ๋กœ๊ทธ์ธ ์š”์ฒญ
  3. ์†Œ์…œ ๋กœ๊ทธ์ธ ์ธ์ฆ ์„œ๋ฒ„์—์„œ ๋กœ๊ทธ์ธ ์„ฑ๊ณต ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ ์ฒ˜๋ฆฌ
    • ์†Œ์…œ ๋กœ๊ทธ์ธ ์„œ๋น„์Šค ๋“ฑ๋ก๊ณผ ์Šคํ”„๋ง ๋ถ€ํŠธ ์„ค์ • ํŒŒ์ผ์— ์ž‘์„ฑํ•œ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ url๋กœ ์ฒ˜๋ฆฌ ์ง„ํ–‰
    • http://{BACKEND IP:PORT}/login/oauth2/code/{์„œ๋น„์Šค๋ช…} -> ๊ด€๋ก€์ ์ธ URL ๋„ค์ด๋ฐ
  4. ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ๋กœ ๋“ค์–ด์˜จ code๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ OAuth2AuthorizationRequestRedirectFilter์™€ OAuth2LoginAuthenticationFilter๋ฅผ ํ†ตํ•ด ์†Œ์…œ ๋กœ๊ทธ์ธ ์ธ์ฆ ์„œ๋ฒ„์—๊ฒŒ access ํ† ํฐ ์š”์ฒญ
  5. OAuth2LoginAuthenticationProvider ๋ฅผ ํ†ตํ•ด, ๋ฐ›์€ access ํ† ํฐ์œผ๋กœ ์†Œ์…œ ๋กœ๊ทธ์ธ ๋ฆฌ์†Œ์Šค ์„œ๋ฒ„๋กœ๋ถ€ํ„ฐ ์œ ์ € ์ •๋ณด ํš๋“
  6. DefaultOAuth2UserService๋ฅผ ์ƒ์†ํ•œ CustomOAuth2UserService ํด๋ž˜์Šค์—์„œ loadUser() ํ˜ธ์ถœํ•˜์—ฌ ํ•ด๋‹น ์œ ์ € ์ •๋ณด ๋กœ๋”ฉ
  7. ๋“ฑ๋กํ•œ OAuth2LoginSuccessHandler ๋ฅผ ํ†ตํ•ด ์ธ์ฆ ์„ฑ๊ณต ์ฒ˜๋ฆฌ
    • jwt ์ธ์ฆ ์ฟ ํ‚ค ์‘๋‹ต
    • ํ•˜์ดํผ ๋งํฌ๋กœ ์š”์ฒญํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ๋”ฐ๋กœ ํ•ด๋‹น ์†Œ์…œ ๋กœ๊ทธ์ธ ํƒญ์„ ์ž„์˜๋กœ ๋‹ซ๊ฑฐ๋‚˜ ์‘๋‹ต์„ ์กฐํšŒํ•  ์ˆ˜ ์—†์œผ๋ฏ€๋กœ ์ฟ ํ‚ค ๋ฐฉ์‹์œผ๋กœ ์ „๋‹ฌํ•œ ๊ฒƒ์ด๊ณ , ํ•ด๋‹น ํƒญ์„ ์ปจํŠธ๋กคํ•˜๊ธฐ ์œ„ํ•ด ์ฝœ๋ฐฑ์šฉ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋งŒ๋“ค๊ณ  ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ ์‹œํ‚ด์œผ๋กœ์จ ์†Œ์…œ ๋กœ๊ทธ์ธ ์™„๋ฃŒ ํ›„ ํƒญ ๋‹ซ๊ธฐ

์ธ์ฆ API ํ˜ธ์ถœ

  1. ํšŒ์›์ •๋ณด ์š”์ฒญ API ํ˜ธ์ถœ with jwt ๋‹ด์€ ์ฟ ํ‚ค
  2. ์‹œํ๋ฆฌํ‹ฐ ์„ค์ • ํŒŒ์ผ์— ์ง€์ •ํ•œ requestMatchers().authenticated() ์— ๋”ฐ๋ผ์„œ ์ธ์ฆ ์ ์šฉ
    • ์ธ์ฆ์ด ์ ์šฉ๋œ URL์€ SecurityContextHolder ๋‚ด๋ถ€์— ์žˆ๋Š” Authentication ๊ฐ์ฒด์˜ ์ธ์ฆ ์—ฌ๋ถ€ ํ™•์ธ
  3. JwtFilter ์—์„œ ์ฟ ํ‚ค์— ๋‹ด๊ธด jwt ๊ฒ€์ฆ
    • ๊ฒ€์ฆ์— ์„ฑ๊ณตํ•œ๋‹ค๋ฉด, Authentication ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜์—ฌ SecurityContextHolder์— ์„ธํŒ… ํ›„ ํ•„ํ„ฐ ์ง„ํ–‰
    • ๊ฒ€์ฆ์— ์‹คํŒจํ•œ๋‹ค๋ฉด ํ˜น์€ ์ธ์ฆ ์ ์šฉ URL์ด ์•„๋‹ˆ๋ผ์„œ ํ† ํฐ์ด ์—†๋‹ค๋ฉด, ์•„๋ฌด๋Ÿฐ ๋™์ž‘๋„ ํ•˜์ง€ ์•Š๊ณ  ํ•„ํ„ฐ ์ง„ํ–‰

About

This repository contains tutorial code for implementing authentication and authorization with Spring Boot and React.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages