[부트캠프] TIL - 인증, 인가, JWT
인증과 인가
인증 : 해당 유저가 실제 그 유저가 맞는지 확인하는 과
인가 : 유저가 특정 리소스에 접근하는것에 대한 허가를 확인하는 과정
동작과정
1. 사용자가 로그인 요청을 보냅니다.
2. 서버는 DB의 유저 테이블을 뒤져서 아이디 비밀번호를 대조해봐야겠죠?
3. 실제 유저테이블의 정보와 일치한다면 인증을 통과한 것으로 보고 유저의 정보를 JWT로 암호화 해서 내보냅니다.
4. 서버는 로그인 요청의 응답으로 jwt 토큰을 내어줍니다.
5. 클라이언트는 그 토큰을 저장소에 보관하고 앞으로의 요청마다 토큰을 같이 보냅니다.
6. 클라이언트의 요청에서 토큰을 발견했다면 서버는 토큰을 검증합니다.
7. 이후에는 로그인 된 유저에 따른 응답을 내어줍니다.
JWT란
Json Web Token
Json 포맷을 사용하여 사용자에 대한 속성을 저장하는 Claim 기반 Web Token 일반적으로 쿠키 저장소를 사용하여 JWT를 저장.
JWT 장점
- 동시접속자가 많을 경우 서버의 부하를 낮춤
- Client, Server가 다른 도메인을 사용할 때
단점
- 구현복잡도 증가
- JWT 내용이 커질수록 네트워크 비용증가
- 생성된 JWT 일부만 만료시킬 방법이 없다 ( 보관위치가 클라이언트이므로 서버측에서는 조치할수가없음)
- Secret Key 유출시 JWT 조작이 가능
JWT 전달방식은 개발자가 정함.
예시) 응답 Header 에 다음형태로 전달하면 브라우저의 쿠키 저장소에 자동으로 JWT가 저장.
Cookie cookie = new Cookie(AUTHORIZATION_HEADER, token); // Name-Value
cookie.setPath("/");
// Response 객체에 Cookie 추가
res.addCookie(cookie);
쿠키에서 JWT를 꺼내는 방법
// HttpServletRequest 에서 Cookie Value : JWT 가져오기
public String getTokenFromRequest(HttpServletRequest req) {
Cookie[] cookies = req.getCookies();
if(cookies != null) {
for (Cookie cookie : cookies) {
if (cookie.getName().equals(AUTHORIZATION_HEADER)) {
try {
return URLDecoder.decode(cookie.getValue(), "UTF-8"); // Encode 되어 넘어간 Value 다시 Decode
} catch (UnsupportedEncodingException e) {
return null;
}
}
}
}
return null;
}
쿠키에 담긴 정보가 여러개일 수 있으므로, 이름이 JWT가 담긴 쿠키의 이름과 동일한지 확인하여 JWT를 가져옵니다.
서버에 전달하면 서버는 JWT의 위조여부를 Secret Key로 검증합니다.
만료기간도 검사합니다.
검증과정을 거쳐 인증이 되면 JWT에서 사용자 정보를 가져와 확인합니다.
JWT 구조
JWT는 평문으로 누구나 복호화가 가능합니다.
하지만 Secret KEy가 없으면 수이 불가능.
결국 JWT는 Read Only Data
JWTUtil 클래스
JWTUtil 클래스 예제
@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();
}
여기에 담긴 정보의 한 조각을 Claim 이라고 하며 key -value 쌍으로 저장하고 토큰에는 여러개의 Claim이 저장가능.
저부분을 일부 수정하면 토큰 생성이 가능.