인증(Authentication)과 인가(Authorization) 란?
인증(Authentication)과 인가(Authorization) 란?
인증 ▼해당 유저가 실제 유저인지 인증하는 개념지문인식, 로그인 등 실제 유저가 맞는지 확인하는 장치인가 ▼해당 유저가 특정 리소스에 접근이 가능한지 허가를 확인하는 개념관리자 페이
seungg8361.tistory.com
JWT를 사용하기 전에 의존성과 설정을 추가해 주자
build.gradle ▼
compileOnly group: 'io.jsonwebtoken', name: 'jjwt-api', version: '0.11.5'
runtimeOnly group: 'io.jsonwebtoken', name: 'jjwt-impl', version: '0.11.5'
runtimeOnly group: 'io.jsonwebtoken', name: 'jjwt-jackson', version: '0.11.5'
application.properties ▼
jwt.secret.key={비밀 키 설정} // ex) base64 인코딩이 되어있는 비밀 키
먼저 사용자 권한을 관리하기 위한 enum을 생성해 보자
// enum은 상수의 집합 클래스
public enum UserRoleEnum {
// 각 상수의 열거형 인스턴스
USER(Authority.USER), // 사용자 권한
ADMIN(Authority.ADMIN); // 관리자 권한
private final String authority;
UserRoleEnum(String authority) {
this.authority = authority;
}
// ex) UserRoleEnum.USER.getAuthority -> "ROLE_USER"를 반환한다.
public String getAuthority() {
return this.authority;
}
public static class Authority {
public static final String USER = "ROLE_USER";
public static final String ADMIN = "ROLE_ADMIN";
}
}
JWT를 사용하는 단계 ▼
JwtUtil.java 파일을 생성해서 관리해 보자.
0. JWT 데이터
// Header KEY 값 ex) 쿠키의 name
public static final String AUTHORIZATION_HEADER = "Authorization";
// 사용자 권한 값의 KEY ex) admin, 일반 유저
public static final String AUTHORIZATION_KEY = "auth";
// Token 식별자 : 토큰 앞에 붙음 => 해당하는 값은 토큰이다 라는 규칙
// Bearer는 JWT나 Oauth에 대한 토큰을 사용한다는 뜻.
public static final String BEARER_PREFIX = "Bearer ";
private final long TOKEN_TIME = 60 * 60 * 1000L; // 토큰 만료 시간 ex) 60분
// application.properties에 설정한 값을 가져온다
@Value("${jwt.secret.key}") // Base64 Encode 한 SecretKey
private String secretKey;
private Key key; // secret 키를 담을 객체
// enum으로 되어있음.
private final SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
// 딱 한 번만 받아오면 되는 값을 사용할 때마다 요청을 새로 받아오는 것을 막기위해
// JwtUtil 클래스의 생성자 호출한 뒤에 key필드에 secretKey를 담기위해
@PostConstruct
public void init() {
// base64로 인코딩 되어 있는 secretKey를 디코딩해서 사용
byte[] bytes = Base64.getDecoder().decode(secretKey);
// HMAC 알고리즘에 적합한 키를 생성 ex) HS256, HS384, HS512와 같은 표준 HMAC 서명 알고리즘에 사용
key = Keys.hmacShaKeyFor(bytes);
}
1. JWT 생성
public String createToken(String username, UserRoleEnum role) {
Date date = new Date();
return BEARER_PREFIX +
Jwts.builder() // Jwts라는 클래스에 빌더를 사용 ex) JWT 토큰이 생성이 된다.
.setSubject(username) // 사용자 식별자 값(ID)
.claim(AUTHORIZATION_KEY, role) // 사용자 권한 (앞에는 key, 뒤에는 value)
.setExpiration(new Date(date.getTime() + TOKEN_TIME)) // 만료 시간
.setIssuedAt(date) // 발급일
// 앞에는 secretKey값, 뒤에는 암호화 알고리즘
.signWith(key, signatureAlgorithm) // 암호화 알고리즘
.compact();
}
2. 생성된 JWT를 Cookie에 저장
public void addJwtToCookie(String token, HttpServletResponse response) {
try {
// URLEncoder를 사용해서 url에서 사용되지 않는 문자들을 %와 특수기호로 변환
// token -> 인코딩할 문자열, utf-8 -> 사용할 문자 집합
// 쿠키에 저장하기 전에 공백제거
token = URLEncoder.encode(token, "utf-8").replaceAll("\\+", "%20");
// "Authorization" 에는 token의 값이 들어있다.
Cookie cookie = new Cookie(AUTHORIZATION_HEADER, token); // key-value
cookie.setPath("/");
// Response 객체에 Cookie 추가하여 브라우저로 반환하면 브라우저의 쿠키 저장소에 저장됨.
// 개발자 모드에 Set-cookie 부분에 "Authorization=Bearer="로 저장되고 name은 Authorization으로 저장됨.
response.addCookie(cookie);
} catch (UnsupportedEncodingException e) {
logger.error(e.getMessage());
}
}
3. Cookie에 들어있는 JWT 토큰을 Substring
public String substringToken(String tokenValue) {
log.info("substringToken");
// 공백과 null인지 Bearer로 시작 하는지 안하는지 확인
if (StringUtils.hasText(tokenValue) && tokenValue.startsWith(BEARER_PREFIX)) {
return tokenValue.substring(7); // 순수한 토큰을 뽑아내기 위해 Bearer, 공백을 포함햔 7자 인덱싱
}
logger.error("Not Found Token");
throw new NullPointerException("Not Found Token");
}
4. JWT 검증
public boolean validateToken(String token) {
try {
// Jwts.parserBuilder를 사용하여 JWT를 파싱할 수 있음.
// 검증, setSigningKey -> 서명 검증에 사용할 비밀키를 설정, parseClaimsJws -> 서명 검증에 성공하면 Jws<Claims> 객체 반환
// Jws(Json Web Signature) : 서명된 JWT 토큰, Claims : Payload 부분
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;
}
5. JWT에서 사용자 정보 가져오기
public Claims getUserInfoFromToken(String token){
// Jwts.parserBuilder() 와 secretKey 를 사용하여 JWT 의 Claims 를 가져와 담겨있는 사용자의 정보를 사용
return Jwts.parserBuilder()
.setSigningKey(key).build()
.parseClaimsJws(token)
.getBody(); // claims에 있는 정보들 반환
}
기타. 필터 구현 시 Cookie 값에서 JWT 가져오기
// HttpServletRequest 에서 Cookie Value : JWT 가져오기
public String getTokenFromRequest(HttpServletRequest res){
Cookie[] cookies = res.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;
}
'🔥스파르타 TIL (Spring)' 카테고리의 다른 글
Spring Security 란? (0) | 2025.02.10 |
---|---|
필터(Filter) 란? (0) | 2025.02.10 |
인증(Authentication)과 인가(Authorization) 란? (0) | 2025.02.09 |
Bean 이란? (0) | 2025.02.09 |
JPA 란? (2) - Spring Data JPA (0) | 2025.02.08 |