使用 Flask 和 rauth 進行 Github Oauth 登陸

lihua184 8年前發布 | 15K 次閱讀 Flask OAuth 安全相關 Github

來自: http://python.jobbole.com/84283/

最近我研究了一下Flask的OAuth庫。情況并不樂觀,大部分的包都已過時并且不再被支持了。大家熟知的Flask-Oauth基本上已經被廢棄了,我也不推薦依賴這個庫。有趣的是OAuth本身其實是很簡單的,認證之后你會得到一個訪問token,你只需將其作為GET參數或者作為request的特殊的header即可。也許其他的OAuth提供者需要更復雜的邏輯,但是Github僅此就足夠。

我在StackOverflow上請教了如何解決Flask-OAuth的問題 ,從rauth的作者那里得到回答,他建議試一下他寫的庫。本文就是對他及開源社區的回報。我寫了這個真實的例子,告訴你如何使用Flask、rauth、Github和會話來對你的站點的用戶進行認證。

首先你需要設置好本地的環境。你需要安裝好python、pip和virtualenv。接下來對環境進行設置:

$ virtualenv venv
$ source venv/bin/activate 
(venv)$ pip install rauth Flask Flask-SQLAlchemy
$ virtualenvvenv
$ sourcevenv/bin/activate 
(venv)$ pipinstallrauthFlaskFlask-SQLAlchemy

如果要運行我的例子,你還需要安裝Sqlite的python綁定,SQLAlchemy會需要它:

(venv)$ pip install pysqlite
(venv)$ pipinstallpysqlite

pip會安裝所有需要的依賴,所以你的環境已經就緒了。

現在你需要到Github設置頁面里找到 Applications sections ,為你設置好一個新的應用。下圖顯示了在我的配置區域。

下面是代碼,主模塊如下:

# github.py
from flask import (Flask, flash, request, redirect,
    render_template, url_for, session)
from flask.ext.sqlalchemy import SQLAlchemy

from rauth.service import OAuth2Service

# Flask config
SQLALCHEMY_DATABASE_URI = 'sqlite:///github.db'
SECRET_KEY = '\xfb\x12\xdf\xa1@i\xd6>V\xc0\xbb\x8fp\x16#Z\x0b\x81\xeb\x16'
DEBUG = True

# Flask setup
app = Flask(__name__)
app.config.from_object(__name__)
db = SQLAlchemy(app)

# Use your own values in your real application 
github = OAuth2Service(
    name='github',
    base_url='https://api.github.com/',
    access_token_url='https://github.com/login/oauth/access_token',
    authorize_url='https://github.com/login/oauth/authorize',
    client_id= '477151a6a9a9a25853de',
    client_secret= '23b97cc6de3bea712fddbef70a5f5780517449e4',
)

# models
class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    login = db.Column(db.String(80), unique=True)
    name = db.Column(db.String(120))

    def __init__(self, login, name):
        self.login = login
        self.name = name

    def __repr__(self):
        return '<User %r>' % self.login

    @staticmethod
    def get_or_create(login, name):
        user = User.query.filter_by(login=login).first()
        if user is None:
            user = User(login, name)
            db.session.add(user)
            db.session.commit()
        return user

# views
@app.route('/')
def index():
    return render_template('login.html')

@app.route('/about')
def about():
    if session.has_key('token'):
        auth = github.get_session(token = session['token'])
        resp = auth.get('/user')
        if resp.status_code == 200:
            user = resp.json()

        return render_template('about.html', user = user)
    else:
        return redirect(url_for('login'))

@app.route('/login')
def login():
    redirect_uri = url_for('authorized', next=request.args.get('next') or
        request.referrer or None, _external=True)
    print(redirect_uri)
    # More scopes http://developer.github.com/v3/oauth/#scopes
    params = {'redirect_uri': redirect_uri, 'scope': 'user:email'}
    print(github.get_authorize_url(**params))
    return redirect(github.get_authorize_url(**params))

# same path as on application settings page
@app.route('/github/callback')
def authorized():
    # check to make sure the user authorized the request
    if not 'code' in request.args:
        flash('You did not authorize the request')
        return redirect(url_for('index'))

    # make a request for the access token credentials using code
    redirect_uri = url_for('authorized', _external=True)

    data = dict(code=request.args['code'],
        redirect_uri=redirect_uri,
        scope='user:email,public_repo')

    auth = github.get_auth_session(data=data)

    # the "me" response
    me = auth.get('user').json()

    user = User.get_or_create(me['login'], me['name'])

    session['token'] = auth.access_token
    session['user_id'] = user.id

    flash('Logged in as ' + me['name'])
    return redirect(url_for('index'))

if __name__ == '__main__':
    db.create_all()
    app.run()
# github.py
fromflaskimport (Flask, flash, request, redirect,
    render_template, url_for, session)
