[์ธ์ฆ๊ณผ ์ธ๊ฐ]
์ฟ ํค์ ์ธ์
JWT
<์งค๋งํ bean>
ํน์, bean์ ์๋์ผ๋ก ๋ฑ๋กํด์ผ๋๋ ๊ฒฝ์ฐ๋ ์์๊น?
๊ธฐ์ ์ ์ธ ๋ฌธ์ ๋ ๊ณตํต ๊ด์ฌ์ฌ๋ฅผ ์ฒ๋ฆฌํ ๋ ์ฌ์ฉํ๋ ๊ฐ์ฒด๋ ์๋์ผ๋ก bean์ ๋ฑ๋กํด์ฃผ๋ ๊ฒ์ด ์ข๋ค. (๋น๊ต์ ๋ถ๊ฐ์ ์ธ ๊ฒ)
- @Component ๋ง๊ณ ๋ฉ์๋์ @Bean ์ ๋ํ ์ด์ ์ถ๊ฐํด์ฃผ๋ฉด ๋๋ค~!
์ธ์ฆ/์ธ๊ฐ
์ธ์ฆ: ํด๋น ์ ์ ๊ฐ ์ค์ ์ ์ ์ธ์ง ์ธ์ฆํ๋ ๊ฒ ex)๋ก๊ทธ์ธ
์ธ๊ฐ: ํด๋น ์ ์ ๊ฐ ํน์ ๋ฆฌ์์ค์ ์ ๊ทผ์ด ๊ฐ๋ฅํ์ง ํ๊ฐ๋ฅผ ํ์ธํ๋ ๊ฒ ex)๋นํ์๋ก๊ทธ์ธ
<์น ์ ํ๋ฆฌ์ผ์ด์ ์ธ์ฆ>
- ์ผ๋ฐ์ ์ผ๋ก ์๋ฒ-ํด๋ผ์ด์ธํธ ๊ตฌ์กฐ๋ก ๋์ด์๊ณ , ์ค์ ๋ก ์ด ๋๊ฐ์ง๋ ๋ฉ๋ฆฌ ๋จ์ด์ ธ์๋ค.
- HTTP ํ๋กํ ์ฝ์ ์ด์ฉํ์ฌ ํต์ ํ๋๋ฐ, ๊ทธ ํต์ ์ ๋น์ฐ๊ฒฐ์ฑ๊ณผ ๋ฌด์ํ๋ก ์ด๋ฃจ์ด์ง๋ค.
์ฝ๊ฒ ๋งํด, ๋ช๊ฐ์ง ๊ฒฝ์ฐ๋ฅผ ์ ์ธํ๊ณค ์๋ฒ์ ํด๋ผ์ด์ธํธ๋ ์ค์ ๋ก ์ฐ๊ฒฐ๋์ด ์๋ ๊ฒ์ด ์๋๋ค. (์๋ฒ ๋น์ฉ ๋ฌธ์ )
ํ์ง๋ง ์ฐ๋ฆฌ๊ฐ ์ฌ์ฉํ์ ๋๋ ์ผ๋ จ์ ์ฒ๋ฆฌ ๊ณผ์ ์ด ์ฐ๊ฒฐ๋์ด ์๋ค๊ณ ๋๊ปด์ง๋ค. ์ด๋ป๊ฒ ๊ฐ๋ฅํ ๊น?
# ์ ์ ๊ฐ ์ธ์ฆํ๋ ๋ฐฉ๋ฒ์ ์๋ก ๋ค์ด๋ณด์
์ธ์ฆ ๋ฐฉ์์๋ ํฌ๊ฒ ๋๊ฐ์ง๊ฐ ์๋ค.
1. ์ฟ ํค-์ธ์ ๋ฐฉ์
:'์ธ์ ์ ์ฅ์' ๋ผ๋ ๊ฐ๋ ์ด ์กด์ฌํ๊ณ ํด๋ผ์ด์ธํธ๊ฐ ๋ก๊ทธ์ธ์ ํด์ ํต๊ณผํ์ ๋ ์๋ฒ๋ก๋ถํฐ ์ธ์ ์์ด๋๋ฅผ ๋ฐ๊ธ๋ฐ๊ฒ ๋๊ณ
ํด๋ผ์ด์ธํธ๋ ํด๋น sessin-id ๋ฅผ '์ฟ ํค'๋ผ๋ ์ ์ฅ์์ ๋ณด๊ดํ๊ณ ๋ค์ ์์ฒญ์ ํ ๋๋ง๋ค session-id๋ฅผ ๊ฐ์ด ๋ณด๋ธ๋ค.(http header)
์ ์ด์ ์ธ์ ์์ด๋ ๋ฐ๊ธํด์ค ๋ ๊ทธ๊ฑธ ์ฟ ํค์ ๋ด์์ ์ฃผ๋๊ฑด๊ฐ? ๊ทธ ๋ค์์ ํด๋ผ์ด์ธํธ๊ฐ ๊ณ์ํด์ ๊ทธ๊ฑธ ์ฐ๊ณ ?
์๋๋ฐ? ์ธ์ ์์ด๋ ์์ด ์ ์ด์ ํด๋ผ์ด์ธํธ๊ฐ ์ฟ ํค ์์ฒด๋ฅผ ๊ฐ๊ณ ์๋๊ฑด๊ฐ?
<์ฟ ํค>
- ํด๋ผ์ด์ธํธ์ ์ ์ฅ๋ ๋ชฉ์ ์ผ๋ก ์์ฑํ ์ ๋ณด๋ฅผ ๋ด์ ํ์ผ
- ์ฌ๋ฌ ๊ตฌ์ฑ ์์๊ฐ ์๋๋ฐ ๊ฐ์ฅ ์ค์ํ ๋ถ๋ถ์ name, value ์ด๋ค.
- name: ์ฟ ํค๋ฅผ ์๋ณํ๋๋ฐ ์ฌ์ฉ๋๋ ํค (์ค๋ณต x)
- value: ์ฟ ํค์ ๊ฐ
<์ธ์ >
- ์๋ฒ์์ ์ผ์ ์๊ฐ๋์ ํด๋ผ์ด์ธํธ ์ํ๋ฅผ ์ ์งํ๊ธฐ ์ํด ์ฌ์ฉํ๋ค.
- ์๋ฒ์์ ํด๋ผ์ด์ธํธ๋ณ๋ก ์ ์ผ๋ฌด์ดํ '์ธ์ ID'๋ฅผ ๋ถ์ฌํ ํ ํด๋ผ์ด์ธํธ ๋ณ ํ์ํ ์ ๋ณด๋ฅผ ์๋ฒ์ ์ ์ฅ!
- ํด๋น ID๋ ํด๋ผ์ด์ธํธ์ ์ฟ ํค๊ฐ(์ธ์ ์ฟ ํค)์ผ๋ก ์ ์ฅ๋์ด ํด๋ผ์ด์ธํธ ์๋ณ์ ์ฌ์ฉ๋๋ค.
2. JWT (JSON Web Token)
: ์ธ์ฆ์ ํ์ํ ์ ๋ณด๋ฅผ ์ํธํ์ํจ ํ ํฐ์ด๋ค. 1๋ฒ ๋ฐฉ์๊ณผ ์ ์ฌํ๊ฒ ํด๋น ํ ํฐ(JSON ํํ์ claim ๊ธฐ๋ฐ ํ ํฐ์)์ผ๋ก ์๋ฒ๊ฐ ํด๋ผ์ด์ธํธ๋ฅผ ์๋ณํ๋ค.
-์๋ฒ์์ ๋ก๊ทธ์ธ ์ ๋ณด๋ฅผ ์ ์ฅํ๋ ๊ฒ์ด ์๋ ๋ก๊ทธ์ธ ์ ๋ณด๋ฅผ secret key๋ฅผ ์ฌ์ฉํด JWT๋ก ์ํธํํ๋ค. ํด๋น JWT ํ ํฐ์ ํตํด ์ธ์ฆ/์ธ๊ฐ๋ฅผ ์ฒ๋ฆฌํ๋ค.
<์ฌ์ฉ ํ๋ฆ>
1. ํด๋ผ์ด์ธํธ ๋ก๊ทธ์ธ ์ฑ๊ณต ์
- ์๋ฒ์์ ๋ก๊ทธ์ธ ์ ๋ณด๋ฅผ JWT๋ก ์ํธํํ๋ค.
- ๊ทธ๋ฆฌ๊ณ ์ง์ ์ฟ ํค๋ฅผ ์์ฑ, JWT๋ฅผ ๋ด์ Client ์๋ต์ ์ ๋ฌํ๋ค.
- ๋ธ๋ผ์ฐ์ ์ฟ ํค ์ ์ฅ์์ ์๋์ผ๋ก ํด๋น JWT ์ ์ฅ๋๋ค.
2. Client์์ JWT๋ฅผ ํตํด ์ธ์ฆํ๋ ๋ฐฉ์์?
- ํด๋ผ์ด์ธํธ๊ฐ API ์์ฒญ ์ ์๋ฒ์์๋ ๋งค ์์ฒญ๋ง๋ค ์ฟ ํค์ ํฌํจ๋ JWT๋ฅผ ์ฐพ๋๋ค. (์ฟ ํค์๋ ์ฌ๋ฌ ์ ๋ณด๊ฐ ๋ด๊ฒจ ์๋ค. ๊ทธ ์ค JWT๊ฐ ๋ด๊ธด ์ฟ ํค์ ์ด๋ฆ์ด ๋์ผํ์ง ํ์ธ ํ ๊ฐ์ ธ์จ๋ค.)
3. ์๋ฒ?
- ํด๋ผ์ด์ธํธ๊ฐ ์ ๋ฌํ JWT secret key ์ฌ์ฉํด ์์กฐ ์ฌ๋ถ๋ฅผ ๊ฒ์ฆ ๋ฐ ํด๋น ํ ํฐ์ ์ ํจ๊ธฐ๊ฐ์ ๊ฒ์ฆํ๋ค.
- ๊ฒ์ฆ ์ฑ๊ณต ์ ํด๋น ํ ํฐ์์ user ์ ๋ณด๋ฅผ ๊ฐ์ ธ์ ํ์ธํ๋ค.
JWT
: ๋๊ตฌ๋ ํ๋ฌธ์ผ๋ก ๋ณตํธํ๊ฐ ๊ฐ๋ฅํ๋ค. ๊ทธ๋ฌ๋ secret key๊ฐ ์์ผ๋ฉด JWT ์์ ์ด ๋ถ๊ฐํ๋ค.
๊ฒฐ๊ตญ JWT๋ Read only ๋ฐ์ดํฐ๋ค.
JWT ๋ค๋ฃจ๊ธฐ
dependency ์ถ๊ฐ ๋ฐ application properties์ jwt secret key ๋ถ๋ถ์ ์ถ๊ฐํด์ฃผ์ด์ผ ํ๋ค.
JWT ์ฌ์ฉ ์)
//๊ฐ๋
์ฑ์ ์ํด import ๋ ๋ฆผ
@Component
public class JwtUtil {
// Header KEY ๊ฐ
public static final String AUTHORIZATION_HEADER = "Authorization";
// ์ฌ์ฉ์ ๊ถํ ๊ฐ์ KEY
public static final String AUTHORIZATION_KEY = "auth";
// Token ์๋ณ์
public static final String BEARER_PREFIX = "Bearer ";
// ํ ํฐ ๋ง๋ฃ์๊ฐ
private final long TOKEN_TIME = 60 * 60 * 1000L; // 60๋ถ
@Value("${jwt.secret.key}") // Base64 Encode ํ SecretKey
private String secretKey;
private Key key;
private final SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
// ๋ก๊ทธ ์ค์
public static final Logger logger = LoggerFactory.getLogger("JWT ๊ด๋ จ ๋ก๊ทธ");
@PostConstruct
public void init() {
byte[] bytes = Base64.getDecoder().decode(secretKey);
key = Keys.hmacShaKeyFor(bytes);
}
// ํ ํฐ ์์ฑ
public String createToken(String username, UserRoleEnum role) {
Date date = new Date();
return BEARER_PREFIX +
Jwts.builder()
.setSubject(username) // ์ฌ์ฉ์ ์๋ณ์๊ฐ(ID)
.claim(AUTHORIZATION_KEY, role) // ์ฌ์ฉ์ ๊ถํ
.setExpiration(new Date(date.getTime() + TOKEN_TIME)) // ๋ง๋ฃ ์๊ฐ
.setIssuedAt(date) // ๋ฐ๊ธ์ผ
.signWith(key, signatureAlgorithm) // ์ํธํ ์๊ณ ๋ฆฌ์ฆ
.compact();
}
// JWT Cookie ์ ์ ์ฅ
public void addJwtToCookie(String token, HttpServletResponse res) {
try {
token = URLEncoder.encode(token, "utf-8").replaceAll("\\+", "%20"); // Cookie Value ์๋ ๊ณต๋ฐฑ์ด ๋ถ๊ฐ๋ฅํด์ encoding ์งํ
Cookie cookie = new Cookie(AUTHORIZATION_HEADER, token); // Name-Value
cookie.setPath("/");
// Response ๊ฐ์ฒด์ Cookie ์ถ๊ฐ
res.addCookie(cookie);
} catch (UnsupportedEncodingException e) {
logger.error(e.getMessage());
}
}
// JWT ํ ํฐ substring
public String substringToken(String tokenValue) {
if (StringUtils.hasText(tokenValue) && tokenValue.startsWith(BEARER_PREFIX)) {
return tokenValue.substring(7);
}
logger.error("Not Found Token");
throw new NullPointerException("Not Found Token");
}
// ํ ํฐ ๊ฒ์ฆ
public boolean validateToken(String token) {
try {
Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token);
return true;
} catch (SecurityException | MalformedJwtException | SignatureException e) {
logger.error("Invalid JWT signature, ์ ํจํ์ง ์๋ JWT ์๋ช
์
๋๋ค.");
} catch (ExpiredJwtException e) {
logger.error("Expired JWT token, ๋ง๋ฃ๋ JWT token ์
๋๋ค.");
} catch (UnsupportedJwtException e) {
logger.error("Unsupported JWT token, ์ง์๋์ง ์๋ JWT ํ ํฐ ์
๋๋ค.");
} catch (IllegalArgumentException e) {
logger.error("JWT claims is empty, ์๋ชป๋ JWT ํ ํฐ ์
๋๋ค.");
}
return false;
}
// ํ ํฐ์์ ์ฌ์ฉ์ ์ ๋ณด ๊ฐ์ ธ์ค๊ธฐ
public Claims getUserInfoFromToken(String token) {
return Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token).getBody();
}
}
'Spring' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
TIL Spring #3-3 (0) | 2023.12.18 |
---|---|
TIL Spring #3-2 (0) | 2023.12.15 |
TIL Spring #2-5 (0) | 2023.12.13 |
TIL Spring #2-4 (0) | 2023.12.12 |
TIL Spring #2-3 (0) | 2023.12.12 |