Hola usuario de nuestro sitio web, descubrimos la respuesta a tu pregunta, deslízate y la encontrarás más abajo.
Solución:
Otra respuesta menciona Flask-Rauth, pero no entra en detalles sobre cómo usarlo. Hay algunas trampas específicas de Google, pero finalmente las he implementado y funciona bien. Lo integro con Flask-Login para poder decorar mis vistas con azúcar útil como @login_required
.
Quería poder admitir múltiples proveedores de OAuth2, por lo que parte del código es genérico y se basa en la excelente publicación de Miguel Grinberg sobre el soporte de OAuth2 con Facebook y Twitter aquí.
Primero, agregue su información de autenticación de Google específica de Google en la configuración de su aplicación:
GOOGLE_LOGIN_CLIENT_ID = ".apps.googleusercontent.com"
GOOGLE_LOGIN_CLIENT_SECRET = ""
OAUTH_CREDENTIALS=
'google':
'id': GOOGLE_LOGIN_CLIENT_ID,
'secret': GOOGLE_LOGIN_CLIENT_SECRET
Y cuando crea su aplicación (en mi caso, el módulo __init__.py
):
app = Flask(__name__)
app.config.from_object('config')
En el módulo de su aplicación, cree auth.py
:
from flask import url_for, current_app, redirect, request
from rauth import OAuth2Service
import json, urllib2
class 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='code',
redirect_uri=self.get_callback_url())
)
def callback(self):
if 'code' not in request.args:
return None, None, None
oauth_session = self.service.get_auth_session(
data='code': request.args['code'],
'grant_type': 'authorization_code',
'redirect_uri': self.get_callback_url()
,
decoder = json.loads
)
me = oauth_session.get('').json()
return (me['name'],
me['email'])
Esto crea un genérico OAuthSignIn
clase que puede ser subclasificada. La subclase de Google extrae su información de la lista de información publicada de Google (en formato JSON aquí). Esta es información que está sujeta a cambios, por lo que este enfoque asegurará que esté siempre actualizada. Una limitación de esto es que si una conexión a Internet no está disponible en su servidor en el momento en que se inicializa la aplicación Flask (el módulo importado), no se instanciará correctamente. Esto casi nunca debería ser un problema, pero almacenar los últimos valores conocidos en la base de datos de configuración para cubrir esta eventualidad es una buena idea.
Finalmente, la clase devuelve una tupla de name, email
en el callback()
función. De hecho, Google devuelve mucha más información, incluido el perfil de Google+, si está disponible. Inspeccione el diccionario devuelto por oauth_session.get('').json()
para verlo todo. Si en el authorize()
función amplía el alcance (para mi aplicación, email
es suficiente), puede acceder a más información a través de la API de Google.
A continuación, escriba el puntos de vista para atarlo todo junto:
from flask.ext.login import login_user, logout_user, current_user, login_required
@app.route('/authorize/')
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/')
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'))
Finalmente, mi /login
vista y plantilla para que todo suceda:
@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 %
% endblock %
Asegúrese de que las direcciones de devolución de llamada correctas estén registradas en Google, y el usuario simplemente debe hacer clic en “Iniciar sesión con Google” en su página de inicio de sesión, y las registrará e iniciará sesión.
He buscado bastante sobre el uso de diferentes bibliotecas, pero todas me parecían exageradas en cierto sentido (puede usarlo en cualquier plataforma, pero para eso necesita toneladas de código) o la documentación no explicaba lo que quería. En pocas palabras: lo escribí desde cero, entendiendo así el proceso de autenticación. true API de Google. No es tan difícil como parece. Básicamente, debe seguir las https://developers.google.com/accounts/docs/OAuth2WebServer pautas y eso es todo. Para ello, también deberá registrarse en https://code.google.com/apis/console/ para generar credenciales y registrar sus enlaces. He usado un subdominio simple que apunta a la IP de mi oficina, ya que solo permite dominios.
Para el inicio de sesión / administración de usuarios y las sesiones, he usado este complemento para el matraz http://packages.python.org/Flask-Login/; habrá algún código basado en eso.
Así que lo primero es lo primero: vista de índice:
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')
por lo que esta vista no se abrirá hasta que tengamos un usuario autenticado. Hablando de usuarios – modelo de usuario:
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 ""
% (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
Probablemente haya algo mal con UserMixin, pero me ocuparé de eso último. Su modelo de usuario se verá diferente, solo hágalo compatible con flask-login.
Entonces, lo que queda: la autenticación en sí misma. Me puse para flask-login
esa vista de inicio de sesión es 'login'
. Login
ver renderiza html con un botón de inicio de sesión que apunta a google – google redirecciona a Auth
vista. Debería ser posible simplemente redirigir al usuario a Google en caso de que su sitio web sea solo para usuarios registrados.
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'))
Entonces, todo se divide en dos partes: una para obtener el token de Google _get_token
. Otro para usarlo y recuperar datos básicos del usuario en _get_data
.
Mi archivo de configuración contiene:
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/'
Tenga en cuenta que las vistas deben tener una ruta de URL adjunta a la aplicación, por lo que he usado esta urls.py
para poder rastrear mis vistas más fácilmente e importar menos cosas al archivo de creación de la aplicación del matraz:
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)
Todo esto en conjunto hace que la autorización de Google funcione en Flask. Si lo copia y lo pega, podría necesitar algunos arreglos con la documentación de inicio de sesión del matraz y las asignaciones de SQLAlchemy, pero la idea está ahí.
Prueba Authomatic (soy el mantenedor de ese proyecto). Es muy simple de usar, funciona con cualquier marco de Python y apoya 16 OAuth 2.0, 10 OAuth 1.0a proveedores y OpenID.
A continuación, se muestra un ejemplo sencillo sobre cómo autenticar a un usuario con Google y obtener su lista de videos de YouTube:
# main.py
from flask import Flask, request, make_response, render_template
from authomatic.adapters import WerkzeugAdapter
from authomatic import Authomatic
from authomatic.providers import oauth2
CONFIG =
'google':
'class_': oauth2.Google,
'consumer_key': '########################',
'consumer_secret': '########################',
'scope': oauth2.Google.user_info_scope + ['https://gdata.youtube.com'],
,
app = Flask(__name__)
authomatic = Authomatic(CONFIG, 'random secret string for session signing')
@app.route('/login//', methods=['GET', 'POST'])
def login(provider_name):
response = make_response()
# Authenticate the user
result = authomatic.login(WerkzeugAdapter(request, response), provider_name)
if result:
videos = []
if result.user:
# Get user info
result.user.update()
# Talk to Google YouTube API
if result.user.credentials:
response = result.provider.access('https://gdata.youtube.com/'
'feeds/api/users/default/playlists?alt=json')
if response.status == 200:
videos = response.data.get('feed', ).get('entry', [])
return render_template(user_name=result.user.name,
user_email=result.user.email,
user_id=result.user.id,
youtube_videos=videos)
return response
if __name__ == '__main__':
app.run(debug=True)
También hay un tutorial de Flask muy simple que muestra cómo autenticar a un usuario por Facebook y Twitter y hablar con sus API para leer las fuentes de noticias del usuario.
Ten en cuenta dar recomendación a esta noticia si te fue de ayuda.