토큰 기반 인증

김금동·2021년 11월 7일
0

목록 보기
1/4

사용자 인증방식에는
서버 기반 인증방식과
토큰 기반 인증방식이 있는데
나는 토큰 기반 인증방식을 구현해 보았다.
(서버 기반 인증방식 또한 구현할 예정)

로그인을 하면 서버에서 토큰을 생성하고 클라이언트가 토큰을 받고 로그인을 한 사람만 이용할 수 있는 api를 사용하는 과정을 코드로 구현한 걸 간단히 코드로 메모하려한다.

설치한 라이브러리

Flask==2.0.2
pymongo==3.12.1
pyjwt==2.3.0

목차

  1. 로그인을 하면 서버에서 토큰생성 후 클라이언트에게 토큰제공
  2. 클라이언트가 토큰(jwt)을 저장하는 방식 2가지
    (쿠키에 저장, localstorage저장 = (1), (2))
  3. 로그인을 해야지 이용할 수 있는 api구현
    (클라이언트가 쿠키에 저장할때, localstorage저장할때
    = (1), (2))
    (현재 (2)는 아직 구현안됨)
#로그인시 토큰생성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"))
profile
나원래chu해

0개의 댓글