fromflask.ext.sqlalchemyimportSQLAlchemy
 
fromrauth.serviceimportOAuth2Service
 
# Flask config
SQLALCHEMY_DATABASE_URI = 'sqlite:///github.db'
SECRET_KEY = '\xfb\x12\xdf\xa1@i\xd6>V\xc0\xbb\x8fp\x16#Z\x0b\x81\xeb\x16'
DEBUG = True
 
# Flask setup
app = Flask(__name__)
app.config.from_object(__name__)
db = SQLAlchemy(app)
 
# Use your own values in your real application
github = OAuth2Service(
    name='github',
    base_url='https://api.github.com/',
    access_token_url='https://github.com/login/oauth/access_token',
    authorize_url='https://github.com/login/oauth/authorize',
    client_id= '477151a6a9a9a25853de',
    client_secret= '23b97cc6de3bea712fddbef70a5f5780517449e4',
)
 
# models
class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    login = db.Column(db.String(80), unique=True)
    name = db.Column(db.String(120))
 
    def__init__(self, login, name):
        self.login = login
        self.name = name
 
    def__repr__(self):
        return '<User %r>' % self.login
 
    @staticmethod
    defget_or_create(login, name):
        user = User.query.filter_by(login=login).first()
        if useris None:
            user = User(login, name)
            db.session.add(user)
            db.session.commit()
        return user
 
# views
@app.route('/')
defindex():
    return render_template('login.html')
 
@app.route('/about')
defabout():
    if session.has_key('token'):
        auth = github.get_session(token = session['token'])
        resp = auth.get('/user')
        if resp.status_code == 200:
            user = resp.json()
 
        return render_template('about.html', user = user)
    else:
        return redirect(url_for('login'))
 
@app.route('/login')
deflogin():
    redirect_uri = url_for('authorized', next=request.args.get('next') or
        request.referreror None, _external=True)
    print(redirect_uri)
    # More scopes http://developer.github.com/v3/oauth/#scopes
    params = {'redirect_uri': redirect_uri, 'scope': 'user:email'}
    print(github.get_authorize_url(**params))
    return redirect(github.get_authorize_url(**params))
 
# same path as on application settings page
@app.route('/github/callback')
defauthorized():
    # check to make sure the user authorized the request
    if not 'code' in request.args:
        flash('You did not authorize the request')
        return redirect(url_for('index'))
 
    # make a request for the access token credentials using code
    redirect_uri = url_for('authorized', _external=True)
 
    data = dict(code=request.args['code'],
        redirect_uri=redirect_uri,
        scope='user:email,public_repo')
 
    auth = github.get_auth_session(data=data)
 
    # the "me" response
    me = auth.get('user').json()
 
    user = User.get_or_create(me['login'], me['name'])
 
    session['token'] = auth.access_token
    session['user_id'] = user.id
 
    flash('Logged in as ' + me['name'])
    return redirect(url_for('index'))
 
if __name__ == '__main__':
    db.create_all()
    app.run()

模板:

<!-- templates/about.html -->
<!doctype html>
<title>Login</title>

<h1>About you</h1>
Welcome , and your email is
<br />
<a href ="">Main page</a>
<!-- templates/about.html -->
<!doctypehtml>
<title>Login</title>
 
<h1>Aboutyou</h1>
Welcome , and youremailis
<br />
<a href ="">Mainpage</a>
<!-- templates/login.html -->
<!doctype html>
<title>Login</title>

<h1>Login</h1>
<a href ="">Login with OAuth2</a>
<h1>About</h1>
<a href="">About</a> If you are not logged in then you will be redirected to login page.
<!-- templates/login.html -->
<!doctypehtml>
<title>Login</title>
 
<h1>Login</h1>
<a href ="">LoginwithOAuth2</a>
<h1>About</h1>
<a href="">About</a> If youarenot loggedin then youwillberedirectedto loginpage.

啟動后將創建數據庫:

(venv)$ python github.py
(venv)$ pythongithub.py

在瀏覽器打開網站后,將在 session 保存 login.access_token,這是不安全的,但作為例子已經足夠了。實際的網站中你可以這樣使用:

# Probably your User model placed in different blueprint
from authmodule.models import User

@app.before_request
def before_request():
    g.user = None
    if 'user_id' in session:
        if session['user_id']:
            user = User.query.get(session['user_id'])
            if user:
                g.user = user
            else:
                del session['user_id']
# Probably your User model placed in different blueprint
fromauthmodule.modelsimportUser
 
@app.before_request
defbefore_request():
    g.user = None
    if 'user_id' in session:
        if session['user_id']:
            user = User.query.get(session['user_id'])
            if user:
                g.user = user
            else:
                delsession['user_id']
 本文由用戶 lihua184 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!