์ ํฌ์คํ ์์ ์์กด์ฑ ์ถ๊ฐ,
์ด์ ๋ฐ๊ธ์ & ๋น๋ฐํค ์ค์ ํ๋ค์
๊ทธ๊ฑธ ๋ณ์๋ก ๊ฐ๊ณ ์ค๋ ํด๋์ค (JwtProperties.java) ๋ง๋ค์์
โ
์ด๋ฒ์๋ ํ ํฐ ์์ฑํ๊ณ , ์ ํจ์ฑ ๊ฒ์ฆํ๊ณ ,
ํ ํฐ์์ ํ์ํ ์ ๋ณด ๋นผ์ค๋ ํด๋์ค ์์ฑํ ๊ฑฐ์
โ
์ด๋ฆ์ TokenProvider.java
์์น๋
config/jwt ๋๋ ํ ๋ฆฌ ๋ด
/*
ํ ํฐ ์์ฑ & ์ฌ๋ฐ๋ฅธ ํ ํฐ์ธ์ง ์ ํจ์ฑ ๊ฒ์ฌ
ํ ํฐ์์ ํ์ํ ์ ๋ณด ๊ฐ์ ธ์ค๋ ํด๋์ค
*/
@RequiredArgsConstructor
@Service
public class TokenProvider {
private final JwtProperties jwtProperties;
public String generateToken(User user, Duration expiredAt) {
Date now = new Date();
return makeToken(new Date(now.getTime() + expiredAt.toMillis()), user);
}
// JWT ํ ํฐ ์์ฑ ๋ฉ์๋
private String makeToken(Date expiry, User user) {
Date now = new Date();
return Jwts.builder()
.setHeaderParam(Header.TYPE, Header.JWT_TYPE)
.setIssuer(jwtProperties.getIssuer())
.setIssuedAt(now)
.setExpiration(expiry)
.setSubject(user.getEmail())
.claim("id", user.getId())
.signWith(SignatureAlgorithm.HS256, jwtProperties.getSecretKey())
.compact();
}
// JWT ํ ํฐ ์ ํจ์ฑ ๊ฒ์ฆ ๋ฉ์๋
public boolean validToken(String token) {
try{
Jwts.parser().setSigningKey(jwtProperties.getSecretKey())
.parseClaimsJws(token);
return true;
} catch (Exception e){
return false;
}
}
// ํ ํฐ ๊ธฐ๋ฐ์ผ๋ก ์ธ์ฆ ์ ๋ณด๋ฅผ ๊ฐ์ ธ์ค๋ ๋ฉ์๋
public Authentication getAuthentication(String token) {
Claims claims = getClaim(token);
Set<SimpleGrantedAuthority> authorities = Collections.singleton(new SimpleGrantedAuthority("ROLE_USER"));
return new UsernamePasswordAuthenticationToken(new org.springframework.security.core.userdetails.User(claims.getSubject(), "", authorities), token, authorities);
}
// ํ ํฐ ๊ธฐ๋ฐ์ผ๋ก ์ ์ ID ๊ฐ์ ธ์ค๋ ๋ฉ์๋
public Long getUserId(String token) {
Claims claims = getClaim(token);
return claims.get("id", Long.class);
}
private Claims getClaim(String token) {
return Jwts.parser()
.setSigningKey(jwtProperties.getSecretKey())
.parseClaimsJws(token)
.getBody();
}
}
์ฑ ์ ์๋ ์ฝ๋ ๊ทธ๋๋ก์ธ๋ฐ,
import ๋ช๊ฐ ์๋ชปํด์ ์๋ฌ๋ฌ์์
โ
์ฉ๋ฑ๋ง์๊ฑฐ importํ์ง์๊ฒ ์กฐ์ฌํ์ผ
โ
โ
์ด์ ํ์ค์ฉ ๋ฏ์ด์ ๋ถ์ํด๋ณด๊ฒ์ผ
public String generateToken(User user, Duration expiredAt) {
Date now = new Date();
return makeToken(new Date(now.getTime() + expiredAt.toMillis()), user);
}
์ ์ผ ๋จผ์ ํ ํฐ ๋ง๋๋ ํจ์
generateToken ๋ฉ์๋
์ธ์๋ก user๋ ๋ง๋ฃ์ผ์ ๋ฐ์์ ๋ง๋ ๋ค
return ํ์ ์ String์ด๊ณ
makeToken๋ฉ์๋๋ฅผ ํตํด ๋ง๋ ํ ํฐ์ returnํจ
์ด์ makeToken()๋ฉ์๋๋ฅผ ๋ณด๋ฉด
// JWT ํ ํฐ ์์ฑ ๋ฉ์๋
private String makeToken(Date expiry, User user) {
Date now = new Date();
return Jwts.builder()
.setHeaderParam(Header.TYPE, Header.JWT_TYPE)
.setIssuer(jwtProperties.getIssuer())
.setIssuedAt(now)
.setExpiration(expiry)
.setSubject(user.getEmail())
.claim("id", user.getId())
.signWith(SignatureAlgorithm.HS256, jwtProperties.getSecretKey())
.compact();
}
์ด๊ฒ ์ด์ ๋ฑ JWT ํ ํฐ์ ๋ง๋๋ ๋ฉ์ธ ํจ์์
โ
โ
expiry๋ user๋ฅผ ๋งค๊ฐ๋ณ์๋ก ๋ฐ์.
expiry๋ new Date(now.getTime() + expiredAt.toMillis())
๋ก generateToken์์ ๋ฃ์ด์ฃผ๊ณ ์์.
โ
์ฆ, ํ์ฌ ์๊ฐ now๋ก๋ถํฐ expiredAt๋งํผ ๋ํ ์์ = ๋ง๋ฃ ์๊ฐ(expiry) ์ ๊ณ์ฐ
์ ๋ฆฌํ์๋ฉด
expiry๋ JWT์ ๋ง๋ฃ์๊ฐ์ ์๋ ค์ฃผ๋ Duration๊ฐ์ฒด์!
โ
์ฐธ๊ณ ๋ก
Jwts.builder() ๋ถ๋ถ์ JWT ํ ํฐ์ "์ง์ ๊ตฌ์ฑ(Build)"ํ๋ ๊ณณ
โ
โ
.setHeaderParam(Header.TYPE, Header.JWT_TYPE)
ํค๋ ํ์ ์ JWT๋ก ์ง์ ํ๊ณ
โ
.setIssuer(jwtProperties.getIssuer())
propertiesํ์ผ์์ ์ง์ ํ ์ด์์ด ๊ฐ์ผ๋ก ์ง์ ํจ
โ
.setIssuedAt(now)
.setExpiration(expiry)
๋ง๋ค์ด์ง ์๊ฐ, expiry(์ ํจ์๊ฐ) ๋ฃ์ด์ฃผ๊ณ ์๊ณ
โ
.setSubject(user.getEmail())
์ ์ ์ด๋ฉ์ผ ๋ฃ์ด์ฃผ๊ณ ์์
์ฌ์ฉ์ ์๋ณ ์ ๋ณด
์ธ์ฆ ๊ณผ์ ์์ ใ ์ด ๊ฐ์ ๊ธฐ์ค์ผ๋ก ์ฌ์ฉ์ ์กฐํํ ์๋์๋ค๊ณ ํจ
โ
โ
.claim("id", user.getId())
์ฟจ๋์ id = ์ ์ ID
์ปค์คํ ํด๋ ์ ๋ฃ๋ ๋ถ๋ถ.
id๋ผ๋ ํค์ ์ ์ ID๋ฅผ ๋ฃ๊ณ ์์.
์ํ๋ ์ ๋ณด๋ฅผ ํค-๊ฐ ํํ๋ก ์ฝ์ ๊ฐ๋ฅ
(ex. claim("role", "ADMIN") ๋ฑ..)
โ
.signWith(SignatureAlgorithm.HS256, jwtProperties.getSecretKey())
ํ ํฐ์ ๋์งํธ ์๋ช
์ ์๋ช ์ ํตํด์ ๋ณ์กฐ ์ฌ๋ถ ํ๋จ ๊ฐ๋ฅ
โ
.compact();
์ง๊ธ๊น์ง ์ค์ ํ ๋ชจ๋ ์ ๋ณด๋ฅผ ํ๋์ JWT ๋ฌธ์์ด(String)์ผ๋ก ์์ถํด์
๋ฐํ!!!!
โ
๊ทธ๋ผ ์ต์ข ์ ์ผ๋ก
aaaaa.bbbbb.cccc
์ด๋ฐ ์ฐ๋ฆฌ๊ฐ ์๋ JWTํ ํฐ ๋ชจ์์๊ฐ ๋จ.
โ
โ
๋ค์์ JWTํ ํฐ ์ ํจ์ฑ ๊ฒ์ฌํ๋ ๋ฉ์๋
// JWT ํ ํฐ ์ ํจ์ฑ ๊ฒ์ฆ ๋ฉ์๋
public boolean validToken(String token) {
try{
Jwts.parser().setSigningKey(jwtProperties.getSecretKey())
.parseClaimsJws(token);
return true;
} catch (Exception e){
return false;
}
}
โ
Jwts.parser().setSigningKey(jwtProperties.getSecretKey())
.parseClaimsJws(token);
์ฐ๋ฆฌ๊ฐ propertiesํ์ผ์์ ์ง์ ํด๋จ๋
secret_key๋ก ๋ณตํธํ ํ๋ ๊ณผ์ ์.
โ
๋ณตํธ ๊ณผ์ ์์ ์๋ฌ ์๋๋ฉด true ๋ฐํํจ.
// ํ ํฐ ๊ธฐ๋ฐ์ผ๋ก ์ธ์ฆ ์ ๋ณด๋ฅผ ๊ฐ์ ธ์ค๋ ๋ฉ์๋
public Authentication getAuthentication(String token) {
Claims claims = getClaim(token);
Set<SimpleGrantedAuthority> authorities = Collections.singleton(new SimpleGrantedAuthority("ROLE_USER"));
return new UsernamePasswordAuthenticationToken(new org.springframework.security.core.userdetails.User(claims.getSubject(), "", authorities), token, authorities);
}
์์์ ๋ง๋ token์ ๋งค๊ฐ๋ณ์๋ก ๋ฐ์์
์ธ์ฆ ์ ๋ณด๋ฅผ ๋ด์ Authentication๊ฐ์ฒด๋ฅผ ๋ฐํํ๋ ๋ฉ์๋์.
โ
Claims claims = getClaim(token);
JWT ํ ํฐ ํ์ฑํด์ JWTํ์ด๋ก๋(body)๋ด์ฉ์ Claims ๊ฐ์ฒด ๋ก ๋ฐ์์ด
์ฌ์ฉ์ ์ด๋ฉ์ผ์ด ๋ค์ด์๋ ํ ํฐ ์ ๋ชฉ sub์ ํ ํฐ ๊ธฐ๋ฐ์ผ๋ก
์ธ์ฆ ์ ๋ณด๋ฅผ ์์ฑ.
์ด ์์ subject, id, exp..๋ฑ ์์
โ
โ
โ
Set<SimpleGrantedAuthority> authorities = Collections.singleton(new SimpleGrantedAuthority("ROLE_USER"));
์ ์ ์๊ฒ ๋ถ์ฌ๋ ๊ถํ ๋ชฉ๋ก์ ์ค์ ํจ
(์ฌ๊ธฐ์๋ ROLE_USER ํ๋๋ง ์ค์ ํ์)
โ
โ
return new UsernamePasswordAuthenticationToken(new org.springframework.security.core.userdetails.User(claims.getSubject(), "", authorities), token, authorities);
์ด ์ค์ด ํต์ฌ์ธ๋ฐ,
์ต์ข ์ ์ผ๋ก Authentication ๊ฐ์ฒด ๋ง๋ค์ด์ ๋ฐํํ๋๊ฑฐ์
( ๋ณดํต ์ด getAuthentication()๋ฉ์๋๋
JWT ํํฐ ๋ด์์ ํธ์ถ๋จ.
Authentication๊ฐ์ฒด๋ฅผ SecurityContext์ ๋ฑ๋กํด์ฃผ๋๊ฒ. )
โ
์ธ์๊ฐ ์ธ๊ฐ์ธ๋ฐ,
UserDetail ๊ฐ์ฒด, token, authorities
์ด๋ ๊ฒ ์ธ๊ฐ์.
์งํผํฐ ๋๋ ค๋ณด๋๊น
(principle, credentials, authorities๋ผ๊ณ ํจ)
โ
โ
์ฒซ๋ฒ์งธ๋ถํฐ ๋ณด๋ฉด
principle์ "์ธ์ฆ๋ ์ฌ์ฉ์ ์ ๋ณด" ๋ผ๊ณ ํจ.
new org.springframework.security.core.userdetails.User(claims.getSubject(), "", authorities)
์คํ๋ง ์ํ๋ฆฌํฐ ๋ด๋ถ์ ์ผ๋ก ์ฐ๋ UserDetail ๊ฐ์ฒด ๋ง๋ค๊ณ ์์.
UserDetail ๊ฐ์ฒด ๋๊ฒจ์, ๋์ค์ ์ปจํธ๋กค๋ฌ์์ @AuthenticationPrinciple๋ก ์ ์ ์ ๋ณด
๋ฝ์๋ด์ ์ธ ์ ์๊ฒ ํด์ค.
โ
claims.getSubject() ๋ ํ ํฐ์์ ์ถ์ถํ ์ด๋ฉ์ผ or ์ ์ ๋ค์ (์๋ณ๋๋๊ฑฐ. ๋ด ๊ฒฝ์ฐ email)
โ
"" : ๋น ๋ฌธ์์ด. ๋น๋ฒ ํ์ ์์ผ๋๊น
์ด๊ฑฐ ์๋ฃ๋?
UserDetail ๊ฐ์ฒด์ธ User๋ง๋ค ๋ ๋น๋ฒ๋ ๋ฃ์ด์ผ๋จ.
๊ทผ๋ฐ ์ฐ๋ฆฌ๋ JWT ๋ฐฉ์์ผ๋ก ํ๊ณ ์์ด์ ์๋ฌด ์๋ฏธ ์์
๋ ธ์๋ฏธ. ๊ทธ๋์ ๋น ๋ฌธ์์ด ๋ฃ๋๊ฑฐ์.
โ
authorites : ์ด ์ ์ ์ ๊ถํ๋ค. ์ฌ๊ธฐ์๋ ROLE_USER
โ
โ
๋๋ฒ์งธ ์ธ์ ๋ณด๋ฉด
token
โ
credentials๋ผ๊ณ ํจ.
์ฌ์ฉ์๊ฐ ์ธ์ฆํ ๋ ์ ์ถํ ์๊ฒฉ ์ฆ๋น. ์ด๋ผ๊ณ ํฉ๋๋ค.
์ ํต์ ์ธ ๋ก๊ทธ์ธ์ด๋ผ๋ฉด ์ฌ๊ธฐ์ ๋น๋ฐ๋ฒํธ๊ฐ ๋ค์ด๊ฐ์ผ ํ๋ค๊ณ ํจ.
โ
๊ทผ๋ฐ ์ฐ๋ฆฐ JWT ๊ธฐ๋ฐ ๋ก๊ทธ์ธ ํ๋ ์ค์ด๋ผ
ํ ํฐ ์์ฒด๊ฐ ์ธ์ฆ ์๋จ์ด๋ผ์
๊ฑ token๋ฌธ์์ด ๊ทธ๋๋ก ๋ฃ๋๊ฑฐ๋ผ๊ณ ํฉ๋๋ค.
โ
โ
โ
์ธ๋ฒ์งธ ์ธ์ ๋ณด๋ฉด
authorities
โ
์์์
Collections.singleton(new SimpleGrantedAuthority("ROLE_USER"))
๋ก ๊ถํ ๋ชฉ๋ก ๋ง๋ค์์.
โ
์ง๊ธ์ ํ๋์ฝ๋ฉ์ผ๋ก ROLE_USERํ๋๋ง ๋ฃ์ ์ํ๊ธดํจ.
โ
์ด ์ฌ์ฉ์๊ฐ ์ด๋ค ๊ถํ์ ๊ฐ๊ณ ์๋๊ฐ?๋ฅผ
์๊ฑธ๋ก ์ฒดํฌํจ.
// ํ ํฐ ๊ธฐ๋ฐ์ผ๋ก ์ ์ ID ๊ฐ์ ธ์ค๋ ๋ฉ์๋
public Long getUserId(String token) {
Claims claims = getClaim(token);
return claims.get("id", Long.class);
}
ํ ํฐ ๊ธฐ๋ฐ์ผ๋ก user์ ID๋ฅผ ๊ฐ์ ธ์ค๋ ๋ฉ์๋์
JWTํ ํฐ์์ ์ ์ ID๋ฅผ ๊บผ๋ด๊ธฐ ์ํ ํจ์.
โ
getClaimํจ์ ํธ์ถํด์ ํด๋ ์ ์ ๋ณด ๋ฐ๊ณ ,
๊ทธ ํด๋ ์์์ id ํค๋ก ์ ์ฅ๋ ๊ฐ ๊ฐ์ ธ์์ ๋ฐํํจ.
โ
๊ทธ๋ ๋ค๋ฉด ์๊น ์์์ Authentication๊ฐ์ฒด ๋ง๋ค ๋๋ userId ๊บผ๋ด์ค์ง ์์๋
์ด ํจ์๊ฐ ์ ํ์ํ๋?
โ
๊ทธ๊ฑด Authentication๊ฐ์ฒด ์์ฐ๊ณ ํ ํฐ์์ ์ ์ ID๋ง ์ง์ ๊บผ๋ด๊ณ ์ถ์๋๊ฐ
์๊ธฐ ๋๋ฌธ!
โ
์๊น ์์์ ํ๊ฑด, Authentication๊ฐ์ฒด๋ฅผ
SecurityContext์ ๋ฑ๋กํด์ ๊บผ๋ด๋ณด๋ ์ฉ๋์์.
โ
โ
โ
๋ ๊ถ๊ธํ๊ฒ
Authentication๊ฐ์ฒด์ JWT ํ ํฐ์ ์ญํ ์ด ํผ๋๋๊ธฐ ์์ํจ
โ
getAuthentication()๋ฉ์๋ ์ญํ ์ด ์ธ์ฆ ์ ๋ณด ์์ฑ์ด๋ผ๊ณ ํ๋๋ฐ
์ธ์ฆ ์ ๋ณด = JWT์๋๊ฐ?

