[BE-STUDY] JWT

soohee·2022년 1월 7일
0

BE-STUDY

목록 보기
4/7

📕 JWT의 개념

jwt는 Json Web Token의 줄임말로, Json 포맷을 이용해 payload를 사용자에 맞게 바꾸고 저장하는 Clain기반의 Web Token이다. 토큰 자체에 정보를 저장한다.
구조는 Header, Payload, Signature가 있고, Base64로 인코딩되어 표현된다. Header는 쉽게 말해서, 토큰의 타입이 무엇인지, 어떤 인코딩방식을 사용하는지를 나타내준다. Payload에는 사용자 정의 data가 들어가 있다. Signature에는 토큰을 인코딩하거나 유효성 검증할때 사용하는 고유한 암호화 코드이다.

1. Header

헤더에는 typ과 alg 두가지 정보가 담겨있다.
typ은 토큰의 타입 (거의 jwt를 쓴다)을 의미하고, alg는 서명 및 토큰 검증에 사용되는 알고리즘 방식을 의미한다.

{ 
   "alg": "HS256", 
   "typ": JWT
}

2. Payload

payload에는 사용자가 담을 정보들이 claim으로 들어가 있다. 정해진 claim 형식만 있는 줄 알았는데, 프로젝트를 하다보니, 개인적으로 payload 형태를 만들어서 생성할 수도 있다는 것을 깨달았다.

2-1. 등록된 claim

  • iss: 토큰 발급자(issuer)
  • sub: 토큰 제목(subject)
  • aud: 토큰 대상자(audience)
  • exp: 토큰 만료 시간(expiration), NumericDate 형식으로 되어 있어야 함 ex) 1480849147370
  • nbf: 토큰 활성 날짜(not before), 이 날이 지나기 전의 토큰은 활성화 되지 않음
  • iat: 토큰 발급 시간(issued at), 토큰 발급 이후의 경과 시간을 알 수 있음
  • jti: JWT 토큰 식별자(JWT ID), 중복 방지를 위해 사용하며, 일회용 토큰(Access Token) 등에 사용

이러한 claim key 값들이 있는데, 실제 프로젝트에서는 사용하지 않았다. (CustomClaim 사용)

2-2. 커스텀 claim

TokenPayload struct {
	PhoneNumber string 
	Type        string 
	UserId      uint   
}

위와 같이 원하는 정보를 담을 수 있는 payload를 만든 후,

TokenClaims struct {
	TokenPayload
	jwt.StandardClaims
}

아래와 같이 claims를 만들어 주면된다. 굳이 형식을 이렇게 할 필요는 없다. 그저 예시일 뿐.. (jwt.StandardClaims는 expire 시간을 설정해주기 위해 넣어주었다.)

그런다음에,

func GenerateToken(data jwt.Claims) (string, error) {
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, data)

	ss, err := token.SignedString("SECRET"))
	if err != nil {
		return "", err
	}
	return ss, nil
}

i.e. 위와 같은 토큰생성함수를 짜서, token을 만들어 주면된다.
해당 토큰은 위 claims 형식에 맞는 data를 hs256알고리즘으로 인코딩해서 생성한 뒤 "SECRET"이란 string으로 암호화한 token을 return해준다고 보면된다.

3. Signature

토큰을 인코딩하거나 유효성 검증할 때, 쓰는 암호화 코드이다.
2-2 에서 string "SECRET"에 해당한다.
middleware써서, 아래와 같이 만든 후, 라우터에 같이 걸어주면 토큰 검증 후, 해당 method가 실행하게 된다.

jwtConfig = middleware.JWTConfig{
	Claims:     &TokenClaims{},
	SigningKey: []byte("SECRET"),
}

📒 JWT 예시


왼쪽에 보이는 문자열이 jwt token이고, 오른쪽이 decode한 결과이다.

📗 JWT 실제 사용하기

( echo framework 사용 )
e.POST("라우터주소", userController."method이름", middleware.JWTWithConfig(jwtConf.JwtConfig)) 

다음과 같이 선언해주면, 해당 메소드를 부르게 될 경우, jwt token 검증을 진행 해야한다.
postman 사용해서 authorization에 token을 넣은 후, 실행시켜주면 된다.

검증하는 함수 역시 작성해야한다.
예시로 하나 들자면, token payload의 userId를 비교해서 request로 온 id와 맞을 때, 검증 성공하게 되는 함수를 짜보겠다.

func AuthorizationReqIdWithToken(c echo.Context, Id uint) bool {
	token := c.Get("user").(*jwt.Token)
	claims := token.Claims.(*TokenClaims)
	AuthId := claims.Id

	if Id != AuthId {
		return false
	}
	return true
}

위 함수는, header로 온 token의 claims를 추출한 뒤, 그 속의 userId을 확인한 후, request로 온 id와 비교하는 함수이다.
이 함수를 짠 후, 해당 token검증이 필요한 method에 아래와 같은 형식으로 넣어주면, 검증을 할 수 있다.

if !AuthorizationToken(c, *req.ID) {
		return echo.NewHTTPError(http.StatusUnauthorized, errors.New("failed"))
	}

📚 JWT 추가정보

공개키 암호 방식

JWT는 공개키 암호방식인(PKC)를 사용한다. 비대칭 암호 방식을 이용해 공개키/비밀키를 생성해, 상황에 맞게 키를 사용한다.
이때, 서명과 암호화라는 개념이 나타난다.

서명: 비밀 키를 가진 일부만 데이터에 서명가능. 공개 키를 가진 아무나 데이터의 서명을 검증할 수 있다.
암호화: 공개 키를 가진 아무나 데이터를 암호화 할 수 있고, 비밀 키를 가진 일부만 데이터를 복호화 해 확인할 수 있다.

공개키 암호화 방식은 비밀 키로 암호화한 데이터를 공개키로 복호화 할 수 있고, 반대로 공개키로 암호화 한 데이터는 비밀키로 복호화 할 수 있다.
비밀 키로 암호화 한 것을 비밀 키로 풀거나 그 반대는 불가능하다. 왜? 비대칭 암호 방식이니까~

🐾 한발짝 성장일기

token이라는 값으로, 해당 method에 접근권한을 둔다는 것을 처음으로 배웠다. 단순히 로그인 구현만 할 줄 알았던 나였는데, 한~두달만에 jwt Token을 이용해서, 해당 아이디를 가진 사용자가 아니면, 접근거부를 할 수 있도록 코드를 짜는 사람이 되었다. 성장한 나 자신... 대견해..

profile
🐻‍❄️

0개의 댓글