J'ai cherché un peu sur l'utilisation de bibliothèques différentes, mais tous semblaient éther overkill dans un certain sens (vous pouvez l'utiliser sur n'importe quelle plateforme, mais pour cela vous avez besoin d'une tonne de code) ou de la documentation n'a pas expliqué ce que je voulais. Longue histoire courte, j'ai écrit à partir de zéro ainsi la compréhension des processus d'authentification vrai API de Google. Il n'est pas aussi difficile qu'il y paraît. Fondamentalement, vous devez suivre https://developers.google.com/accounts/docs/OAuth2WebServer lignes directrices et c'est tout.
Pour cela, vous aurez également besoin de s'inscrire à https://code.google.com/apis/console/ pour générer des informations d'identification et d'enregistrer vos liens. J'ai utilisé de simples sous-domaine qui pointe sur mon IP du bureau, car il ne permet de domaines.
Pour la connexion de l'utilisateur, la gestion et les sessions, j'ai utilisé ce plugin pour flacon http://packages.python.org/Flask-Login/ - il y aura un peu de code sur cette base.
Donc, la première chose à première vue "index":
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')
donc, ce ne sera pas ouverte jusqu'à ce que nous aurons de l'utilisateur authentifié.
Parler des utilisateurs de l'utilisateur modèle:
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:
#.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
Il y a probablement quelque chose de mal avec UserMixin, mais je vais faire face à ce dernier. Votre modèle utilisateur va regarder différemment, il suffit de le rendre compatible avec le flacon de connexion.
Donc ce qui est à gauche de l'authentification elle-même. J'ai mis pour flask-login
que la connexion view est - 'login'
. Login
vue affiche le code html avec le bouton de connexion qui pointe vers google - google redirige vers Auth
vue. Il devrait être possible juste pour rediriger les utilisateurs de google dans le cas du site web uniquement pour les utilisateurs enregistrés.
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'))
Donc, tout est découpé de deux parties - une pour google jeton en _get_token
. D'autres pour l'utilisation et la récupération de base de données de l'utilisateur en _get_data
.
Mon fichier de paramètres contient:
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/'
Gardez à l'esprit que les points de vue doit avoir l'adresse de l'url attaché à l'application j'ai donc utiliser cette urls.py
le fichier afin que je puisse suivre mon point de vue plus facilement et importer moins de choses à gourde application de création de fichier:
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)
Tout cela rend le travail de Google autorisation dans le Flacon. Si vous copiez collez - il pourrait prendre un certain raccommoder avec flacon de connexion de la documentation et de SQLAlchemy mappages, mais l'idée est là.