html
Mastering Spring Boot OAuth2 JWT Token Generation: A Comprehensive Guide
๋ชฉ์ฐจ
- ์๊ฐ
- OAuth2 ๋ฐ JWT ์ดํด
- Spring Boot ์ ํ๋ฆฌ์ผ์ด์ ๋ณด์
- JWT ํ ํฐ ์์ฑ ๊ตฌํ
- Authentication Manager ๊ตฌ์ฑ
- AuthController ์์ฑ
- TokenService ์ค๋ช
- ๊ฒฐ๋ก
์๊ฐ
์ค๋๋ ์ ๋์งํธ ํ๊ฒฝ์์ ์น ์ ํ๋ฆฌ์ผ์ด์ ๋ณด์์ ๋งค์ฐ ์ค์ํฉ๋๋ค. ๋ง์ดํฌ๋ก์๋น์ค์ ๋ถ์ฐ ์์คํ ์ ์ฆ๊ฐ๋ก ์ธํด ์๋น์ค ๊ฐ์ ์์ ํ ํต์ ์ ๋ณด์ฅํ๋ ๊ฒ์ด ์ ์ ๋ ๋ณต์กํด์ง๊ณ ์์ต๋๋ค. ์ด eBook์ Spring Boot OAuth2 JWT Token Generation์ ๋ํด ์์ธํ ๋ค๋ฃจ๋ฉฐ, ์ด๋ณด์์ ๊ธฐ๋ณธ ์ง์์ ๊ฐ์ง ๊ฐ๋ฐ์๋ค์ด ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ฒฌ๊ณ ํ ๋ณด์ ๋ฉ์ปค๋์ฆ์ ๊ตฌํํ ์ ์๋๋ก ํฌ๊ด์ ์ธ ๊ฐ์ด๋๋ฅผ ์ ๊ณตํฉ๋๋ค.
์ OAuth2์ JWT์ธ๊ฐ?
OAuth2๋ ์ ํ๋ฆฌ์ผ์ด์ ์ด HTTP ์๋น์ค์ ์ฌ์ฉ์ ๊ณ์ ์ ๋ํ ์ ํ๋ ์ก์ธ์ค๋ฅผ ์ป์ ์ ์๋๋ก ํ๋ ๋๋ฆฌ ์ฑํ๋ ๊ถํ ๋ถ์ฌ ํ๋ ์์ํฌ์ ๋๋ค. JSON Web Tokens (JWT)์ ๊ฒฐํฉ๋๋ฉด ์ฌ์ฉ์ ์ธ์ฆ ๋ฐ ๊ถํ ๋ถ์ฌ๋ฅผ ํจ์จ์ ์ผ๋ก ์ํํ ์ ์๋ ๋งค๋๋ฌ์ด ๋ฐฉ๋ฒ์ ์ ๊ณตํฉ๋๋ค.
์ฅ๋จ์
์ฅ์ | ๋จ์ |
---|---|
ํ์ฅ ๊ฐ๋ฅํ ๋ณด์: ๋๊ท๋ชจ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ ํฉํฉ๋๋ค. | ๋ณต์กํ ๊ตฌ์ฑ: ์ด๊ธฐ ์ค์ ์ด ๋ณต์กํ ์ ์์ต๋๋ค. |
๋ฌด์ํ ์ธ์ฆ: ์๋ฒ ์ธก ์ธ์ ์ ์ ๊ฑฐํ์ฌ ์ฑ๋ฅ์ ํฅ์์ํต๋๋ค. | ํ ํฐ ๊ด๋ฆฌ: ํ ํฐ ์ ์ฅ ๋ฐ ๊ฐฑ์ ์ ์ ์คํ๊ฒ ์ฒ๋ฆฌํด์ผ ํฉ๋๋ค. |
์ํธ ์ด์ฉ์ฑ: ๋ค์ํ ํ๋ซํผ๊ณผ ์๋น์ค์์ ์ ์๋ํฉ๋๋ค. | ํ ํฐ ํฌ๊ธฐ: JWT๋ ์๋์ ์ผ๋ก ํด ์ ์์ด ๋คํธ์ํฌ ์ฑ๋ฅ์ ์ํฅ์ ๋ฏธ์น ์ ์์ต๋๋ค. |
OAuth2์ JWT๋ฅผ ์ธ์ ์ด๋์ ์ฌ์ฉํด์ผ ํ๋?
- ๋ง์ดํฌ๋ก์๋น์ค ์ํคํ ์ฒ: ์๋น์ค ๊ฐ์ ์์ ํ ํต์ ์ ์ด์งํฉ๋๋ค.
- Single Page Applications (SPAs): ๋ฌด์ํ ์ธ์ฆ ๋ฉ์ปค๋์ฆ์ ์ ๊ณตํฉ๋๋ค.
- API ๊ฐ๋ฐ: API ์๋ํฌ์ธํธ์ ๋ํ ์์ ํ ์ก์ธ์ค๋ฅผ ๋ณด์ฅํฉ๋๋ค.
OAuth2 ๋ฐ JWT ์ดํด
๊ตฌํ์ ๋ฐ์ด๋ค๊ธฐ ์ ์ OAuth2 ๋ฐ JWT์ ๊ธฐ๋ณธ ๊ฐ๋ ์ ์ดํดํ๋ ๊ฒ์ด ์ค์ํฉ๋๋ค.
OAuth2 ๊ฐ์
OAuth2๋ ์ ํ๋ฆฌ์ผ์ด์ ์ด HTTP ์๋น์ค์ ์ฌ์ฉ์ ๊ณ์ ์ ๋ํ ์ ํ๋ ์ก์ธ์ค๋ฅผ ์ป์ ์ ์๋๋ก ํ๋ ๊ถํ ๋ถ์ฌ ํ๋ ์์ํฌ์ ๋๋ค. ์ด๋ ์ฌ์ฉ์ ์ธ์ฆ์ ์ฌ์ฉ์ ๊ณ์ ์ ํธ์คํ ํ๋ ์๋น์ค์ ์์ํ๊ณ ์๋ํํฐ ์ ํ๋ฆฌ์ผ์ด์ ์ด ์ฌ์ฉ์ ๊ณ์ ์ ์ ๊ทผํ ์ ์๋๋ก ๊ถํ์ ๋ถ์ฌํจ์ผ๋ก์จ ์๋ํฉ๋๋ค.
JWT ๊ฐ์
JSON Web Tokens (JWT)๋ ๋ ๋น์ฌ์ ๊ฐ์ ์ฃผ์ฅ์ ๋ํ๋ด๋ ๊ฐ๊ฒฐํ๊ณ URL-safeํ ํ ํฐ์ ๋๋ค. ์ธ ๋ถ๋ถ์ผ๋ก ๊ตฌ์ฑ๋ฉ๋๋ค:
- Header: ํ ํฐ ์ ํ๊ณผ ์ฌ์ฉ๋ ํด์ฑ ์๊ณ ๋ฆฌ์ฆ์ ํฌํจํฉ๋๋ค.
- Payload: ์ฃผ์ฅ ๋๋ ๋ฐ์ดํฐ๋ฅผ ํฌํจํฉ๋๋ค.
- Signature: ํ ํฐ์ ๋ฌด๊ฒฐ์ฑ์ ๋ณด์ฅํฉ๋๋ค.
Spring Boot ์ ํ๋ฆฌ์ผ์ด์ ๋ณด์
Spring Boot ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ณด์ํ๋ ๊ฒ์ ์ธ์ฆ ๋ฐ ๊ถํ ๋ถ์ฌ ๋ฉ์ปค๋์ฆ์ ์ค์ ํ๋ ๊ฒ์ ํฌํจํฉ๋๋ค. OAuth2์ JWT๋ฅผ ์ฌ์ฉํ์ฌ ํ ์คํธ API๋ฅผ ๋ณด์ํ๋ ๋ฐฉ๋ฒ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
๋ณด์ ์๊ตฌ ์ฌํญ ์ถ๊ฐ
API ์๋ํฌ์ธํธ๋ฅผ ๋ณด์ํ๊ธฐ ์ํด ๋ณด์ ์๊ตฌ ์ฌํญ ์ฃผ์์ ์ถ๊ฐํ ์ ์์ต๋๋ค. ์๋ฅผ ๋ค์ด:
1 |
@SecurityRequirement(name = "SteadyEasy-Demo-API") |
์ด ์ฃผ์์ HTTP๋ฅผ ์ฌ์ฉํ ์ธ์ฆ์ ๊ธฐ๋ฐ์ผ๋ก ํ๋ Bearer ํ ํฐ ์ ํ์ ์ฌ์ฉํ๋ "SteadyEasy-Demo-API"๋ผ๋ ์ฌ์ ์ ์๋ ๋ณด์ ์คํค๋ง๋ฅผ ํ์ฉํฉ๋๋ค.
REST API์ ํ๊ทธ ํตํฉ
ํ๊ทธ๋ ํน์ API๋ฅผ ๋ถ๋ฅํ๊ณ ์ค๋ช ํ๋ ๋ฐ ๋์์ด ๋์ด ๊ฐ๋ ์ฑ๊ณผ ์กฐ์ง์ ํฅ์์ํต๋๋ค. ์๋ฅผ ๋ค์ด:
1 |
@Tag(name = "Authentication", description = "Endpoints for user authentication") |
JWT ํ ํฐ ์์ฑ ๊ตฌํ
JWT ํ ํฐ ์์ฑ์ ์ ํ๋ฆฌ์ผ์ด์ ๋ณด์์ ์ค์ํ ์ธก๋ฉด์ ๋๋ค. ์ด๋ ํด๋ผ์ด์ธํธ๊ฐ ์ดํ ์์ฒญ์ ์ธ์ฆํ๋ ๋ฐ ์ฌ์ฉํ ์ ์๋ ํ ํฐ์ ์์ฑํ๋ ๊ฒ์ ํฌํจํฉ๋๋ค.
Authentication Manager ์์ฑ
Authentication Manager๋ ์ธ์ฆ ํ๋ก์ธ์ค๋ฅผ ์ฒ๋ฆฌํ๋ ์ญํ ์ ํฉ๋๋ค. ์ค์ ๋ฐฉ๋ฒ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค:
1 2 3 4 |
@Bean public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception { return config.getAuthenticationManager(); } |
์ค๋ช
- AuthenticationConfiguration: ์ธ์ฆ ์ค์ ์ ๊ตฌ์ฑํฉ๋๋ค.
- AuthenticationManager: ์ธ์ฆ ์์ฒญ์ ๊ด๋ฆฌํฉ๋๋ค.
Authentication Manager ๊ตฌ์ฑ
์ ์ ํ ๊ตฌ์ฑ์ ์ ํ๋ฆฌ์ผ์ด์ ๋ด์์ ์ธ์ฆ์ด ์ํํ๊ฒ ํ๋ฅด๋๋ก ๋ณด์ฅํฉ๋๋ค.
๋จ๊ณ๋ณ ๊ตฌ์ฑ
- Authentication Manager๋ฅผ ์ํ Bean ์ ์:
1234@Beanpublic AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {return config.getAuthenticationManager();} - ๊ธฐ๋ณธ ๋ฉ์ปค๋์ฆ ์ฌ์ ์:
Spring Boot๊ฐ ์ ๊ณตํ๋ ๊ธฐ๋ณธ ์ธ์ฆ ๋ฉ์ปค๋์ฆ์ ์ ํ๋ฆฌ์ผ์ด์ ์ ์๊ตฌ ์ฌํญ์ ๋ง๊ฒ ์ฌ์ฉ์ ์ ์ํฉ๋๋ค.
์ค์์ฑ
๊ตฌ์ฑ์ด ์๋ชป๋๋ฉด ์ธ์ฆ ์คํจ๋ ๋ณด์ ์ทจ์ฝ์ ์ด ๋ฐ์ํ ์ ์์ต๋๋ค. ๊ฐ๋ ฅํ ๋ณด์์ ๋ณด์ฅํ๊ธฐ ์ํด ์ธ์ฆ ํ๋ฆ์ ์ดํดํ๋ ๊ฒ์ด ํ์์ ์ ๋๋ค.
AuthController ์์ฑ
AuthController๋ ์ฑ๊ณต์ ์ธ ๋ก๊ทธ์ธ ์ JWT ํ ํฐ์ ์์ฑํ๋ ๋ฑ์ ์ธ์ฆ ๊ด๋ จ ์์ฒญ์ ์ฒ๋ฆฌํฉ๋๋ค.
AuthController ๊ตฌํ
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
@RestController public class AuthController { @Autowired private AuthenticationManager authenticationManager; @Autowired private TokenService tokenService; @PostMapping("/token") public ResponseEntity<String> token(@RequestBody AuthRequest authRequest) { Authentication authentication = authenticationManager.authenticate( new UsernamePasswordAuthenticationToken(authRequest.getUsername(), authRequest.getPassword()) ); String token = tokenService.generateToken(authentication); return ResponseEntity.ok(token); } } |
์ค๋ช
- @RestController: ์ด ํด๋์ค๊ฐ REST API ์์ฒญ์ ์ฒ๋ฆฌํจ์ ๋ํ๋ ๋๋ค.
- @PostMapping("/token"): /token
์๋ํฌ์ธํธ์ POST ์์ฒญ์ ๋งคํํฉ๋๋ค.
- AuthenticationManager: ์ ๊ณต๋ ์๊ฒฉ ์ฆ๋ช ์ ์ธ์ฆํฉ๋๋ค.
- TokenService: ์ธ์ฆ์ด ์ฑ๊ณตํ๋ฉด JWT ํ ํฐ์ ์์ฑํฉ๋๋ค.
TokenService ์ค๋ช
TokenService๋ JWT ํ ํฐ์ ์์ฑํ๋ ์ญํ ์ ํฉ๋๋ค. ํ ํฐ์ ์์ฑํ๊ณ ๊ด๋ฆฌํ๋ ๋ฐ ํ์ํ ๋ ผ๋ฆฌ๋ฅผ ์บก์ํํฉ๋๋ค.
TokenService ๊ตฌํ
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 |
@Service public class TokenService { private final JWTEncoder encoder; public TokenService(JWTEncoder encoder) { this.encoder = encoder; } public String generateToken(Authentication authentication) { Instant now = Instant.now(); String scope = authentication.getAuthorities().stream() .map(GrantedAuthority::getAuthority) .collect(Collectors.joining(" ")); JWTClaimsSet claims = new JWTClaimsSet.Builder() .issuer("self") .issuedAt(Date.from(now)) .expirationTime(Date.from(now.plus(1, ChronoUnit.HOURS))) .subject(authentication.getName()) .claim("scope", scope) .build(); return encoder.encode(JWTParameters.from(claims)).getTokenValue(); } } |
๋จ๊ณ๋ณ ๋ถ์
- ์ข
์์ฑ ์ฃผ์
:
- JWTEncoder: JWT ์ฃผ์ฅ์ ํ ํฐ์ผ๋ก ์ธ์ฝ๋ฉํฉ๋๋ค.
- generateToken ๋ฉ์๋:
- Instant.now(): ํ์ฌ ์๊ฐ์ ์บก์ฒํฉ๋๋ค.
- Scope ์ถ์ถ:
- ์ธ์ฆ ๊ฐ์ฒด์์ ๊ถํ(์ญํ )์ ๊ฐ์ ธ์ต๋๋ค.
- ๊ณต๋ฐฑ์ผ๋ก ๊ตฌ๋ถ๋ ๋จ์ผ ๋ฌธ์์ด๋ก ๋งคํํ๊ณ ๊ฒฐํฉํฉ๋๋ค.
- JWTClaimsSet ๊ตฌ์ฑ:
- issuer: ํ ํฐ ๋ฐ๊ธ์๋ฅผ ์๋ณํฉ๋๋ค.
- issuedAt: ํ ํฐ ์์ฑ ํ์์คํฌํ์ ๋๋ค.
- expirationTime: ํ ํฐ ์ ํจ ๊ธฐ๊ฐ(์: 1์๊ฐ).
- subject: ์ธ์ฆ๋ ์ฌ์ฉ์์ ์ฌ์ฉ์ ์ด๋ฆ์ ๋๋ค.
- scope: ์ฌ์ฉ์ ์ญํ /๊ถํ์ ๋๋ค.
- ํ ํฐ ์ธ์ฝ๋ฉ:
- ์ ๊ณต๋ ์ธ์ฝ๋๋ฅผ ์ฌ์ฉํ์ฌ ์ฃผ์ฅ์ JWT ํ ํฐ์ผ๋ก ์ธ์ฝ๋ฉํฉ๋๋ค.
- ํ ํฐ ๊ฐ์ ๋ฐํํฉ๋๋ค.
์ฝ๋ ์ฃผ์
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 |
// ์ด ํด๋์ค๋ฅผ ์๋น์ค ๊ณ์ธต ์ปดํฌ๋ํธ๋ก ํ์ํ๋ ์๋น์ค ์ฃผ์ @Service public class TokenService { // JWT ์ธ์ฝ๋๋ฅผ ์ํ ์ต์ข
๋ณ์ private final JWTEncoder encoder; // JWTEncoder์ ์ข
์์ฑ ์ฃผ์
์ ์ํ ์์ฑ์ public TokenService(JWTEncoder encoder) { this.encoder = encoder; } // ์ธ์ฆ ์ธ๋ถ ์ ๋ณด์ ๊ธฐ๋ฐํ JWT ํ ํฐ์ ์์ฑํ๋ ๋ฉ์๋ public String generateToken(Authentication authentication) { // ํ์ฌ ํ์์คํฌํ๋ฅผ ๊ฐ์ ธ์ต๋๋ค. Instant now = Instant.now(); // ์ฌ์ฉ์ ๊ถํ์ ์ถ์ถํ๊ณ ๊ฒฐํฉํฉ๋๋ค. String scope = authentication.getAuthorities().stream() .map(GrantedAuthority::getAuthority) .collect(Collectors.joining(" ")); // JWT ์ฃผ์ฅ ๊ตฌ์ถ JWTClaimsSet claims = new JWTClaimsSet.Builder() .issuer("self") // ํ ํฐ์ ๋ฐ๊ธ์ .issuedAt(Date.from(now)) // ํ ํฐ ์์ฑ ์๊ฐ .expirationTime(Date.from(now.plus(1, ChronoUnit.HOURS))) // ํ ํฐ ๋ง๋ฃ ์๊ฐ .subject(authentication.getName()) // ์ฃผ์ (์ฌ์ฉ์ ์ด๋ฆ) .claim("scope", scope) // ์ฌ์ฉ์ ์ญํ .build(); // JWT ํ ํฐ์ ์ธ์ฝ๋ฉํ๊ณ ๋ฐํํฉ๋๋ค. return encoder.encode(JWTParameters.from(claims)).getTokenValue(); } } |
ํ๋ก๊ทธ๋จ ์ถ๋ ฅ
์ฑ๊ณต์ ์ธ ์ธ์ฆ ํ, AuthController๋ JWT ํ ํฐ์ ๋ฐํํฉ๋๋ค. ์์ ์ถ๋ ฅ:
1 |
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... |
์ด ํ ํฐ์ ์ดํ API ์์ฒญ์์ ๋ณดํธ๋ ๋ฆฌ์์ค์ ์ ๊ทผํ๊ธฐ ์ํด Authorization ํค๋์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
๊ฒฐ๋ก
์ด ๊ฐ์ด๋๋ OAuth2 ๋ฐ JWT๋ฅผ ์ฌ์ฉํ์ฌ Spring Boot ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ณด์ํ๋ ์์ธํ ๊ณผ์ ์ ์ ๊ณตํ์ต๋๋ค. ๊ธฐ๋ณธ ๊ฐ๋ ์ดํด๋ถํฐ JWT ํ ํฐ ์์ฑ ๊ตฌํ๊น์ง, ๊ฐ ๋จ๊ณ๋ ์ ํ๋ฆฌ์ผ์ด์ ์ด ์ต์ ๋ณด์ ํ์ค์ ์ค์ํ๋๋ก ๋ณด์ฅํฉ๋๋ค.
์ฃผ์ ์์ :
- OAuth2๋ ๊ถํ ๋ถ์ฌ๋ฅผ ์ํ ๊ฐ๋ ฅํ ํ๋ ์์ํฌ๋ฅผ ์ ๊ณตํฉ๋๋ค.
- JWT ํ ํฐ์ ๋ฌด์ํ ๋ฐ ํ์ฅ ๊ฐ๋ฅํ ์ธ์ฆ์ ์ฉ์ดํ๊ฒ ํฉ๋๋ค.
- Spring Boot์ ๋ณด์ ๊ตฌ์ฑ ์์์ ๋ํ ์ ์ ํ ๊ตฌ์ฑ ๋ฐ ์ดํด๊ฐ ํ์์ ์ ๋๋ค.
- ์ฃผ์์ด ๋ฌ๋ฆฐ ์ฝ๋๋ ๊ฐ๋ ์ฑ๊ณผ ์ ์ง ๊ด๋ฆฌ๋ฅผ ํฅ์์ํต๋๋ค.
์ด ๊ฐ์ด๋๋ฅผ ๋ฐ๋ฅด๋ฉด ๊ฐ๋ฐ์๋ค์ ์์ ํ๊ณ ํจ์จ์ ์ธ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ณด์ฅํ๋ ์์ ํ ์ธ์ฆ ๋ฉ์ปค๋์ฆ์ ๊ตฌํํ ์ ์์ต๋๋ค.
์ฐธ๊ณ : ์ด ๊ธฐ์ฌ๋ AI์ ์ํด ์์ฑ๋์์ต๋๋ค.