대표적인 사용자 인증 방법에는 서버 기반 인증 / 토큰 기반 인증 존재.
스프링 시큐리티는 기본적으로 제공하는 세션 기반인증을 사용하여 사용자마다 사용자의 정보를 담은 세션을 생성/저장하여 인증 한다.
토큰 기반 인증은 토큰을 사용하여 인증하는 방법이다.
토큰은 서버에서 클라이언트를 구분하기 위한 유일한 값이다.
서버가 토큰을 생성하여 클라이언트에게 제공하고, 클라이언트가 서버에게 요청과 함께 이 토큰을 전송하여 유효한 사용자인지 확인할 수 있게 된다.
JSON Web Token의 약자로 전자 서명 된 URL-safe (URL로 이용할 수있는 문자 만 구성된)의 JSON 형식의 토큰의 표준 규격이다. 발급 받은 JWT를 이용해 인증을 하기 위해서는 HTTP 요청 헤더 중 Authorization 키 값에 Bearer + JWT 토큰값을 넣어 보내야한다.
JWT 구조
JWT는 .을 기준으로 헤더(header), 내용(payload), 서명(signature)으로 이루어져 있다.
출처 : http://www.opennaru.com/opennaru-blog/jwt-json-web-token/
헤더 (Header)
토큰의 타입(type)과 해싱 알고리즘(alg)을 지정하는 정보를 담는다.
{
"type": "JWT",
"alg": "HS256"
}
위 같은 경우에는 JWT 토큰, HS256 해싱 알고리즘을 사용한다는 내용이다.
내용 (Payload)
토큰과 관련된 정보를 담는다. 내용의 단위를 클레임(claim)이라 칭하며, 클레임은 키값의 한 쌍으로 구성되어 있다.
클레임은 등록된 클레임(registered claim), 공개 클레임(public claim), 비공개 클레임(private claim)으로 나눌 수 있다.
등록된 클레임
다음과 같이 토큰에 대한 정보를 담는데 사용된다.
- iss
토큰 발급자 (issuer)
- sub
토큰 제목(subject)
- aud
토큰 대상자(audience)
- exp
토큰의 만료 시간(expiration). 시간은 NumericDate 형식으로 하며(예:1480849147370), 항상 현재 시간 이후로 설정한다.
- nbf
토큰의 활성 날짜와 비슷한 개념으로 nbf는 Not Before를 의미한다. NumericDate 형식으로 날짜를 지정하며, 이 날짜가 지나기 전까지는 토큰이 처리되지 않는다.
- iat
토큰이 발급된 시간으로 iat은 issued at을 의미한다.
- jti
JWT의 고유 식별자로서 주로 일회용 토큰에 사용한다.
공개 클레임 (public claim)
공개되어도 상관 없는 클레임을 의미한다. 충돌을 방지할 수 있는 이름을 가져야하며, 보통 클레임 이름을 URI로 짓는다.
비공개 클레임 (private claim)
공개되면 안되는 클레임을 의미하고, 클라이언트와 서버 간의 통신에 사용된다.
- JWT 예시
{ "iss": "qlql7748@gmail.com", // 등록된 클레임 "iat": 1622370878, // 등록된 클레임 "exp": 1622372678, // 등록된 클레임 "https://inkyung.com/jwt_claims/is_admin": true, // 공개 클레임 "email": "qlql7748@gmail.com", // 비공개 클레임 "hello": "Hello" // 비공개 클레임 }
서명 (signature)
해당 토큰이 조작되었거나 변경되지 않았음을 확인하는 용도로 사용되며, 암호화 되어있다.
헤더의 인코딩값과 내용의 인코딩값을 합친 후에 주어진 비밀키를 사용해 해시값을 생성해 서명으로 사용한다.
- JWE, JWS, JWT 추가 설명
- JWT
JSON Web Token으로, claims 집합을 JWS나 JWS+JWE 구조로 인코딩된 JSON 객체로 표현한다. 기술적으로 "JWT"는 서명되지 않은 토큰을 의미하지만, 일반적으로 JWT는 JWS나 JWS+JWE를 의미한다.
여기서 Cliam은 서명을 하거나 암호화하여 사용하는데, 디지털 서명을 하는 방식은 JWS(JSON Web Signature)방식이고 암호화 하는 방식은 JWE(JSON Web Encryption)다. 이러한 claim의 집합은 JSON 객체로 표현되어 전달되는데, 이를 JWT Claims Set이라 한다.
- JWS — JSON Web Signature
서버는 JWT를 JWS 체계로 서명해서 시그너처signature와 함께 클라이언트로 전송한다. 시그너처는 JWT에 포함된 권한claim이 위조되었거나 변경되지 않았다는 것을 보장한다. 즉, JWS를 통해 위변조를 확인할 수 있을 뿐 JWT는 근본적으로 암호화 되지않은 문자열plaintext이다. 따라서 JWT에 민감한 정보를 저장해서는 안 된다.
- JWE — JSON Web Encryption
반면에 JWE 체계에서는 내용이 서명없이 암호화된다. 보안은 당연히 암호화 방식이 좋지만, 클라이언트가 claim의 데이터를 사용하려면 디지털 서명 방식을 사용해야한다. 따라서 대부분 JWT라고 하면 JWS를 가리킨다.
JWE는 JWT에 암호화를 통한 기밀성은 부여하지만, JWE를 JWS로 서명해서 담는 만큼의 보안성을 제공하지는 못한다.
즉, 결국은 JWS+JWE를 사용하여 안전한 토큰을 만들 수 있다.
출처 : http://www.opennaru.com/opennaru-blog/jwt-json-web-token/
cf. 클라이언트의 로그인 정보를 서버 메모리에 저장하지 않기 때문에 토큰기반 인증 메커니즘을 제공할 수 있다.
보안성을 위해 토큰의 유효기간이 짧아야 좋지만, 사용자는 토큰을 너무 짧은 시간동안만 사용할 수 있어 불편할 수 있다. 이 부분을 보완하기 위해 나타난 개념이 리프레시 토큰이다.
리프레시 토큰은 사용자를 인증하기 위한 수단이 아니라, 액세스 토큰이 만료 되었을 시 새로운 엑세스 토큰을 발급하기 위해 사용되는 토큰이다.
따라서 액세스 토큰의 유효기간은 짧게 설정하고, 리프레시 토큰의 유효기간은 길게 설정하여 사용한다.
Refresh Token Process
클라이언트가 서버에게 인증 요청을 한다.
서버는 클라이언트에서 전달한 정보를 바탕으로 인증 정보가 유효한지 확인한 후, 엑세스 토큰과 리프레시 토큰을 만들어 클라이언트에게 전달한다. 클라이언트는 전달 받은 토큰을 저장한다.
서버에서 생성한 리프레시 토큰은 DB에도 저장한다.
인증을 필요로 하는 API를 호출할 때, 클라이언트는 저장된 액세스 토큰과 함께 API를 요청한다.
서버는 전달 받은 액세스 토큰을 검증한 후, 유효하다면 클라이언트에게 요청한 내용을 처리한다.
액세스 토큰이 만료된 뒤, 클라이언트가 만료된 엑세스 토큰과 함께 API를 요청한다.
서버는 전달 받은 엑세스 토큰을 검증한 후, 만료되었다면 토큰이 만료되었다는 에러를 클라이언트에게 응답한다.
클라이언트는 저장해둔 리프레시 토큰과 함께 새로운 엑세스 토큰을 발급하는 요청을 서버에게 보낸다.
서버는 전달 받은 리프레시 토큰을 DB에 저장되어 있는 리프레시 토큰과 같은지 확인한다.
만약 유효한 리프레시 토큰이라면, 새로운 엑세스 토큰을 재발급하여 클라이언트에게 응답해준다.
그 이후 클라이언트는 다시 API 요청을 하게된다.
추가 참고
http://www.opennaru.com/opennaru-blog/jwt-json-web-token/
https://medium.com/@OutOfBedlam/jwt-%EC%9E%90%EB%B0%94-%EA%B0%80%EC%9D%B4%EB%93%9C-53ccd7b2ba10
https://velog.io/@dae-hwa/JWTJSON-Web-Token-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0