프로젝트 중 로그인 기능 구현을 위해 JWT, OAuth2 를 사용해서 카카오 로그인하기 구현을 해보았다.
우선 오늘은 jwt, oauth2, 스프링 시큐리티란 무엇인지 알아보는 시간을 갖도록 하자.
JWT 🔒
Jwt란 Json Web Token 의 줄임말로 '인증에 필요한 정보들을 암호화시킨 토큰' 이며, 서버와 클라이언트 통신 시 HTTP header에 실어 인증 수단으로 사용된다.
JWT의 구조
JWT는 위와 같은 구조로 디코드 되어있는데 . 을 중심으로 Header, Payload, Signiture 로 나누어진다.
1. Header
Jwt의 헤더는 alg와 typ으로 구성되어 있다.
"alg" : 암호화할 해싱 알고리즘
"typ" : 토큰의 타입
2. Payload
Payload는 위와 같은 형식으로 주요 내용들을 담고 있다.
여기서는 하나의 정보를 한 조각(Claim) 이라고 부른다.
Payload에 들어가는 클레임은 3종류로 이루어져 있으며 아래와 같다.
- 등록된 (registered) 클레임
iss
토큰 발급자 (issuer)
sub
토큰 제목 (subject)
aud
토큰 대상자 (audience)
exp
토큰의 만료시간 (expiraton), 시간은 NumericDate 형식으로 되어있어야 하며 (예: 1480849147370) 언제나 현재 시간보다 이후로 설정되어있어야 한다. (자바에서 설정시 new Date(System.currentTimeMillis()) 사용)
nbf
Not Before 를 의미하며, 토큰의 활성 날짜와 비슷한 개념이다. 여기에도 NumericDate 형식으로 날짜를 지정하며, 이 날짜가 지나기 전까지는 토큰이 처리되지 않는다.
iat
토큰이 발급된 시간 (issued at), 이 값을 사용하여 토큰의 age 가 얼마나 되었는지 판단할 수 있다.
jti
JWT의 고유 식별자로서, 주로 중복적인 처리를 방지하기 위하여 사용된다. 일회용 토큰에 사용하면 유용하다.
- 공개 (public) 클레임
공개 클레임들은 충돌이 방지된 (collision-resistant) 이름을 가지고 있어야 한다.
충돌을 방지하기 위해서는, 클레임 이름을 URI 형식으로 지어야한다.
- 비공개 (private) 클레임
등록된 클레임도 아니고, 공개된 클레임들도 아님.
양 측간에 (클라이언트 - 서버) 협의 하에 사용되는 클레임 이름들이다.
공개 클레임과는 달리 이름이 중복되어 충돌이 될 수 있으니 사용할 때에 유의해야 한다.
{
"kakaoId": "testId123"
}
3. signature
Header와 Payload를 더한 뒤 비밀키로 해싱하여 생성한다.
Header와 Payload는 단순히 인코딩 된 값이기 때문에 제 3자가 복호화 및 조작할 수 있지만, Signature는 서버 측에서 관리하는 비밀키가 유출되지 않는 이상 복호화할 수 없다.
따라서 Signature는 토큰의 위변조 여부를 확인하는 데 사용된다.
여기서 해싱이란?
해시 함수 또는 해시 알고리즘 또는 해시함수 알고리즘은 임의의 길이의 데이터를 고정된 길이의 데이터로 매핑하는 함수이다.
해시 함수에 의해 얻어지는 값은 해시 값, 해시 코드, 해시 체크섬 또는 간단하게 해시라고 한다.
이렇게 해시 알고리즘을 사용하여 해시 코드를 얻는 과정을 해싱이라고 한다.
사용법
아래에 설명해야 할 것들이 많으니 간단히 이해할 수 있도록 설명하겠다.
설명할 방법은 사용되는 다양한 방법 중 이번 프로젝트에 사용할 방법 이니 이 점 참고하자.
1. 서버는 로그인 등을 통해 인증된 회원에게 Jwt AccessToken과 RefreshToken을 발급한다.
AccessToken은 각종 서비스를 이용할 때 회원 인증 수단으로 사용되며 "보안을 위해" 짧은 유효시간(exp) 을 가진다
RefreshToken은 AccessToken을 재발급하기 위한 목적으로 발급되며 AccessToken이 만료되면 RefreshToken과 AccessToken을 보내어 "서버로부터 AccessToken을 재발급" 받을 수 있다.
이러한 기능을 갖다 보니 RefreshToken이 AccessToken보다 훨씬 긴 유효시간을 갖는다.
2. AccessToken을 사용하여 서비스 이용
AccessToken 을 사용할 때는 HTTP Header에 Token을 삽입하여 서버로 Request를 보낸다.
서버는 JwtInterceptor 를 사용하여 Controller에 접근하기 전에 토큰을 먼저 낚아채어 검증한다.
3. RefreshToken을 사용하여 AccessToken 갱신
RefreshToken 또한 HTTP Header에 삽입하여 서버로 Request를 보내며 갱신을 위해 AccessToken 과 함께 서버로 보내야 한다.
JwtInterceptor 는 RequestHeader에 RefreshToken이 있다면 RefreshToken을 검증한 뒤에 AccessToken을 재발급한다.
4. RefreshToken 만료 시
RefreshToken이 만료되었다면 예외를 발생시킨다.
예외가 발생되면 재로그인을 통해 다시 토큰을 재발급받아 서비스를 이용할 수 있다.
OAuth 🔒
위키백과 정의에 따르면, OAuth는 인터넷 사용자들이 비밀번호를 제공하지 않고 다른 웹사이트 상의 자신들의 정보에 대해 웹사이트나 애플리케이션의 접근 권한을 부여할 수 있는 공통적인 수단으로써 사용되는, 접근 위임을 위한 개방형 표준이다.
다시 말해 사용자가 애플리케이션에게 모든 권한을 넘기지 않고 사용자 대신 서비스를 이용할 수 있게 해주는 HTTP 기반의 보안 프로토콜이다.
이러한 OAuth 를 사용하면 새로운 사이트에 신규로 가입하지 않고도, OAuth를 제공하는 기존 가입했던 서비스의 계정을 통해 로그인할 수 있다.
OAuth 에서 Auth는 Authentication(인증) & Authorization(허가) 2가지 의미를 포함하고 있다.
SNS 계정을 통해 특정 사이트에 로그인을 해본 경험이 있다면 ‘서비스 제공자를 대신하여 제 3자가 어떤 정보를 사용하도록 접근을 허용하겠는가?’ 라는 물음을 보신 적이 있으실 것이다.
여기서 제 3자는 여러분들이 이용할 서비스(쇼핑몰 등)이다.
이 물음에 동의하면, 먼저 서비스 제공자에게 자신을 인증하고 제 3자에게 자신의 정보(이메일 주소, 이름, 나이, 성별 등)를 사용하도록 접근 권한을 부여하는 것이다.
OAuth 로 인해 사용자 입장에서는 더욱더 여러 서비스들을 하나의 계정으로 관리할 수 있게 되어 편해지고 개발자 입장에서는 민감한 사용자 정보를 다루지 않아 위험 부담이 줄고 서비스 제공자로부터 사용자 정보를 활용할 수 있기 때문에 장점이 상당히 많다.
사용법
OAuth는 JWT 사용법과 유사하게 AccessToken과 RefreshToken을 통해 사용할 수 있다.
1. 인가코드(Authorization code) 흭득
OAuth를 제공하는 플랫폼에 로그인하여 인가 코드를 획득한다.
인가 코드를 특정 URI에 파라미터로 붙여 전송하면 유저 정보를 획득할 수 있는 AccessToken과 RefreshToken을 발급받을 수 있다.
2. AccessToken으로 유저 정보 얻기
OAuth를 제공하는 플랫폼의 API 서버 특정 URI에 AccessToken을 HTTP Header에 포함한 Request 요청을 날린다.
API 서버는 AccesToken을 검증하고 유저 정보를 Response 한다.
스프링 시큐리티 🔒
이 부분에 대해선 정보가 부족하기 때문에 자세히 공부한 뒤에 채워 넣도록 하겠다.
유저에게 Role(권한)을 주고 특정 권한이 있는 유저만 어떠한 API에 접근할 수 있도록 한다는 등의 설정을 할 수 있는 것으로 알고 있으면 후에 구현할 부분들을 대강 이해할 수 있을 것이다.
요정도 내용만 이해하고 있다면 뒤의 내용들을 이해할 수 있을 것이다.
다음 시간부터 본격적으로 개발에 들어가 보겠다.
reference:
https://devhaks.github.io/2019/05/31/oauth2/
https://velog.io/@dnjscksdn98/JWT-JSON-Web-Token-%EC%86%8C%EA%B0%9C-%EB%B0%8F-%EA%B5%AC%EC%A1%B0
'스터디' 카테고리의 다른 글
[객체지향 / 스프링] DI 와 IoC란 무엇인가 (1) | 2022.03.28 |
---|---|
[객체지향] SOLID 원칙이란? (4) | 2022.03.28 |
[SpringBoot] 스프링부트 Swagger 2 적용법 및 예외 처리 (0) | 2022.02.04 |
[SpringBoot] 스프링부트 Dependency 버전 선택하는 법 (0) | 2022.02.01 |
[SpringBoot] [트러블슈팅] Swagger2 - ApplicationContextException: failed to start bean 'documentationpluginsbootstrapper' (0) | 2022.01.18 |