另一个答案提到了Flask-Rauth,但是没有详细介绍如何使用它。有一些特定于Google的陷阱,但是我终于实现了它,并且效果很好。我将其与Flask-
Login集成在一起,因此可以使用有用的糖(例如)装饰我的视图
@login_required。
我希望能够支持多个OAuth2提供程序,因此部分代码是通用的,并基于Miguel
Grinberg在此处有关通过Facebook和Twitter支持OAuth2的出色文章。
首先,将来自Google的特定Google身份验证信息添加到应用程序的配置中:
GOOGLE_LOGIN_CLIENT_ID = "<your-id-ending-with>.apps.googleusercontent.com"GOOGLE_LOGIN_CLIENT_SECRET = "<your-secret>"OAUTH_CREDENTIALS={ 'google': { 'id': GOOGLE_LOGIN_CLIENT_ID, 'secret': GOOGLE_LOGIN_CLIENT_SECRET }}在创建应用程序时(在我的示例中,是模块的
__init__.py):
app = Flask(__name__)app.config.from_object('config')在您的应用模块中,创建
auth.py:
from flask import url_for, current_app, redirect, requestfrom rauth import OAuth2Serviceimport json, urllib2class OAuthSignIn(object): providers = None def __init__(self, provider_name): self.provider_name = provider_name credentials = current_app.config['OAUTH_CREDENTIALS'][provider_name] self.consumer_id = credentials['id'] self.consumer_secret = credentials['secret'] def authorize(self): pass def callback(self): pass def get_callback_url(self): return url_for('oauth_callback', provider=self.provider_name, _external=True) @classmethod def get_provider(self, provider_name): if self.providers is None: self.providers={} for provider_class in self.__subclasses__(): provider = provider_class() self.providers[provider.provider_name] = provider return self.providers[provider_name]class GoogleSignIn(OAuthSignIn): def __init__(self): super(GoogleSignIn, self).__init__('google') googleinfo = urllib2.urlopen('https://accounts.google.com/.well-known/openid-configuration') google_params = json.load(googleinfo) self.service = OAuth2Service( name='google', client_id=self.consumer_id, client_secret=self.consumer_secret, authorize_url=google_params.get('authorization_endpoint'), base_url=google_params.get('userinfo_endpoint'), access_token_url=google_params.get('token_endpoint') ) def authorize(self): return redirect(self.service.get_authorize_url( scope='email', response_type='pre', redirect_uri=self.get_callback_url()) ) def callback(self): if 'pre' not in request.args: return None, None, None oauth_session = self.service.get_auth_session( data={'pre': request.args['pre'],'grant_type': 'authorization_pre','redirect_uri': self.get_callback_url() }, deprer = json.loads ) me = oauth_session.get('').json() return (me['name'], me['email'])这将创建一个
OAuthSignIn可以子类化的通用类。Google子类从Google发布的信息列表(此处为JSON格式)中提取信息。这些信息会随时更改,因此这种方法将确保它始终是最新的。这种限制之一是,如果在初始化Flask应用程序(导入的模块)时服务器上没有Internet连接,则将无法正确实例化它。这几乎永远不会成为问题,但是在配置数据库中存储最新的值以解决这种情况是个好主意。
最后,该类
name,email在
callback()函数中返回一个元组。Google实际上会返回更多信息,包括Google+个人资料(如果有)。检查返回的字典
oauth_session.get('').json()以查看全部内容。如果authorize()您在函数中扩展了范围(对于我的应用程序来说
API访问更多信息。
接下来,编写 视图 以将它们捆绑在一起:
from flask.ext.login import login_user, logout_user, current_user, login_required@app.route('/authorize/<provider>')def oauth_authorize(provider): # Flask-Login function if not current_user.is_anonymous(): return redirect(url_for('index')) oauth = OAuthSignIn.get_provider(provider) return oauth.authorize()@app.route('/callback/<provider>')def oauth_callback(provider): if not current_user.is_anonymous(): return redirect(url_for('index')) oauth = OAuthSignIn.get_provider(provider) username, email = oauth.callback() if email is None: # I need a valid email address for my user identification flash('Authentication failed.') return redirect(url_for('index')) # Look if the user already exists user=User.query.filter_by(email=email).first() if not user: # Create the user. Try and use their name returned by Google, # but if it is not set, split the email address at the @. nickname = username if nickname is None or nickname == "": nickname = email.split('@')[0] # We can do more work here to ensure a unique nickname, if you # require that. user=User(nickname=nickname, email=email) db.session.add(user) db.session.commit() # Log in the user, by default remembering them for their next visit # unless they log out. login_user(user, remember=True) return redirect(url_for('index'))最后,我的
/login视图和模板使这一切变为现实:
@app.route('/login', methods=['GET', 'POST'])def login(): if g.user is not None and g.user.is_authenticated(): return redirect(url_for('index')) return render_template('login.html', title='Sign In')login.html:
{% extends "base.html" %}{% block content %} <div id="sign-in"> <h1>Sign In</h1> <p> <a href={{ url_for('oauth_authorize', provider='google') }}><img src="{{ url_for('static', filename='img/sign-in-with-google.png') }}" /></a> </div>{% endblock %}确保正确的回调地址已在Google中注册,并且用户只需在您的登录页面上单击“使用Google登录”,它将进行注册并登录。



