๋ฒ ์ด์ง๋ฐ
์ธ์ฆ์ธ๊ฐ ํ์ฉ(ํ ํฐ์ธ์ฆ๋ฐฉ์: JWT)
12/26 11:00 ~ 12:10 (์ฝ 1์๊ฐ 10๋ถ ์งํ)
JWT
- ์ํธํ๊ฐ ์๋๋ค!
- ๊ทธ๋ฅ ๋จ์ง ๊ธฐ์กด ํ ํฐ์ ๋ณ์กฐ๋ฅผ ๋ง๊ธฐ ์ํด์ JWT์ Signature๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด๋ค. → ๋ณ์กฐ๋ฅผ ๋ง๊ธฐ ์ํด์ ๋ค์ด๊ฐ๋ค.
- ์ฒซ ๋ฒ์งธ ์์ญ์ HEADER๋ผ๋ ๊ณณ์ด๋ค.
- ์๋ช ์ด ์ฌ์ฉ๋ ์๊ณ ๋ฆฌ์ฆ์ด ๋ฌด์์ธ์ง, ํ ํฐ์ ํ์ ์ด ๋ฌด์์ธ์ง ์๋ ค์ฃผ๋ ๊ตฌ์ญ์ด๋ค.
- ์ฐ์ฐํ ๋ ์ฌ์ฉ๋๋ ์๊ณ ๋ฆฌ์ฆ์ ๋ปํ๋ค.
- ๋ ๋ฒ์งธ ์์ญ์ PAYLOAD๋ผ๋ ๊ณณ์ด๋ค. → ๊ธฐ์กด ํ ํฐ์ ์ด๊ฒ๋ง ์์ผ๋ฉด ๋๋ค.
- ๋ฐ์ดํฐ์ด๋ค. ํ์ํ ๋ฐ์ดํฐ๊ฐ ๋ด๊ฒจ์๋ ๊ตฌ์ญ์ด๋ค.
- ์ธ ๋ฒ์งธ ์์ญ์ SIGNATUREํ๋ ๊ณณ์ด๋ค.(์๋ช
) → HEADER + PAYLOAD + ๋น๋ฐํค = SIGNATURE
- ๋ณ์กฐ๋ฅผ ๋ง๊ธฐ ์ํด์ ์๊ฒผ๋ค.
- ๋น๋ฐํค๋ ๋ณ์กฐ๊ฐ ๋์๋์ง ์๋์ง๋ฅผ ํ์ธํ๋ ๋ฐ ์ฐ์ด๋ ๊ธฐ์ ์ด๋ค.
ํ๋ฆ & ๊ตฌ์ฑ๋
1. ๊ธฐ๋ณธ ์คํ๋ง ์์ฒญ ์๋ต ์ฒ๋ฆฌ ํ๋ฆ๋
2. ์ธ์ฆ์ธ๊ฐ ํ๋ฆ๋
- ์๋ฌธ์ ํด๋ผ์ด์ธํธ๊ฐ Controller์ ์ ๊ทผํ๋ค.
- ๋ก๊ทธ์ธ์ ์ ๊ทผํด์ ํ ํฐ์ ๋ฐ๊ธ ๋ฐ๊ณ ์ธ์ฆ์ด ํ์ํ API์ ์ ๊ทผ์ ํ๊ฒ ๋๋ฉด ์ฌ์ฉํ ์ ์๋ค.
3. ์ค์ต: ํ ํฐ์์ ํ์ฉํ ๊ฐ์ฒด (JwtUtils)
public class JwtUtils {
public String createToken() {
// 1. ํ ํฐ ์์ฑ
// 2. ํ ํฐ ๋ฐํ
}
public Long extractStudentIdFromToken(String token) {
// 1. ํ ํฐ ํด๋
// 2. ์ ๋ณด ์ถ์ถ
// 3. ํ์ ์๋ณ์ ๋ฐํ
}
}
ํ ํฐ ๋ฐ๊ธ
1. ํ ํฐ ๋ฐ๊ธ ๋ก์ง: createToken()
@Component
public class JwtUtils {
// SECRET ์ ๊ฒฝ์ฐ ์คํ๋ง์ค์ ํ์ผ(application.properties) ์์ ์ฃผ์
๋ฐ์ ํ์ฉ๊ฐ๋ฅํฉ๋๋ค.
private static final String SECRET = "my-cat-is-flying"; // ์๊ตฌํธ(์ฐ๋ฆฌ๋ง ์๋ ์ํธ๋ผ๊ณ ์๊ฐํ๋ฉด ๋๋ค.)
...(์๋ต)...
/**
* jwt ํ ํฐ ๋ฐ๊ธ
*
* ํ ํฐ ์์ฑ์ ํ์ํ ์ ๋ณด๋ฅผ ๋งค๊ฐ๋ณ์๋ก ๋ฐ์ JWT ํ ํฐ์ ์์ฑํ๊ณ ๋ฐํํฉ๋๋ค.
* @param studentId ํ์ ์๋ณ์
* @param role ํ์ ๊ถํ
* @param dataYouWannaPut ํ ํฐ์ ๋ฃ๊ณ ์ถ์ ๋ฐ์ดํฐ
* @return token
* @throws UnsupportedEncodingException
*/
public String createToken(Long studentId, String role, String dataYouWannaPut) throws UnsupportedEncodingException {
// 1. ํ ํฐ ์๋ช
์ ํ์ฉ๋ ์๊ณ ๋ฆฌ์ฆ ์ค์
Algorithm algorithm = Algorithm.HMAC256(SECRET); // Header
// 2. ํ ํฐ ์์ฑ
String token = JWT.create()
.withIssuer("sparta.basic.com")
.withSubject(studentId.toString()) // "Key": "value" -> "์ง์ ": "์กฐ์ ๊ฐ๋ฅ"
.withClaim("role", role) // Key๋ ๋ด ๋ง์๋๋ก ์กฐ์ ๊ฐ๋ฅํ๋ค.
.withClaim("customField1", dataYouWannaPut)
.withClaim("customField2", "customFieldValue2")
.withIssuedAt(new Date())
.withExpiresAt(new Date(System.currentTimeMillis() + 3600 * 1000))
.sign(algorithm); // ์๋ช
// 3. ํ ํฐ ๋ฐํ
return token;
}
...(์๋ต)...
}
2. ์์ฑ๋ ํ ํฐ ๋ด์ฉ ์์(Json)
{
"sub": "student1",
"role": "admin",
"customField1": "customFieldValue1",
"customField2": "customFieldValue2",
"iss": "example.com",
"exp": 1735129767,
"iat": 1735126167
}
๋ฉ์๋ | ์์ | ์ค๋ช |
withIssuer( ) | www.example.com | ๋ฐ๊ธ์: JWT๋ฅผ ์์ฑํ ์์คํ ํน์ ์๋ฒ |
withSubject( ) | user1 | ์ฌ์ฉ์: JWT์ ์ฐ๊ด๋ ์ฌ์ฉ์ ID, ๋๊ตฌ๋ฅผ ์ํด ์์ฑ๋์๋์ง ํ์ |
withClaim( ) | admin | ์ปค์คํ ํ๋: ์ ํ๋ฆฌ์ผ์ด์ ์์ ํ์ํ ์ถ๊ฐ ์ ๋ณด๋ฅผ ์ ๊ณตํ๊ธฐ ์ํด ํ์ฉ |
withIssuedAt( ) | 175123152 | ๋ฐ๊ธ ์๊ฐ: ํ ํฐ์ ์์ฑ ์๊ฐ์ ํ์ |
withExpiresAt( ) | 1735119552 | ๋ง๋ฃ ์๊ฐ: ํ ํฐ์ ๋ง๋ฃ ์๊ฐ์ ํ์ |
- ์ํธํ๊ฐ ์๋๊ธฐ ๋๋ฌธ์ ๋ ธ์ถ๋๋ฉด ์๋๋ ์ ๋ณด(Password)๋ ๋ฃ์ผ๋ฉด ์๋๋ค!!
ํ ํฐ ํด๋
ํ ํฐ์ ํ์ฉํ๊ธฐ ์ํด์๋ ๋จผ์ ์๋ฒ๋ก ํ ํฐ์ ์ ์กํด์ผ ํ๋ค.
๋ณดํต Request Headers์ ํ ํฐ์ ๋ด์ ์ ์กํ๋ค.
1. ํด๋ผ์ด์ธํธ์์ ์๋ฒ๋ก ํ ํฐ์ ๋ณด๋ด๋ ๋ค์ํ ๋ฐฉ๋ฒ
a. ์ฟผ๋ฆฌ ํ๋ผ๋ฏธํฐ ํ์ฉ - Query Params
GET /api/resource?token=<JWT ํ ํฐ>
b. ์์ฒญ ๋ณธ๋ฌธ์ผ๋ก ์ ๋ฌ - Request Body
{
"token": "<JWT ํ ํฐ>"
}
c. ๊ธฐ๋ณธ ํค๋ ํ์ฉ - Request Header → ๋ง์ด ์ฌ์ฉํ๋ ๋ฐฉ์์ด๋ค.
Authorization: <JWT ํ ํฐ>
d. OAuth 2.0 ํ์ค → ๋ง์ด ์ฌ์ฉํ๋ ๋ฐฉ์์ด๋ค.
Authorization: Bearer <JWT ํ ํฐ>
2. ํ ํฐ ํด๋ (๊ธฐ๋ณธ ํค๋๋ฅผ ํ์ฉ)
@Component
public class JwtUtils {
// SECRET ์ ๊ฒฝ์ฐ ์คํ๋ง์ค์ ํ์ผ(application.properties) ์์ ์ฃผ์
๋ฐ์ ํ์ฉ๊ฐ๋ฅํฉ๋๋ค.
private static final String SECRET = "my-cat-is-flying";
private static final String BEARER_PREFIX = "Bearer";
...(์๋ต)...
/**
* ํ ํฐ์์ ํ์ ์๋ณ์๋ฅผ ์ถ์ถ ํฉ๋๋ค.
* @param token ์ผ๋ฐ ํ ํฐ
* @return studentId(Long) ํ์ ์๋ณ์
* @throws UnsupportedEncodingException
*/
public Long extractStudentIdFromToken(String token) throws UnsupportedEncodingException {
// 1. ํ ํฐ์ ํ์ฉํ ์๊ณ ๋ฆฌ์ฆ ์ค์
Algorithm algorithm = Algorithm.HMAC256(SECRET);
// 2. ํ ํฐ ๊ฒ์ฆ
DecodedJWT decodedToken = JWT.require(algorithm)
.withIssuer("sparta.basic.com")
.build()
.verify(token);
// 3. ํ ํฐ์์ ์ํ๋ ์ ๋ณด ์ถ์ถ
System.out.println("subject: " + decodedToken.getSubject());
System.out.println("role: " + decodedToken.getClaim("role").asString());
System.out.println("customField1: " + decodedToken.getClaim("customField1").asString());
System.out.println("customField2: " + decodedToken.getClaim("customField2").asString());
System.out.println("issued At: " + decodedToken.getIssuedAt());
System.out.println("expires At: " + decodedToken.getExpiresAt());
// 4. ํ์ ์๋ณ์ ๋ฐํ
String studentId = decodedToken.getSubject();
return Long.parseLong(studentId);
}
...(์๋ต)...
}
3. ํ ํฐ ํด๋ (OAuth 2.0 ํ์ค: Bearer)
@Component
public class JwtUtils {
// SECRET ์ ๊ฒฝ์ฐ ์คํ๋ง์ค์ ํ์ผ(application.properties) ์์ ์ฃผ์
๋ฐ์ ํ์ฉ๊ฐ๋ฅํฉ๋๋ค.
private static final String SECRET = "my-cat-is-flying";
private static final String BEARER_PREFIX = "Bearer";
...(์๋ต)...
/**
* ๋ฒ ์ด๋ฌ ํ ํฐ์์ ํ์ ์๋ณ์๋ฅผ ์ถ์ถ ํฉ๋๋ค.
* @param bearerToken
* @return studentId(Long) ํ์ ์๋ณ์
* @throws UnsupportedEncodingException
*/
public Long extractStudentIdFromBearerToken(String bearerToken) throws UnsupportedEncodingException {
// 1. ํ ํฐ์ ํ์ฉํ ์๊ณ ๋ฆฌ์ฆ
Algorithm algorithm = Algorithm.HMAC256(SECRET);
// 2. Bearer ํ ํฐ ์ถ์ถ
String token = bearerToken.substring(BEARER_PREFIX.length()).trim();
// 3. ํ ํฐ ๊ฒ์ฆ
DecodedJWT decodedToken = JWT.require(algorithm)
.withIssuer("sparta.basic.com")
.build()
.verify(token);
// 4. ํ ํฐ์์ ์ํ๋ ์ ๋ณด ์ถ์ถ
System.out.println("subject: " + decodedToken.getSubject());
System.out.println("role: " + decodedToken.getClaim("role").asString());
System.out.println("customField1: " + decodedToken.getClaim("customField1").asString());
System.out.println("customField2: " + decodedToken.getClaim("customField2").asString());
System.out.println("issued At: " + decodedToken.getIssuedAt());
System.out.println("expires At: " + decodedToken.getExpiresAt());
// 5. ํ์ ์๋ณ์ ๋ฐํ
String studentId = decodedToken.getSubject();
return Long.parseLong(studentId);
}
}
ํํฐ๋ ์ ์ฌ์ฉํ๋ ๊ฒ์ผ๊น?!
- ์ค๋ณต ์ฝ๋๋ฅผ ์ค์ด๊ธฐ ์ํด์ ํํฐ๋ฅผ ์ฌ์ฉํ๋ค.
- ์๋ต ์๋๋ ํจ์ฌ ๋ ๋น ๋ฅด๋ค.
- ํ์ง๋ง ํํฐ๋ฅผ ์ฐ์ง ์๋๋ค๊ณ ํด์ ํ๋ฆฌ๊ฒ ๊ตฌํํ๋ ๊ฒ์ ์๋๋ค.
์ ์ฒด ์ฝ๋
๋๋ณด๊ธฐ
JwtUtils.java
@Component
public class JwtUtils {
// SECRET ์ ๊ฒฝ์ฐ ์คํ๋ง์ค์ ํ์ผ(application.properties) ์์ ์ฃผ์
๋ฐ์ ํ์ฉ๊ฐ๋ฅํฉ๋๋ค.
private static final String SECRET = "my-cat-is-flying"; // ์ฐ๋ฆฌ๋ง ์๊ณ ์๋ ์๊ตฌํธ
private static final String BEARER_PREFIX = "Bearer";
/**
* jwt ํ ํฐ ๋ฐ๊ธ
*
* ํ ํฐ ์์ฑ์ ํ์ํ ์ ๋ณด๋ฅผ ๋งค๊ฐ๋ณ์๋ก ๋ฐ์ JWT ํ ํฐ์ ์์ฑํ๊ณ ๋ฐํํฉ๋๋ค.
* @param studentId ํ์ ์๋ณ์
* @param role ํ์ ๊ถํ
* @param dataYouWannaPut ํ ํฐ์ ๋ฃ๊ณ ์ถ์ ๋ฐ์ดํฐ
* @return token
* @throws UnsupportedEncodingException
*/
public String createToken(Long studentId, String role, String dataYouWannaPut) throws UnsupportedEncodingException {
// 1. ํ ํฐ ์๋ช
์ ํ์ฉ๋ ์๊ณ ๋ฆฌ์ฆ ์ค์
Algorithm algorithm = Algorithm.HMAC256(SECRET); // Header
// 2. ํ ํฐ ์์ฑ
String token = JWT.create()
.withIssuer("sparta.basic.com")
.withSubject(studentId.toString())
.withClaim("role", role)
.withClaim("customField1", dataYouWannaPut)
.withClaim("customField2", "customFieldValue2")
.withIssuedAt(new Date())
.withExpiresAt(new Date(System.currentTimeMillis() + 3600 * 1000))
.sign(algorithm); // ์๋ช
return token;
}
/**
* ํ ํฐ์์ ํ์ ์๋ณ์๋ฅผ ์ถ์ถ ํฉ๋๋ค.
* @param token ์ผ๋ฐ ํ ํฐ
* @return studentId(Long) ํ์ ์๋ณ์
* @throws UnsupportedEncodingException
*/
public Long extractStudentIdFromToken(String token) throws UnsupportedEncodingException {
// 1. ํ ํฐ์ ํ์ฉํ ์๊ณ ๋ฆฌ์ฆ ์ค์
Algorithm algorithm = Algorithm.HMAC256(SECRET);
// 2. ํ ํฐ ๊ฒ์ฆ
DecodedJWT decodedToken = JWT.require(algorithm)
.withIssuer("sparta.basic.com")
.build()
.verify(token);
// 3. ํ ํฐ์์ ์ํ๋ ์ ๋ณด ์ถ์ถ
System.out.println("subject: " + decodedToken.getSubject());
System.out.println("role: " + decodedToken.getClaim("role").asString());
System.out.println("customField1: " + decodedToken.getClaim("customField1").asString());
System.out.println("customField2: " + decodedToken.getClaim("customField2").asString());
System.out.println("issued At: " + decodedToken.getIssuedAt());
System.out.println("expires At: " + decodedToken.getExpiresAt());
// 4. ํ์ ์๋ณ์ ๋ฐํ
String studentId = decodedToken.getSubject();
return Long.parseLong(studentId);
}
/**
* ๋ฒ ์ด๋ฌ ํ ํฐ์์ ํ์ ์๋ณ์๋ฅผ ์ถ์ถ ํฉ๋๋ค.
* @param bearerToken
* @return studentId(Long) ํ์ ์๋ณ์
* @throws UnsupportedEncodingException
*/
public Long extractStudentIdFromBearerToken(String bearerToken) throws UnsupportedEncodingException {
// 1. ํ ํฐ์ ํ์ฉํ ์๊ณ ๋ฆฌ์ฆ
Algorithm algorithm = Algorithm.HMAC256(SECRET);
// 2. Bearer ํ ํฐ ์ถ์ถ
String token = bearerToken.substring(BEARER_PREFIX.length()).trim();
// 3. ํ ํฐ ๊ฒ์ฆ
DecodedJWT decodedToken = JWT.require(algorithm)
.withIssuer("sparta.basic.com")
.build()
.verify(token);
// 4. ํ ํฐ์์ ์ํ๋ ์ ๋ณด ์ถ์ถ
System.out.println("subject: " + decodedToken.getSubject());
System.out.println("role: " + decodedToken.getClaim("role").asString());
System.out.println("customField1: " + decodedToken.getClaim("customField1").asString());
System.out.println("customField2: " + decodedToken.getClaim("customField2").asString());
System.out.println("issued At: " + decodedToken.getIssuedAt());
System.out.println("expires At: " + decodedToken.getExpiresAt());
// 5. ํ์ ์๋ณ์ ๋ฐํ
String studentId = decodedToken.getSubject();
return Long.parseLong(studentId);
}
}
AuthController.java
@Slf4j
@RestController
@RequestMapping("/auth")
@RequiredArgsConstructor
public class AuthController {
private final JwtUtils jwtUtils;
/**
* ๋ก๊ทธ์ธ API
* ํ์์ email, password ๋ฅผ ๋ฐ์ ๋ก๊ทธ์ธ ์๋๋ฅผํฉ๋๋ค.
* ๋ก๊ทธ์ธ ์ฑ๊ณต์ JWT ํ ํฐ์ ์์ฑํด ํด๋ผ์ด์ธํธ๋ก ๋ฐํํฉ๋๋ค.
* @return token
* @throws UnsupportedEncodingException
*/
@GetMapping("/login")
public String loginAPI() throws UnsupportedEncodingException {
// 1. ๋ก๊ทธ์ธ์ ์ฑ๊ณตํ๋ค๊ณ ๊ฐ์ ํฉ๋๋ค.
// ๋ก๊ทธ์ธ ๋ก์ง ์
// 2. ํ ํฐ์ ๋ด์ ์ ๋ณด ์ค๋น
Long studentId = 1L; // ํ์ ์๋ณ์
String role = "student"; //
String dataThatYouWannaPut = "๋ด ๋ง์๋๋ก ๋ฐ์ดํฐ";
// 3. ํ ํฐ ๋ฐ๊ธ
String token = jwtUtils.createToken(studentId, role, dataThatYouWannaPut);
return token;
}
/**
* ์ธ์ฆ์ด ํ์ํ API
* ์ด API๋ ๋ก๊ทธ์ธํ ์ฌ์ฉ์๋ง ์ ๊ทผํ ์ ์๋ ์์ ์
๋๋ค.
* Authorization ํค๋์์ JWT๋ฅผ ์ถ์ถํ์ฌ ์ฌ์ฉ์๋ฅผ ์ธ์ฆํฉ๋๋ค.
* ์ฃผ์์ ํ์ฉํด์ ์ผ๋ฐ ํ ํฐ Bearer ํ ํฐ์ Authorization ํค๋์์ ์ถ์ถํด์ ํ์ฉํฉ๋๋ค.
*
* @param request Authorization ํค๋๋ฅผ ํฌํจํ ์์ฒญ ์ ๋ณด๋ฅผ ์ ๋ฌ ๋ฐ์ต๋๋ค.
* @throws UnsupportedEncodingException
*/
@GetMapping("/api")
public void authRequiredAPI(HttpServletRequest request) throws UnsupportedEncodingException {
// 1. requestHeader ์์ ํ ํฐ ์ถ์ถ
String token = request.getHeader("Authorization");
// 2. ํ ํฐ์์ ์ ๋ณด ์ถ์ถ
// 2-1. ์ผ๋ฐ ํ ํฐ ์ฌ์ฉํ ๊ฒฝ์ฐ
Long studentId = jwtUtils.extractStudentIdFromToken(token);
// 2-2.๋ฒ ์ด๋ฌ ํ ํฐ ์ฌ์ฉํ ๊ฒฝ์ฐ
// Long studentId = jwtUtils.extractStudentIdFromBearerToken(token);
// 3. ํ์ ์์ด๋ ํ์ฉ
log.info("ํ์ ์์ด๋๋: {}", studentId);
}
}
'๊ณต๋ถ..์คํฐ๋... > ์์ค๋ณ ํ์ต๋ฐ' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
์์ค๋ณ ํ์ต๋ฐ_๋ฒ ์ด์ง๋ฐ 5ํ์ฐจ ์ธ์ (1) | 2024.12.22 |
---|---|
์์ค๋ณ ํ์ต๋ฐ_๋ฒ ์ด์ง๋ฐ 4ํ์ฐจ ์ธ์ (0) | 2024.12.21 |
์์ค๋ณ ํ์ต๋ฐ_๋ฒ ์ด์ง๋ฐ 3ํ์ฐจ ์ธ์ (0) | 2024.12.16 |
์์ค๋ณ ํ์ต๋ฐ_๋ฒ ์ด์ง๋ฐ 2ํ์ฐจ ์ธ์ (0) | 2024.12.11 |
์์ค๋ณ ํ์ต๋ฐ_๋ฒ ์ด์ง๋ฐ 1ํ์ฐจ ์ธ์ (0) | 2024.12.07 |