我曾经搜索了很多使用不同库的方法,但它们中的所有库似乎在某种意义上都过于复杂(你可以在任何平台上使用它,但你需要大量代码),或者文档没有解释我想要的内容。 简而言之 - 我从头开始编写了认证 Google API 的过程,因此理解了整个流程。 它并不像听起来那么难。 基本上,您需要遵循https://developers.google.com/accounts/docs/OAuth2WebServer的指南,就这样。为此,您还需要在https://code.google.com/apis/console/注册以生成凭据并注册您的链接。 我使用了一个简单的子域名指向我的办公室IP,因为它只允许域名。
对于用户登录/管理和会话,我使用了这个Flask插件http://packages.python.org/Flask-Login/ - 一些代码基于此。
所以首先是索引视图:
from flask import render_template
from flask.ext.login import current_user
from flask.views import MethodView
from myapp import app
class Index(MethodView):
def get(self):
if not current_user.is_authenticated():
return app.login_manager.unauthorized()
return render_template('index.html')
所以在认证用户之前,这个视图将不会打开。
谈到用户 - 用户模型:
from sqlalchemy.orm.exc import NoResultFound
from sqlalchemy import Column, Integer, DateTime, Boolean, String
from flask.ext.login import UserMixin
from myapp.metadata import Session, Base
class User(Base):
__tablename__ = 'myapp_users'
id = Column(Integer, primary_key=True)
email = Column(String(80), unique=True, nullable=False)
username = Column(String(80), unique=True, nullable=False)
def __init__(self, email, username):
self.email = email
self.username = username
def __repr__(self):
return "<User('%d', '%s', '%s')>" \
% (self.id, self.username, self.email)
@classmethod
def get_or_create(cls, data):
"""
data contains:
{u'family_name': u'Surname',
u'name': u'Name Surname',
u'picture': u'https://link.to.photo',
u'locale': u'en',
u'gender': u'male',
u'email': u'propper@email.com',
u'birthday': u'0000-08-17',
u'link': u'https://plus.google.com/id',
u'given_name': u'Name',
u'id': u'Google ID',
u'verified_email': True}
"""
try:
user = Session.query(cls).filter_by(email=data['email']).one()
except NoResultFound:
user = cls(
email=data['email'],
username=data['given_name'],
)
Session.add(user)
Session.commit()
return user
def is_active(self):
return True
def is_authenticated(self):
"""
Returns `True`. User is always authenticated. Herp Derp.
"""
return True
def is_anonymous(self):
"""
Returns `False`. There are no Anonymous here.
"""
return False
def get_id(self):
"""
Assuming that the user object has an `id` attribute, this will take
that and convert it to `unicode`.
"""
try:
return unicode(self.id)
except AttributeError:
raise NotImplementedError("No `id` attribute - override get_id")
def __eq__(self, other):
"""
Checks the equality of two `UserMixin` objects using `get_id`.
"""
if isinstance(other, UserMixin):
return self.get_id() == other.get_id()
return NotImplemented
def __ne__(self, other):
"""
Checks the inequality of two `UserMixin` objects using `get_id`.
"""
equal = self.__eq__(other)
if equal is NotImplemented:
return NotImplemented
return not equal
可能是UserMixin出了问题,但我稍后会处理。您的用户模型将看起来不同,只需使其与flask-login兼容即可。
所以剩下的就是身份验证本身。 我为flask-login
设置了登录视图为'login'
。Login
视图呈现带有指向Google的登录按钮的HTML - Google重定向到Auth
视图。如果这是仅供已登录用户使用的网站,则可以直接将用户重定向到Google。
import logging
import urllib
import urllib2
import json
from flask import render_template, url_for, request, redirect
from flask.views import MethodView
from flask.ext.login import login_user
from myapp import settings
from myapp.models import User
logger = logging.getLogger(__name__)
class Login(BaseViewMixin):
def get(self):
logger.debug('GET: %s' % request.args)
params = {
'response_type': 'code',
'client_id': settings.GOOGLE_API_CLIENT_ID,
'redirect_uri': url_for('auth', _external=True),
'scope': settings.GOOGLE_API_SCOPE,
'state': request.args.get('next'),
}
logger.debug('Login Params: %s' % params)
url = settings.GOOGLE_OAUTH2_URL + 'auth?' + urllib.urlencode(params)
context = {'login_url': url}
return render_template('login.html', **context)
class Auth(MethodView):
def _get_token(self):
params = {
'code': request.args.get('code'),
'client_id': settings.GOOGLE_API_CLIENT_ID,
'client_secret': settings.GOOGLE_API_CLIENT_SECRET,
'redirect_uri': url_for('auth', _external=True),
'grant_type': 'authorization_code',
}
payload = urllib.urlencode(params)
url = settings.GOOGLE_OAUTH2_URL + 'token'
req = urllib2.Request(url, payload)
return json.loads(urllib2.urlopen(req).read())
def _get_data(self, response):
params = {
'access_token': response['access_token'],
}
payload = urllib.urlencode(params)
url = settings.GOOGLE_API_URL + 'userinfo?' + payload
req = urllib2.Request(url)
return json.loads(urllib2.urlopen(req).read())
def get(self):
logger.debug('GET: %s' % request.args)
response = self._get_token()
logger.debug('Google Response: %s' % response)
data = self._get_data(response)
logger.debug('Google Data: %s' % data)
user = User.get_or_create(data)
login_user(user)
logger.debug('User Login: %s' % user)
return redirect(request.args.get('state') or url_for('index'))
所以所有内容都被分为两部分 - 一个用于在_get_token
中获取谷歌令牌,另一个用于使用它并检索基本用户数据在_get_data
中。
我的设置文件包含:
GOOGLE_API_CLIENT_ID = 'myid.apps.googleusercontent.com'
GOOGLE_API_CLIENT_SECRET = 'my secret code'
GOOGLE_API_SCOPE = 'https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email'
GOOGLE_OAUTH2_URL = 'https://accounts.google.com/o/oauth2/'
GOOGLE_API_URL = 'https://www.googleapis.com/oauth2/v1/'
记住,视图必须附加到应用程序的URL路径,因此我使用这个urls.py
文件,以便更轻松地跟踪我的视图并在Flask应用程序创建文件中导入较少的内容:
from myapp import app
from myapp.views.auth import Login, Auth
from myapp.views.index import Index
urls = {
'/login/': Login.as_view('login'),
'/auth/': Auth.as_view('auth'),
'/': Index.as_view('index'),
}
for url, view in urls.iteritems():
app.add_url_rule(url, view_func=view)
所有这些综合在一起,使得在Flask中工作Google授权变得容易。如果你复制粘贴它 - 你可能需要在flask-login的文档和SQLAlchemy映射中进行一些修改,但是思路已经在那里。