32
35

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Flask使ってAPI!認証機能を作ってみたよ みんな見てね

Last updated at Posted at 2019-01-21

#はじめに
APIって何なのってとこから始まり、色々調べてくうちに:skull_crossbones:Flask:skull_crossbones:に辿り着きました。
とりあえず触ってみようってことで私の軌跡を残そうと思います。

IT企業に入社しましたが運用系にいるため開発は趣味として行っています。
理解していないことも多く、間違ったことたくさんあると思うのでご指摘の程よろしくお願いします。

Ubuntu18.04,Python3を使っています。

#問題・やりたいこと

  • flaskを使ってAPIを実装する
  • ユーザー認証後、アクセストークンを発行する

#解決策・実施したこと

英二:baseball:はパスワードに大好きな"ゆでたまご"を設定しています。

User = {
    'eiji':{
        #yudetamago
        'password':'pbkdf2:sha256:50000$j3QeNmki$6bf450823b50d5f0ee308bf8bd001d596323081ce227fb3f68612e440bf891c6'
    }
}

ゆでたまごはwerkzeugのgenerate_password_hashを使ってハッシュ化しています。

$python
>>> from werkzeug.security import generate_password_hash
>>> generate_password_hash('yudetamago')
'pbkdf2:sha256:50000$j3QeNmki$6bf450823b50d5f0ee308bf8bd001d596323081ce227fb3f68612e440bf891c6'

ユーザー情報とPOSTされた情報を照合していきます。

from flask import Flask,request,make_response,jsonify
from werkzeug.security import check_password_hash
import datetime
import jwt

@api.route('/authorize',methods=['POST'])
def authorize():
    input_name = request.json['name']
    input_password = request.json['password']
    eiji_pass = User[input_name]['password']

    if not check_password_hash(eiji_pass,input_password):
        return make_response('Password is incorrect',400)

POSTされたデータからnameとpasswordを取り出し、nameに該当するパスワードを取り出します。
check_password_hash()ではPOSTされたパスワードと登録されたパスワードを使ってチェックします。


    exp = datetime.datetime.utcnow() + datetime.timedelta(hours=1)
    encoded = jwt.encode({'name': input_name,'exp': exp}, 'SECRET_KEY', algorithm='HS256')
    response = {'user':input_name,'token':encoded.decode('utf-8')}
    return make_response(jsonify(response),200)

ユーザー名とパスワードが正しかった場合、アクセストークンを発行します。
名前とシークレット値と有効時間から作ってます。

flaskを実行し、別のターミナルからcurlコマンドを実行します。

$ curl http://localhost:5000/authorize -X POST -H "Content-Type: application/json" -d '{"name":"eiji","password":"yudetamago"}'
{
  "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiZWlqaSIsImV4cCI6MTU0NzQ1NzQ3NH0.XTHMba76dec2dzlFl0QF_d4r0XFW1DFdy2tE-VfpKWE",
  "user": "eiji"
}

パスワードが正しければユーザー名とアクセストークンを返してくれます。

認証後、アクセストークンを使うためにlogin_required()を作ります。

from flask import Flask,request,abort
import functools
import jwt


def login_required(method):
    @functools.wraps(method)
    def wrapper(*args, **kwargs):
        header = request.headers.get('Authorization')
        _,token = header.split()
        try:
            decoded = jwt.decode(token,'SECRET_KEY',algorithms='HS256')
            username=decoded['name']
        except jwt.DecodeError:
            abort(400,message='Token is not valid.')
        except jwt.ExpiredSignatureError:
            abort(400,message='Token is expired.')
        return method(username,*args, **kwargs)
    return wrapper

中身はヘッダーにアクセストークンが含まれるか、有効時間内かをチェックしています。

@api.route('/user',methods=['GET'])
@login_required
def user(username):
     return 'darega bandou eiji ya!' 

取得したアクセストークンをヘッダーとしてリクエストします。

curl http://localhost:5000/user -X GET -H '{"Authorization: JWT アクセストークン"}'

>>>eiji: darega bando eiji ya!

#おわりに

こんな感じかなっていうノリで認証機能を実装してみました。
APIも機能を合わせて作ってみました。

jwtはPyJwtと明示してpip installしないとjwtというパッケージをインストールしちゃうかもよ
きをつけてね

Markdown方式でプレビューしながら書いてるとプレビュー側を編集しようとしてしまう。

・参考

jwtの中身を見る

32
35
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
32
35

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?