我已经搜索了很多关于使用不同的库,但他们都在某种意义上似乎醚过度杀伤(你可以使用它在任何平台上,但你需要大量的代码)或文档没有解释我想要的。长话短说 - 我从头开始编写它,从而了解真正的Google API认证过程。这听起来不那么难。基本上你需要遵循https://developers.google.com/accounts/docs/OAuth2WebServer准则,就是这样。 为此,您还需要注册https://code.google.com/apis/console/以生成凭证并注册您的链接。我使用了简单的子域指向我的办公室IP,因为它只允许域名。
对于用户登录/管理和会话我已经使用这个插件烧瓶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):
# check if user is logged in
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'[email protected]',
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:
#.one() ensures that there would be just one user with that email.
# Although database should prevent that from happening -
# lets make it buletproof
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
视图呈现与登录按钮指向谷歌的HTML - 谷歌重定向到Auth
视图。应该可能只是将用户重定向到谷歌,以防其登录用户的网站。
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) # must be POST
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) # must be GET
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'))
所以一切都是splited两个部分 - 一个用于获取谷歌令牌_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
文件,这样就可以更容易地跟踪我的看法,减少进口的东西以烧瓶应用程序创建文件:
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工作。如果您复制粘贴它 - 它可能需要一些补丁与烧瓶登录文档和SQLAlchemy映射,但想法是在那里。
多么美妙而完整的例子!非常感谢你。 – emning 2015-04-15 18:06:53
我在过去3年的Rauth github页面上看不到任何活动。看起来像死了的模块。 – Rusty 2016-04-22 16:08:52