on
python Flask 이용해 구글 로그인 구현하기
python Flask 이용해 구글 로그인 구현하기
728x90
중간프로젝트에서 네이버, 카카오, 구글등의 OAuth 로그인을 구현하고 싶었지만 시간에 치여 불가능 했었는데요, 이번에 조별 과제를 통해 기회가 생겨 파이썬에서 구현 해 보았습니다.
developers.google.com/identity/sign-in/web/sign-in
위에서 google developer 페이지를 참고하며 기본적인 먼저 셋팅을 했습니다.
1. Credentials page에 접속합니다.
2. Credentials -> CREATE CREDENTIALS 를 클릭합니다.
3. O Auth client ID를 클릭합니다.
4. Application type은 Web application 을 선택합니다.
5. Name은 적당히 원하는 것을 입력합니다.
6. Authorised redirect URI를 등록 합니다. 제가 아래에서 공유할 python 코드를 사용하실거면, https://127.0.0.1:5000/login/callback 을 반드시 입력해야 합니다. 본인의 어플리케이션을 등록하시면, 로그인 후에 Redirect 될 페이지를 등록하면 됩니다.
7. 어플리케이션이 등록이 완료되었습니다.
8. 해당 어플리케이션을 클릭하고 들어가면 ClientID 와 Client secret을 확인 할 수 있습니다. 우측에 있는 값입니다.
이제 python에서 코드를 작성합니다. 코드는 아래 깃허브 링크에서 참고해서 작성했습니다.
github.com/realpython/materials/tree/master/flask-google-login?__s=nk12ip8oq81gce45lqjk
제일 먼저 pip나 conda install로 아래 목록 중 없는 라이브러리는 설치합니다.
requests==2.21.0
Flask==1.0.2
oauthlib==3.0.1
pyOpenSSL==19.0.0
Flask-Login==0.4.1
db.py 파일입니다.
# http://flask.pocoo.org/docs/1.0/tutorial/database/ import sqlite3 import click from flask import current_app, g from flask.cli import with_appcontext def get_db(): if "db" not in g: g.db = sqlite3.connect( "sqlite_db", detect_types=sqlite3.PARSE_DECLTYPES ) g.db.row_factory = sqlite3.Row return g.db def close_db(e=None): db = g.pop("db", None) if db is not None: db.close() def init_db(): db = get_db() with current_app.open_resource("schema.sql") as f: db.executescript(f.read().decode("utf8")) @click.command("init-db") @with_appcontext def init_db_command(): """Clear the existing data and create new tables.""" init_db() click.echo("Initialized the database.") def init_app(app): app.teardown_appcontext(close_db) app.cli.add_command(init_db_command)
user.py 파일입니다.
from flask_login import UserMixin from googleLogin.db import get_db class User(UserMixin): def __init__(self, id_, name, email, profile_pic): self.id = id_ self.name = name self.email = email self.profile_pic = profile_pic @staticmethod def get(user_id): db = get_db() user = db.execute( "SELECT * FROM user WHERE id = ?", (user_id,) ).fetchone() if not user: return None user = User( id_=user[0], name=user[1], email=user[2], profile_pic=user[3] ) return user @staticmethod def create(id_, name, email, profile_pic): db = get_db() db.execute( "INSERT INTO user (id, name, email, profile_pic)" " VALUES (?, ?, ?, ?)", (id_, name, email, profile_pic), ) db.commit()
schema.sql 파일입니다.
CREATE TABLE user ( id TEXT PRIMARY KEY, name TEXT NOT NULL, email TEXT UNIQUE NOT NULL, profile_pic TEXT NOT NULL );
마지막으로 app.py 파일입니다.
반드시 본인이 위에서 설정한 어플리케이션의 GOOGLE_CLIENT_ID 와 GOOGLE_CLIENT_SECRET 을 써주셔야 합니다.
# Python standard libraries import json import os import sqlite3 from flask import Flask, redirect, request, url_for from flask_login import ( LoginManager, current_user, login_required, login_user, logout_user, ) from oauthlib.oauth2 import WebApplicationClient import requests from googleLogin.db import init_db_command from googleLogin.user import User # Third party libraries # Internal imports # Configuration GOOGLE_CLIENT_ID = os.environ.get("GOOGLE_CLIENT_ID", '여기에 CLIENT_ID를 입력') GOOGLE_CLIENT_SECRET = os.environ.get("GOOGLE_CLIENT_SECRET", '여기에 CLIENT_SECRET 입력') GOOGLE_DISCOVERY_URL = ( "https://accounts.google.com/.well-known/openid-configuration" ) # Flask app setup app = Flask(__name__) app.secret_key = os.environ.get("SECRET_KEY") or os.urandom(24) # User session management setup # https://flask-login.readthedocs.io/en/latest login_manager = LoginManager() login_manager.init_app(app) @login_manager.unauthorized_handler def unauthorized(): return "You must be logged in to access this content.", 403 # Naive database setup try: init_db_command() except sqlite3.OperationalError: # Assume it's already been created pass # OAuth2 client setup client = WebApplicationClient(GOOGLE_CLIENT_ID) # Flask-Login helper to retrieve a user from our db @login_manager.user_loader def load_user(user_id): return User.get(user_id) @app.route("/") def index(): if current_user.is_authenticated: return ( "{}님 어서오세요!! 로그인 되었습니다.! 당신의 이메일 : {}" "당신의 구글 프로필 사진 : " '' '로그아웃하기'.format( current_user.name, current_user.email, current_user.profile_pic ) ) else: return '클릭해서 구글 로그인하기' @app.route("/login") def login(): # Find out what URL to hit for Google login google_provider_cfg = get_google_provider_cfg() authorization_endpoint = google_provider_cfg["authorization_endpoint"] # Use library to construct the request for login and provide # scopes that let you retrieve user's profile from Google request_uri = client.prepare_request_uri( authorization_endpoint, redirect_uri=request.base_url + "/callback", scope=["openid", "email", "profile"], ) return redirect(request_uri) @app.route("/login/callback") def callback(): # Get authorization code Google sent back to you code = request.args.get("code") # Find out what URL to hit to get tokens that allow you to ask for # things on behalf of a user google_provider_cfg = get_google_provider_cfg() token_endpoint = google_provider_cfg["token_endpoint"] # Prepare and send request to get tokens! Yay tokens! token_url, headers, body = client.prepare_token_request( token_endpoint, authorization_response=request.url, redirect_url=request.base_url, code=code, ) token_response = requests.post( token_url, headers=headers, data=body, auth=(GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET), ) # Parse the tokens! client.parse_request_body_response(json.dumps(token_response.json())) # Now that we have tokens (yay) let's find and hit URL # from Google that gives you user's profile information, # including their Google Profile Image and Email userinfo_endpoint = google_provider_cfg["userinfo_endpoint"] uri, headers, body = client.add_token(userinfo_endpoint) userinfo_response = requests.get(uri, headers=headers, data=body) # We want to make sure their email is verified. # The user authenticated with Google, authorized our # app, and now we've verified their email through Google! if userinfo_response.json().get("email_verified"): unique_id = userinfo_response.json()["sub"] users_email = userinfo_response.json()["email"] picture = userinfo_response.json()["picture"] users_name = userinfo_response.json()["given_name"] else: return "User email not available or not verified by Google.", 400 # Create a user in our db with the information provided # by Google user = User( id_=unique_id, name=users_name, email=users_email, profile_pic=picture ) # Doesn't exist? Add to database if not User.get(unique_id): User.create(unique_id, users_name, users_email, picture) # Begin user session by logging the user in login_user(user) # Send user back to homepage return redirect(url_for("index")) @app.route("/logout") @login_required def logout(): logout_user() return redirect(url_for("index")) def get_google_provider_cfg(): return requests.get(GOOGLE_DISCOVERY_URL).json() if __name__ == "__main__": app.run(ssl_context="adhoc")
이제 실행해봅니다.
처음 실행시에는 "Initialized the database." 라는 메시지만 나옵니다. 한번 더 실행하면 정상적으로 서버가 실행됩니다.
브라우저에 https://127.0.0.1:5000/ 를 입력해 접속합니다.
구글 로그인 하기를 클릭한 뒤에
본인의 google 아이디로 로그인 합니다.
비밀번호까지 입력 한 뒤에 다음 버튼을 누르면
정상적으로 로그인 하며 로그인 정보를 불러오는 것을 확인 할 수 있습니다! 위의 코드를 참고해 본인의 프로젝트에 직접 로그인 기능을 구현하실 수 있습니다.
728x90
from http://shanepark.tistory.com/63 by ccl(A) rewrite - 2021-03-24 21:00:34