개념정리
회원가입, 로그인기능 (해시함수 / JWT)
1. 해시함수(암호화 알고리즘)
- 알고리즘의 한 종류로서 임의의 데이터를 입력 받아 항상 고정된 길이의 임의의 값으로 변환해주는 함수
- 해시함수 SHA256은 어떤 길이의 입력값을 넣어도 항상 256바이트의 결과값이 나옴
- 동일한 입력값은 항상 같은 결과값이 나오고, 입력값은 조금이라도 달라지면 완전히 다른 값이 나오게되며, 결과값을 통해 입력값을 알아내는 것이 불가능
[ 회원가입 API ]
- post로 id, pw, nickname을 받아서, mongoDB에 저장
- 저장하기 전에, pw를 sha256 방법(=단방향 암호화. 풀어볼 수 없음)으로 암호화해서 저장
@app.route('/api/register', methods=['POST'])
def api_register():
id_receive = request.form['id_give']
pw_receive = request.form['pw_give']
nickname_receive = request.form['nickname_give']
#저장하기 전 pw를 sha256 방법으로 암호화해서 저장
pw_hash = hashlib.sha256(pw_receive.encode('utf-8')).hexdigest()
db.user.insert_one({'id': id_receive, 'pw': pw_hash, 'nick': nickname_receive})
return jsonify({'result': 'success'})
2. JWT(JSON Web Tokene)
JSON 객체를 사용해 정보(토큰)를 안정성 있게 전달하는 웹표준. 사용자가 로그인하면 서버에서 회원임을 인증하는 토큰을 넘겨줌으로써 이후 회원만 접근할 수 있는 서비스 영역에서 신분을 확인하는 데 쓰임
[JWT 인증절차]
[ 로그인 API ]
- id, pw를 받아서 맞춰보고, 토큰을 만들어 발급
@app.route('/')
def home():
#아래 api_login()에서 만든 토큰을 발급받아 SECRET_KEY를 통해 decode(복호화)
#토큰을 쿠키에 담아 발급
token_receive = request.cookies.get('mytoken')
try:
payload = jwt.decode(token_receive, SECRET_KEY, algorithms=['HS256'])
user_info = db.user.find_one({"id": payload['id']})
return render_template('index.html', nickname=user_info["nick"])
except jwt.ExpiredSignatureError:
return redirect(url_for("login", msg="로그인 시간이 만료되었습니다."))
except jwt.exceptions.DecodeError:
return redirect(url_for("login", msg="로그인 정보가 존재하지 않습니다."))
@app.route('/api/login', methods=['POST'])
def api_login():
id_receive = request.form['id_give']
pw_receive = request.form['pw_give']
# 회원가입 때와 같은 방법으로 pw를 암호화합니다.
pw_hash = hashlib.sha256(pw_receive.encode('utf-8')).hexdigest()
# id, 암호화된pw을 가지고 해당 유저를 찾습니다.
result = db.user.find_one({'id': id_receive, 'pw': pw_hash})
# 찾으면 JWT 토큰을 만들어 발급합니다.
if result is not None:
# JWT 토큰에는, payload와 시크릿키가 필요합니다.
# 시크릿키가 있어야 토큰을 디코딩(=풀기) 해서 payload 값을 볼 수 있습니다.
# 아래에선 id와 exp를 담았습니다. 즉, JWT 토큰을 풀면 유저ID 값을 알 수 있습니다.
# exp에는 만료시간을 넣어줍니다. 만료시간이 지나면, 시크릿키로 토큰을 풀 때 만료되었다고 에러가 납니다.
payload = {
'id': id_receive,
'exp': datetime.datetime.utcnow() + datetime.timedelta(seconds=5)
}
#SECRET_KEY는 상단에 선언
token = jwt.encode(payload, SECRET_KEY, algorithm='HS256')
# token을 줍니다.
return jsonify({'result': 'success', 'token': token})
# 찾지 못하면
else:
return jsonify({'result': 'fail', 'msg': '아이디/비밀번호가 일치하지 않습니다.'})
[ajax로 쿠키 받기]
쿠키의 개념
- 로그인을 구현하면, 반드시 쿠키라는 개념을 사용
- 페이지에 관계없이 브라우저에 임시로 저장되는 정보. 키:밸류 형태(딕셔너리 형태)로 저장
- 쿠키가 있기 때문에, 한번 로그인하면 네이버에서 다시 로그인할 필요가 없음
- 브라우저를 닫으면 자동 삭제되게 하거나, 일정 시간이 지나면 삭제되게 할 수 있음
function login() {
$.ajax({
type: "POST",
url: "/api/login",
data: {id_give: $('#userid').val(), pw_give: $('#userpw').val()},
success: function (response) {
if (response['result'] == 'success') {
// 로그인이 정상적으로 되면, 서버에서 토큰을 받아옵니다.
// 이 토큰을 mytoken이라는 키 값으로 쿠키에 저장합니다.
$.cookie('mytoken', response['token']);
alert('로그인 완료!')
window.location.href = '/'
} else {
// 로그인이 안되면 에러메시지를 띄웁니다.
alert(response['msg'])
}
}
})
}
[추가 개념]
장점
1. 간편하다. JWT는 발급한 후 검증만 하면 되기 때문에 추가 저장소가 필요 없다. 이 때문에 별도의 저장소를 사용하지 않는 서버에서 유리하다. 이는 서버를 확장하거나 유지,보수하는데 유리하다.
2. 확장성이 뛰나다. 토큰 기반으로 하는 다른 인증 시스템에 접근이 가능합니다. Facebook 로그인, Google 로그인 등 토큰기반으로 하는 서비스에서 선택적으로 이름이나 이메일 등을 받을 수 있는 권한도 받을 수 있다.
단점
1. 이미 발급된 JWT에 대해서는 돌이킬 수 없다. JWT는 한 번 발급되면 유효기간이 완료될 때 까지는 계속 사용이 가능하기 때문에 악의적인 사용자는 유효기간이 지나기 전에 정보를 이용할 수 있다.
-> 해결책
기존의 Access Token의 유효기간을 짧게 하고 Refresh Token이라는 새로운 토큰을 발급하면 Access Token을 탈취당해도 상대적으로 피해를 줄일 수 있다
2. Payload 정보가 제한적이다. Payload는 따로 암호화되지 않기 때문에 디코딩하면 누구나 정보를 확인할 수 있다. 따라서 유저의 중요한 정보들은 Payload에 넣을 수 없다.
3. JWT의 길이는 세션/쿠키 방식에 비해 길다. 인증이 필요한 요청이 많아질 수록 서버의 자원낭비가 발생한다.
Header
Header를 복호화(Decoding)하면, type과 alg정보가 들어있다.
- type : JWT (고정)
- alg(방식) : verify signature 정보를 암호화 할 알고리즘 정보 (HS256 등)
Payload
Payload를 복호화(Decoding)하면, 서버에 보낼 정보들이 담겨있다.
- 누가 누구에게 발급했는지
- 토큰의 유효기간
- 어떤 정보를 보내줄 것인지(닉네임 등)
Verify Signature
서버에 비밀 키 값(Secret Key)을 지정해두고, Header, Payload와 함께 알고리즘을 통해 암호화하면 Verify Signature(서명) 값이 나온다.
'개발 하나둘셋 > CS' 카테고리의 다른 글
[네트워크] RESTful 하게 API를 디자인 한다는 것은? (0) | 2021.11.29 |
---|---|
[네트워크] HTTP와 HTTPS의 특징과 차이점 (0) | 2021.11.19 |
[네트워크] REST API와 HTTP 메소드정리 (0) | 2021.11.17 |
객체지향 프로그래밍 (0) | 2021.11.14 |
[네트워크] jinja2, API (0) | 2021.11.07 |