๋ผ๊ณ ํ์ญ๋๋ค.
โ
JWT๋, ์ด ์ฌ์ฉ์๊ฐ ์ธ์ฆ ๋ฐ์๋ค๋ ์ฆ๊ฑฐ๋ฅผ ๋ด๋ฏธ๋ ํฐ์ผ๊ฐ์๊ฑฐ๊ณ
Authentication๊ฐ์ฒด๋ ์ธ์ฆ ์ํ ํํ ๊ฐ์ฒด..๋ผ๊ณ ํจ.
โ
์ฆ getAuthentication()์ด ํ๋ ์ผ์,
JWT ํ ํฐ์ ๊ธฐ๋ฐ์ผ๋ก,
์ฌ์ฉ์ ์ ๋ณด๋ฅผ ๋ณตํธ!ํด์ SpringSecurity๊ฐ ๋ด๋ถ์ ์ผ๋ก ์ดํดํ ์ ์๋
Authentication๊ฐ์ฒด๋ฅผ ๋ง๋๋ ๊ฑฐ์.
โ
โ
โ
โ
+
๋ ๊ถ๊ธํ๋๊ฒ
UsernamePasswordAuthenticationToken์ด๊ฑฐ์์.
์ด๊ฑธ returnํ๊ณ ์๋๋ฐ, ํ์ ์ Authentication์
โ
UsernamePasswordAuthenticationToken์ Authentiation๊ตฌํ์ฒด ์ค ํ๋๋ผ๊ณ ํฉ๋๋ค.
โ
๊ทธ๋์
UsernamePasswordAuthenticationToken(Object principal, Object credentials, Collection authorities)
์ด๋ฐ ๋๋์ผ๋ก ๋ง๋๋๊ฑด๋ฐ,
SpringContextHolder๋ ์ธ์ฆ๋ ์ฌ์ฉ์๋ฅผ UserDetails ํ์ ์ผ๋ก ์ ์ฅํใด๋ค๊ณ ํจ.
๊ทธ๋์ JWT๋ก๋ถํฐ ๋ฝ์๋ธ ์ฌ์ฉ์ ์ ๋ณด( claims.getSubject(), "", authorities )
๋ฅผ UserDetailํํ๋ก ๋ง๋ค์ด์ ๋ฃ๋๊ฒ.
โ
โ
โ
โ
โ
๊ทธ๋ ๋ค๊ณ ํฉ๋๋ค.
โ
๋ ๋ณต์กํจ ใ
๋ด์ผ ๋ค์ ์ ๋ฆฌํด์ผ์ง
์ ๋ ์ง๊ธ ๊ท์ฐฎ์์ ๊ทธ๋ฐ๊ฑด ์๋