사용자 인증방식에는
서버 기반 인증방식과
토큰 기반 인증방식이 있는데
나는 토큰 기반 인증방식을 구현해 보았다.
(서버 기반 인증방식 또한 구현할 예정)
로그인을 하면 서버에서 토큰을 생성하고 클라이언트가 토큰을 받고 로그인을 한 사람만 이용할 수 있는 api를 사용하는 과정을 코드로 구현한 걸 간단히 코드로 메모하려한다.
설치한 라이브러리
Flask==2.0.2
pymongo==3.12.1
pyjwt==2.3.0
목차
#로그인시 토큰생성api
@app.route('/api/login', methods=['POST'])
def token_maker():
#post방식으로 request시에만 등록
if request.method == 'POST':
#로그인 페이지에서 유저가 쓴 email, password받음
email = request.form['email']
pw = request.form['password']
#유저 비밀번호 암호화
pw_encrypt = hashlib.sha256(pw.encode('utf-8')).hexdigest()
#로그인 페이지에서 유저가 쓴 email, password를 데이터베이스에서 확인
findingResult = db.userInfo.find_one({'email': email, 'password': pw_encrypt}, {'_id': False})
#데이터베이스에 유저가 쓴 email과 password가 있을시 토큰생성
if findingResult:
#토큰의 payload식별자는 유저정보중 중복되지 않는 email로하고 토큰만료시간은 600초로 설정
payload = {'id': email,
#쿠키사용시에는 exp를 프론트에서 설정
'exp': datetime.datetime.utcnow() + datetime.timedelta(seconds=600)}
token = jwt.encode(payload, SECRET_KEY, algorithm='HS256')
#생성된 토큰과 메세지 json형식으로 클라이언트로 response
return jsonify({'token': token, 'msg': 'success'})
#데이터베이스에 유저가 쓴 email과 password가 없을 시 토큰생성실패
return jsonify({'msg': 'Not available'})
2-(1).
//로그인 창에 이메일 비밀번호 누르면 실행되는 함수
async function login_info() {
const email = inputId.value;
const pw = inputPw.value;
const options = {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
},
body: `email=${email}&password=${pw}`,
};
const response = await fetch("/api/login", options);
const result = await response.json();
//서버에서 로그인이 성공시
if (result["msg"] == "success") {
// jwt를 쿠키에 저장
document.cookie = `mytoken=${result["token"]}`;
alert("환영합니다!");
// 메인페이지로 이동
window.location.href = "/home";
//서버에서 로그인 실패시
} else {
alert("ID와 비밀번호를 확인해주세요");
}
}
2-(2).
const inputId = document.querySelector('#input_id')
const inputPw = document.querySelector('#input_pw')
//로그인 창에 이메일 비밀번호 누르면 실행되는 함수
async function login_info() {
const email = inputId.value;
const pw = inputPw.value;
const options = {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
},
body: `email=${email}&password=${pw}`,
};
const response = await fetch("/api/login", options)
const result = await response.json();
//서버에서 로그인이 성공시
if (result['msg'] == 'success'){
//jwt를 localStorage에 저장
localStorage.setItem('jwt', result['token'])
alert('환영합니다!');
//localStorage에 있던 jwt 가져옴
const token = localStorage.getItem('jwt')
//jwt header에 포함
const homeOptions = {
method: "GET",
headers: {
"jwt":token
}
};
// 메인 페이지를 열기 위한 ajax요청
// fetch("/home", homeOptions)
//서버에서 로그인 실패시
}else{
alert('ID와 비밀번호를 확인해주세요')
}
}
3-(1).
#로그인을 해야 열리는 메인페이지
@app.route('/home')
def home():
#http request의 header의 cookie를 받음
token_receive = request.cookies.get('mytoken')
#쿠키가 있을시 메인페이즈를 열어주고 payload에서 받은 유저의 이메일을 통해 유저식별
try:
payload = jwt.decode(token_receive, SECRET_KEY, algorithms=['HS256'])
user_info = db.userInfo.find_one({"email": payload['id']})
return render_template('index.html', user_info=user_info)
#쿠키가 없을시 로그인페이지로
except jwt.ExpiredSignatureError:
return redirect(url_for("login"))
except jwt.exceptions.DecodeError:
return redirect(url_for("login"))
일단 안되는 코드:
추측1)fetch랑 render_template 부분이 잘 안맞아서
추측2)애초에 header를 바꾼 get요청은 불가능
3-(2).
#로그인을 해야 열리는 메인페이지
@app.route('/home')
def home():
#http request의 header의 jwt를 받음
token_receive = request.headers.get('jwt')
#헤더에 jwt가 있을시 메인페이즈를 열어주고 payload에서 받은 유저의 이메일을 통해 유저식별
try:
payload = jwt.decode(token_receive, SECRET_KEY, algorithms=['HS256'])
user_info = db.userInfo.find_one({"email": payload['id']})
return render_template('index.html', user_info=user_info)
#헤더에 jwt가 없을시 로그인페이지로
except jwt.ExpiredSignatureError:
return redirect(url_for("login"))
except jwt.exceptions.DecodeError:
return redirect(url_for("login"))