Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
JWT와 Flask, PyJWT로 인증 API 서버 만들기
Search
Junho Kim
August 25, 2018
Programming
0
2.3k
JWT와 Flask, PyJWT로 인증 API 서버 만들기
PyCon 2018 KR Tutorial slide
Junho Kim
August 25, 2018
Tweet
Share
Other Decks in Programming
See All in Programming
Vibe Codingの幻想を超えて-生成AIを現場で使えるようにするまでの泥臭い話.ai
fumiyakume
21
9.8k
QA x AIエコシステム段階構築作戦
osu
0
220
副作用と戦う PHP リファクタリング ─ ドメインイベントでビジネスロジックを解きほぐす
kajitack
3
510
DMMを支える決済基盤の技術的負債にどう立ち向かうか / Addressing Technical Debt in Payment Infrastructure
yoshiyoshifujii
5
680
AIのメモリー
watany
12
1.1k
はじめてのWeb API体験 ー 飲食店検索アプリを作ろうー
akinko_0915
0
180
What's new in Adaptive Android development
fornewid
0
130
新世界の理解
koriym
0
100
React は次の10年を生き残れるか:3つのトレンドから考える
oukayuka
41
16k
Vibe coding コードレビュー
kinopeee
0
370
Bedrock AgentCore ObservabilityによるAIエージェントの運用
licux
8
490
Reactの歴史を振り返る
tutinoko
1
150
Featured
See All Featured
Faster Mobile Websites
deanohume
308
31k
The World Runs on Bad Software
bkeepers
PRO
70
11k
[RailsConf 2023 Opening Keynote] The Magic of Rails
eileencodes
29
9.6k
A Modern Web Designer's Workflow
chriscoyier
695
190k
Build The Right Thing And Hit Your Dates
maggiecrowley
37
2.8k
How to Create Impact in a Changing Tech Landscape [PerfNow 2023]
tammyeverts
53
2.9k
Embracing the Ebb and Flow
colly
86
4.8k
Raft: Consensus for Rubyists
vanstee
140
7k
Put a Button on it: Removing Barriers to Going Fast.
kastner
60
3.9k
Improving Core Web Vitals using Speculation Rules API
sergeychernyshev
18
1k
Rails Girls Zürich Keynote
gr2m
95
14k
jQuery: Nuts, Bolts and Bling
dougneiner
63
7.8k
Transcript
+85৬'MBTL 1Z+85۽ੋૐ"1*ࢲߡٜ݅ӝ 1:$0/,PSFBౚషܻ ӣળഐ
$POUFOUT ܻоѦ৵ೞחо ੋૐੋооߺѱಝࠁӝ ੋૐߑधрױೞѱಝࠁӝ +85ۆ
1ZUIPOীࢲ+85ܳࢎਊೞחੋૐ"1*ࢲߡٜ݅ӝ "EWBODFE5PQJDT Ҋਵәઁٜ
য়טౚషܻীࢲܖחѪܖঋחѪ য়טౚషܻীࢲחղਊਸಝࠅѪੑפ рױೠੋૐੋоJOUSPEVDUJPO 'MBTL৬1Z+85ܳࢎਊೠੋૐ"1*ࢲߡ য়טౚషܻীࢲחղਊನೣೞҊঋणפ
ੋૐੋоܳ৵೧ঠೞաਃ 3#"$ 1ZUIPOEFDPSBUPSܳҳഅೞחߑߨ 'MBTLXFCBQQEFTJHOQBUUFSO
ܻоѦ৵ೞחо .PUJWBUJPO
కୡীחইפҊਢࢲ࠺झח ࢲ࠺झ೧ঠೞחݽٚӝמਸױੌࢲߡীয֍Ҋ .POPSJUIJD ࢲ࠺झܳઁ ҕೞחഋకणפ 8FC 8"4
%# +41 5PNDBU 4FSWMFU .Z4RM ױੌࢲߡীࣁ࣌ࠁী֍ਵݶҊೡѪহભ 6TFS 8FC 8"4 %# GET / index.html (or jsp) ਃ (ਃೞݶ) DB query Querying Data DB ޚ index ಕ (ࢂ) index ಕ
दрաݶࢲ 'SPOUFOEӝࣿبߊೞҊৈ۞оࢲ࠺झҳࢿইఃఫبਊغणפ tۿоঌইࢲ۪؊݂ೞҊߔূ٘ীਃೡԋߔূ٘חؘఠ݅۴u tߔূ٘חۿоਗೞחࢲ࠺झܳೞח"1*݅ઁҕೞݶغѷu ױఋ۽֗য়פ ࢚కਬܳೞঋח3&45ࢲ࠺झૐо
पदр۪؊݂দ 6TFS 8FC "1*4FSWFS %# GET / Index ಕ ௪݂௪݂௪݂.. Querying Data Index.html (৮ࢿغח ࢂ) index ಕ Resource list Resource list
ӏݽоழݶࢲ ഒѢೞӝחޖܻפӬդ"ࢲ࠺झ݅ઁҕೡѱցח#ࢲ࠺झ݅ઁҕ ೞ ࢲ࠺झױਤ۽ࢲߡࣁ࠙ച झாੌইਓ "1*4FSWFS "1*4FSWFS "1*4FSWFS
%# पदр۪؊݂দ 6TFS 8FC GET / (৮ࢿغח ࢂ) index ಕ ࣻ ݆ request
3FRVFTUоפѪջग 4UBUFMFTTೠ3&45ীࢲTUBUFܳਬ೧ঠೞח࢚ടߊࢤ ޛ֤ࠗ࠙3&45"1*חೞաSFRVFTU۽ܻܳղঠೞחѪજणפ ղࠗDPOUSPMMFSীࢲٮ۽۽ਸجܻחೠ؊ۄب Ӓؘ۠QSPUFDUFESFTPVSDFח
ࠁഐ೧ঠೞחਗFH ౠࢎਊоࢎਊೠ SFTPVSDFࠁоઉয়ӝ١ ߐڰ ۞۰ݶݒߣੋૐࢲߡীMPHJOೞҊਃܻೞҊ೧ঠೣ 6TFS 8FC "1*4FSWFS "1*4FSWFS GET / (۽Ӓੋ റ) resource A ࣁਃ resource A resource B ࣁਃ ־ҳջ ք Context ҕਬ X
ੋૐੋооߺѱಝࠁӝ
ੋૐҗੋоҳࢿ ࢎਊ ࢎਊੋૐࠁ *%18 ࢲ࠺झઁҕࢲߡ
पઁܻоઁҕೞҊरࢲ࠺झ ੋૐ߂ࢎਊೲо ੋо ػࢎਊ݅ࢎਊоמ ੋૐੋоࢲߡ ࢎਊੋૐਸ ੋૐػࢎਊࢲ࠺झࢎਊӂೠҙܻ
ੋૐ ੋૐ "VUIFOUJDBUJPO ࢚ࢲ࠺झ PSࢲߡ ীղо־ҳੋܳഛੋ߉חੌ
ࠁా ੋૐਸೞӝਤೠࠁܳCPEZ۽ࠗೞৈਃ *%18 UPLFO ੋૐػTFTTJPO١ ੋૐࢲߡ 6TFS tղо#PCਃu ੋૐ tইפঠu ੋૐѾҗࢲ࠺झী١۾ػࢎਊ١۾غঋࢎਊ ੋૐ%# tੴ#PC u t֨u
ੋо ੋо "VUIPSJ[BUJPO ղоস SFTPVSDF ਸࣻ೯ೡӂೠ "VUIPSJUZ
оҊഛੋ߉חੌ ੋоܳਃೞחࢎਊחੋૐغযযঠೣ ࠁాౠ೯ਤ BDUJPO ীೞৈӂೠ USVF হ GBMTF ܳ߈ജ ੋоࢲߡ ࠁాੋૐࢲߡ৬э ࢲ࠺झઁҕࢲߡ 6TFS tա#PCੋؘ 7.ೞա݅ۄu ࢲ࠺झਃ t#PC7.ٜ݅ӂೠযਃ u ੋоӂೠ ঠॳ USVF ৈӝ7. ࢲ࠺झѾҗ
ੋૐߑधрױೞѱಝࠁӝ
4FTTJPOӝ߈ੋૐੋоߑध ాੋߑध $MJFOUDPPLJFীੋૐੋоࠁܳ֍ঋ FH DPPLJF<rJT@MPHJOs> TFTTJPOLFZчਸਬೞৈ
ࢲߡীࢲਃೠTFTTJPOEJDUী۽Ӓੋਬޖ 5SVF'BMTF ܳঌইࠁחߑध ݒрױ &OUFSQSJTFࢲߡীࢲחࣁ࣌ਸҕਬೞחӝࣿਃ 4FTTJPODMVTUFSJOH PWFSIFBE $PPLJFܳੜTBMU೧ࢲঐഐച WVMOFSBCJMJUZ from flask import Flask, request, session app = Flask(__name__) @app.route('/is_login,', methods=['POST']) def is_login(): return session['logged_in'] # True / False
ੌ߈ੋ 5PLFOӝ߈ੋૐੋоߑध 4FTTJPOীҳগ߉ঋҊ SFRVFTUীੋૐੋоҙ۲ࠁܳೣԋࠁղܻ ੌ߈ਵ۽IUUQSFRVFTUIFBEFSীUPLFOࠁܳನೣೞৈ࣠ ۽ӒੋBDDFTT@UPLFOߊә
0"VUICFBSFS 0"VUI0QFO*% +85١ ੋૐࢲߡ 6TFS *%#PC 18#PC#PC#PC షߊәਃ షߊә BDDFTT@UPLFOOBXLEKOBLKTDOLj ࢲ࠺झઁҕࢲߡ ࢲ࠺झਃ IFBEFSBDDFTT@UPLFOOBXLj 63*(&5TFSWJDF
৵0"VUIউॄਃ ࢶѐ֛ӝ 0"VUIח3'$ীػੋૐੋоQSPUPDPMTUBOEBSE 3'$ য়טܻоࢎਊೞҊೞח+85חੋૐীࢎਊೞחUPLFOGPSNBU
ৈ۞࠙ࢤпೞח0"VUI ,ࢎ۽Ӓੋ0"VUI (ࢎ۽Ӓੋ0"VUI 'ࢎ 0"VUI חݽف0"VUIQSPUPDPMਸ݅दఃח*NQMFNFOUBUJPO ܻоঌҊח0"VUI0QFO*%ੑפ 0QFO*%ߑध৻ࠗܳ֗য়ӝٸޙীղࠗݎઁೠ١ۄࡁࢲ࠺झী חࠗ
ѱо SFRVFTUоUPLFOਸٜҊ৳Ҋ೧ࢲޖࢲ࠺झܳઁҕೡࣻহणפ UPLFOSBOEPN@HFOFSBUFE@TUSJOH UPLFO೧ӏਵ۽ߊәೠѪੋഛੋ %#.4 PUIFS"1* ਃ
UPLFOࢲ࠺झप೯ӂೠоҊחبഛੋਃ
+85ۆ
+40/8FC5PLFO ӝઓহחBDDFTTUPLFOীܳࠗৈ೧ࠁ 5PLFOഛੋױ҅ ੋо ܳੋૐࢲߡীޚঋҊ"1*ࢲߡࣻળীࢲ೧Ѿೞ ޚبٮب݈Ҋযڌѱࢤѹݡחо
ѐQBSU IFBEFS QBZMPBE WFSJGZTJHOBUVSF EPU ਵ۽ҳ࠙ - header.payload.signature eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9. (ࢎਊೠ algorithm, token type: JWT) eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ. (ੋૐ ࠁ SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c (Ѩૐਸ ਤೠ Signature)
+85JOEFUBJM - Header ೧UPLFOࢎਊೞחBMHPSJUIN 5PLFO5ZQF - Payload
UPLFOӂೠࠁ ੋૐࠁ ࢎਊࠁ TDPQF ݅ܐӝр - Signature ਤ߸ઑѨਸਤೠTJHOBUVSF
1BZMPBEJOEFUBJM Payloadীನೣغחղਊٜ JO3'$ SFTFSWFEGJFME iss (issuer)UPLFOߊ೯ sub
(subject)ࢎਊ৬ࢎਊSFTPVSDF63*ઁݾ aud (audience)ߊә࢚ exp (expiration date)ష݅ܐغחदр nbf (not before)షഝࢿചदр чറࠗఠషഝࢿച iat (issued at)షߊәदр jti (JWT ID)5PLFOҳ߹ਸਤೠVOJRVF*% scopeUPLFOࢎਊHSBOUػӂೠ QSJWBUFGJFME ݽٚ Payload Fieldܳ ࢎਊೡ ਃח হ!
+85ܳࢎਊೠੋૐର Client Server (ੋૐ + API) 1045 MPHJOFOEQPJOU XJUI*% 18
"1*4FSWFS SFEJSFDU ੋૐࢲߡ ੋૐറ+85షߊә +85షਸ#SPXTFSীੜ ఃPSTFTTJPO 4FSWJDFSFRVFTUXJUI+85UPLFOJO"VUIPSJ[BUJPO)FBEFS +85TJHOBUVSFѨૐ ਃೠ҃ +85ীࢲ "VUIPSJUZѨૐറࢲ࠺झઁҕ 1SPUFDUFE3FTPVSDF4FSWJDFSFTQPOTF
8IZ+85 ׳߉UPLFOীೞৈੋૐࢲߡী೧UPLFOWBMJEJUZৈࠗܳޛযࠅਃ оহ ޖ۰JOEVTUSZTUBOEBSE 3'$ 5PLFOӝ߈ੋૐߑधীࢲUPLFOGPSNBUਵ۽ੋ߉
ޙઁب݅ٯ উ ইפѢUPLFOఎஂظࢲڙэUPLFOਵ۽SFRVFTUզܻݶޙઁغחѪইצ оਃ эషীೞৈэࢎۈੋधэ*%18۽эࢎۈੋधҗэޙ ઁ +85ࠁউࢿҗחܲোѾా۽ޙઁ
1ZUIPOীࢲ+85ܳࢎਊೞחੋૐ"1*ࢲߡٜ݅ӝ
3FRVJSFEQBDLBHFT য়טࢎਊೡౚషܻীࢲࢎਊೡಁఃݾ۾ 1Z+85 'MBTL 3FRVFTUT
QJQJOTUBMM1Z+85'MBTLSFRVFTUT
BVUI@TFSWFSQZ from flask import Flask, jsonify import jwt import datetime
app = Flask(__name__) secret_key = ‘secret_key' # This should be handled by configuration DB or third-party tool @app.route('/') def index(): return "Hello auth server” # Test for server is working @app.route("/get_token") def get_token(): # db স ਃפ. (ౚషܻীࢲח ࢤۚפ.) issuer = 'pycon_tutorial' subject = ‘localhost:5000/v1' date_time_obj = datetime.datetime exp_time = date_time_obj.timestamp(date_time_obj.utcnow() + datetime.timedelta(hours=24)) scope = ['v1', 'v2'] payload = { 'sub': subject, 'iss': issuer, 'exp': int(exp_time), 'scope': scope } token = jwt.encode(payload, secret_key, algorithm='HS256') return jsonify({ 'msg': 'token is generated', 'access_token': str(token) }), 201
BVUI@TFSWFSQZ ҅ࣘ @app.route("/admin_login") def admin_login(): issuer = ‘pycon_tutorial' # scope
ܲ login যڌѱ ܻೡө? subject = 'localhost:5000/v1' date_time_obj = datetime.datetime exp_time = date_time_obj.timestamp(date_time_obj.utcnow() + datetime.timedelta(hours=24)) scope = ['v1', 'v2', 'v3'] payload = { 'sub': subject, 'iss': issuer, 'exp': int(exp_time), 'scope': scope } token = jwt.encode(payload, secret_key, algorithm='HS256') return jsonify({ 'msg': 'token is generated', 'access_token': str(token) }), 201 app.run(port=5001)
TFSWJDF@TFSWFSQZ from flask import Flask, jsonify, abort, request import jwt
import requests from functools import wraps app = Flask(__name__) secret_key = 'secret_key' # third-party ో۽ ҙܻغযঠ פ. def jwt_token_required(f): # flask-jwt-extendedীب ઁҕೞח Ѫۢ ؘۨఠ ୶оೞӝ / customizing оמೞب۾ self ҳഅ @wraps(f) def decorated_function(*args, **kwargs): # header ରח # token ࢚ੋѤ if not 'Authorization' in request.headers: return jsonify({ 'msg': 'token is not given' }), 400 token = request.headers['Authorization'] try: decoded_token = jwt.decode(token, secret_key, algorithms=['HS256']) except: return jsonify({ 'msg': 'Invalid token given' }), 400 kwargs['decoded_token'] = decoded_token return f(*args, **kwargs) return decorated_function @app.route('/') def index(): return "Hello Pycon Server"
TFSWJDF@TFSWFSQZ ҅ࣘ @app.route('/login') def login(): r = requests.get(“http://127.0.0.1:5001/get_token") # login
auth-serverী షਸ ߊә߉ח ೯ਤ rsp_json = r.json() token = rsp_json['access_token'] return jsonify({ 'access_token': token, 'msg': "Successfully login" }), 200 @app.route("/protected_service") @jwt_token_required def protected_service(**kwargs): return jsonify({ 'msg': 'hello protected user' }), 200 @app.route("/v3") @jwt_token_required def other_scope_service(**kwargs): token = kwargs['decoded_token'] if 'v3' in token[‘scope']: # ୶о۽ ӂೠ ഛੋ ೞח স / decoratorܳ ࣻೞৈ ࠗ࠙ਸ ઁѢೡ ࣻب णפ. # service logic return jsonify({ 'msg': 'welcome v3 user' }), 200 else: return jsonify({ 'msg': 'You are not authorized' }), 401 app.run()
"EWBODFE5PQJDT
5PLFOਸউೞѱࢎਊೞחߑߨ 5PLFO߈٘द)UUQ0OMZDPPLJFীפ 944ҕѺߑ ఃܳࢎਊೞৈ+85ܳೠݶ $43'ীೠߑযܳ೧ঠפ ܻоݽܰחࢎীܲبݫੋীࢲਃৢࣻ
4FDSFULFZחੋૐ"1*ࢲߡ ࢲ࠺झ"1*ࢲߡ݅оҊযঠפ ݒߣషਸࠁউః۽Ѩૐ೧ࢲৢ߄ܲఃੋѨૐ೧ঠೣ ъ۱ೠఃߑध DMJFOUীח֢غঋইঠೣ хೠؘఠחQBZMPBEী֍ঋইঠפ 3FQMBZҕѺਸߑೞӝਤ೧ࢲחUPLFOਬബदрਸૣѱפ 3VMFPGUIVNCחઓೞঋઁҕೞחࢲ࠺झীٮۄࢲ
3FGSFTI5PLFO ߊәೠUPLFOਸୌ֙݅֙ࢎਊೡࣻחহणפ ੌӝрդUPLFOߊәਸ೧ঠפ ࢲ࠺झܳਃೞоࢎਊೞ؍UPLFOӝр݅ܐػ҃ SFGSFTI@UPLFOਸ ٜҊషߊәਃਸ೧ঠפ
5PLFOࠁҙߑߨࢎਊഋకীٮۄࢲ access_tokenTFSWJDF"1*ࢲߡࣻળীࢲ TFDSFULFZEFDPEFܳా೧ ࢲ ܻоמೞ݅ SFGSFTI@UPLFO߈٘दBVUIࢲߡܳా೧ࢲೠߣ؊Ѩૐ ਃפ ࢎਊоמೠUPLFOߊә۽ ECBDDFTTਃ
TFDSFU@LFZܳ߸҃ೞҊरযਃ ࢎप ઁח+85৬ҙ۲ݢઁ ࢜۽TFDSFU@LFZߊә+85৬ҙ۲হחօਸా೧ࢲߊәೞחѪݏ णפ о۸UIJSEQBSUZܳՙҊٜযоঠೠ؍о
FH )5514 PS44- DPOGJH@TFSWFSীࢲQVCTVCҳઑ۽ਊоמ
$MBJN֢JTTVF +85חױࣽIFBEFS DMBJN TJHOBUVSFܳCBTFFODPEJOHೠTUSJOH TJHOBUVSFחTFDSFULFZоನೣػTBMUFETUSJOHۄఎஂغযبղਊ߸ઑ غݶޙઁ9 DMBJNࢎਊੋૐࠁоӒ۽֢ؼ۰о
VTFSFNBJM١ӝఋࢎਊѐੋࠁо֢ؼ۰ оמೠ+85ীࠛਃೠࠁܳѱইঠפ FNBJMэ ߈ب QVCMJDೠࠁח֢غחࣽрܻTFSWJDFחইפ؊ ۄبܲ۽ޙઁоࢤӡࣻب ژחܲMBZFSب )5514 DMBJNਸ୶оঐഐചبхೠࢲ࠺झীࢲח Ҋ۰оמ
хࢎפ
"QQFOEJY"0"VUI'MPX From http://www.sauru.so/blog/basic-of-oauth2-and-jwt/
"QQFOEJY#0"VUI'MPX From http://earlybird.kr/1584