我已經搜索了很多關於使用不同的庫,但他們都在某種意義上似乎醚過度殺傷(你可以使用它在任何平臺上,但你需要大量的代碼)或文檔沒有解釋我想要的。長話短說 - 我從頭開始編寫它,從而瞭解真正的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