django QQ认证登录
一、开发环境
django 1.10.2
python3.5.2
django-social-auth 0.3.6
二、申请QQ互联 APPID及SECRECT_KEY
大致步骤:
1.成为QQ互联开发者
2.创建应用
3.获取应用app_id 及secret_key
三、代码
1.安装认证框架
$ pip3 install social-auth-app-django2.在INSTALLED_APPS中添加应用(settings.py)
INSTALLED_APPS = ( ... 'social_django', ... )
3.更新数据库
python3 managy.py migrate
4.在认证后端添加QQ认证以及QQ互联的key和id(settings.py)
AUTHENTICATION_BACKENDS = ( ... 'social_core.backends.qq.QQOAuth2', ) SOCIAL_AUTH_QQ_KEY = 'your qq app id,like some number,for examle 230402020' # QQ APP_ID SOCIAL_AUTH_QQ_SECRET = 'you qq seckey ,combined by letter with number,for examle: f123bas324' # QQ SECRECT_KEY SOCIAL_AUTH_QQ_USE_OPENID_AS_USERNAME = True5.添加根urls.py的URL路由
urlpatterns = [ ...... url('', include('social_django.urls', namespace='social')) ]6.在需要登录的地方加上如下代码:
<a href="{% url "social:begin" "qq" %}">QQ登录 <!-- 这个登录标识注意要满足QQ互联的要求 --></a>
7.settings.py中的TEMPALTES如下:
TEMPLATES = [ { ... 'OPTIONS': { ... 'context_processors': [ ... 'social_django.context_processors.backends', 'social_django.context_processors.login_redirect', ... ] } } ]
四、可能出现的问题
1.认证页面是空白页面
解决方案,检查APPID和SECRET_KEY是否填写正确
2.redirect_url不合法 10010
解决方案,注意域名的一致性,此问题是由于http请求中url和QQ互联中的 回调地址不同引起的,
https://graph.qq.com/oauth/show?which=Login&display=pc&redirect_uri=http://localhost.com:8080/complete/qq/&client_id=xxxx&response_type=code&state=xxxx
在实际使用过程中出现同样问题,主要是因为网站使用了多个域名,使用www.czxxs.cn和czxxs.cn都能访问网站,此时可以把回调地址设置为两个地址,如下:
再次尝试,错误消失
3.用openid代替昵称存储在数据库,在settings.py中加上以下语句(避免QQ昵称相同引起用户存储错误)
SOCIAL_AUTH_QQ_USE_OPENID_AS_USERNAME = True
参考连接:http://python-social-auth.readthedocs.io/en/latest/configuration/django.html
http://python-social-auth.readthedocs.io/en/latest/backends/qq.html
备注:
功能可以根据上面两个连接进行完善,有问题欢迎留言
五、扩展(Pipeline管道),实现认证流程的自定义
用途:在认证过程中,可以加入自己的函数,实现认证过程的定制化。
原理:自定义的函数会收到当前进程传递的参数,包括strategy,user,和request,建议在定义函数的时候额外添加**kwargs参数来避免 未知参数错误。
返回:管道(自定义的函数)可以返回dict或者None,如果返回其他内容,这些内容则会被看做 一个 response,然后会直接返回给客户端,(Partial Pipeline会介绍原理)
如果返回的dict,那么dict的值会合并到 kwargs参数中,然后传递给下一个pipeline,如果返回None将被看做返回了'{}'空的字典
(认证管道)Authentication Pipeline
认证流程的最后一步,是处理一系列可操作的管道,可以在管道中添加自定义的功能,也可以删除默认的管道。认证系统默认的管道是创建用户实例,然后从认证服务商处获取一些基本的信息。默认的管道如下:
( # 从服务商获取用户信息,然后以一种简单的格式返回,用来将来创建用户使用 # 有时候用户的信息已经在认证消息的 Response 响应中了,但是有时需要使用服务商提供的api 'social_core.pipeline.social_auth.social_details', # 从服务商处获取 social uid,这个uid是服务商给用户的唯一标识 'social_core.pipeline.social_auth.social_uid', # 验证当前认证流程是否合法,需要提供邮箱和域名白名单(好像没用到过,可以自己深入研究) 'social_core.pipeline.social_auth.auth_allowed', # 判断当前认证账户是否已经关联到了网站用户、 'social_core.pipeline.social_auth.social_user', # 为认证用户创建一个用户名,如果用户名冲突则在用户名后添加随机字符串 'social_core.pipeline.user.get_username', # 给用户发送验证邮件,验证其邮箱的真实性,默认禁用 # 'social_core.pipeline.mail.mail_validation', # 把 当前社交认证信息和其他 邮箱相似的用户关联起来,默认禁用 # 'social_core.pipeline.social_auth.associate_by_email', #如果没有发现用户账户则创建用户(以social user的信息创建) 'social_core.pipeline.user.create_user', #创建将社交帐户与用户关联的记录。 'social_core.pipeline.social_auth.associate_user', #使用settings.py指定的值(和access_token等默认值)填充社交记录中的extra_data字段。 'social_core.pipeline.social_auth.load_extra_data', #使用来自auth服务的任何更改的信息更新用户记录。 'social_core.pipeline.user.user_details', )可以在setting中通过设置SOCIAL_AUTH_PIPELINE来覆盖默认的管道。例如可以通过设置如下变量来达到,
不创建用户,只与已经存在的用户关联的目的。
SOCIAL_AUTH_PIPELINE = ( 'social_core.pipeline.social_auth.social_details', 'social_core.pipeline.social_auth.social_uid', 'social_core.pipeline.social_auth.auth_allowed', 'social_core.pipeline.social_auth.social_user', 'social_core.pipeline.social_auth.associate_user', 'social_core.pipeline.social_auth.load_extra_data', 'social_core.pipeline.user.user_details', )
这是在用户已经授权的情况下可以通过这种方式来关联用户,因此在dict中会有 user这一键值。如果认证完全来自外部,必须提供一个用来产生 user键值的管道,比如:
SOCIAL_AUTH_PIPELINE = ( 'social_core.pipeline.social_auth.social_details', 'social_core.pipeline.social_auth.social_uid', 'social_core.pipeline.social_auth.auth_allowed', 'myapp.pipeline.load_user', 'social_core.pipeline.social_auth.social_user', 'social_core.pipeline.social_auth.associate_user', 'social_core.pipeline.social_auth.load_extra_data', 'social_core.pipeline.user.user_details', )也可以在setting中为每一个后端定制管道,例如 SOCIAL_AUTH_TWITTER_PIPELINE,指定的pipeline会覆盖默认的pipeline。
每个pipeline函数会收到以下参数:
1.strategy(包含访问当前存储,后端和请求的参数)
2.社交认证端给出的userid
3.社交认证端给出的用户信息
4.is_new标志,默认值为False
5.传递给 auth_complete方法的参数,默认的视图传递以下参数:
①当前登录的用户,如果未登录,是None
②当前的request
Disconnection Pipeline(断开管道)
和认证管道想反,当用户断开社交账户关联时的功能。
比如,当用户断开所有社交账户关联时,需要用户填写登录密码(这种情况是针对,在认证管道中直接创建用户的情况,认证管道中存在'social_core.pipeline.user.create_user' 这条语句时才需要考虑断开管道,因为这个管道会直接用用户的社交 user_id创建用户名,但是却可以不存在密码如下图)。
可以通过覆盖默认的断开管道,然后添加一个可以检查用户是否有密码的函数,如果不存在的话重定向到填写密码表单,然后继续执行断开流程。注意断开连接需要确保在POST方法下进行,一个简单的方法来确保这一点,是使您的表单POST到/
disconnect /并在管道功能中设置需要密码。
覆盖断开管道的设置如下:
SOCIAL_AUTH_DISCONNECT_PIPELINE = ( # 验证社交关联是否可以断开(确保用户登录不会被断开 破坏?(compromised)) 'social_core.pipeline.disconnect.allowed_to_disconnect', # 收集需要断开的关联 'social_core.pipeline.disconnect.get_entries', # 抽取acces_token 'social_core.pipeline.disconnect.revoke_tokens', # 移除关联 'social_core.pipeline.disconnect.disconnect', )同样也可以设置针对性管道如:
SOCIAL_AUTH_TIWTTER_DISCONNECT_PIPELINE
.
Partial Pipeline(部分?管道)
可以暂停管道返回到用户需要执行的动作,然后再继续执行。
为了实现这一装饰器功能,需要用 @partial装饰器断开处理流程,partial装饰器文件位置位于social/pipeline/partial.py
当需要返回到处理流程时,只需要把用户重定向到/complete/<backend>/或者/diconnect/<backend>/视图中,这样管道就会继续执行同样的功能,但是也可以中断处理流程。
partial数据根据UUID token来区分,token可以存储在session或者追加在url中以partial_token参数命名(默认值),库会从request中获取这些值,然后加载需要的值来让用户继续执行流程。
pipeline功能函数会得到一个current_partial的实例,包含partial token和数据库中需要的数据。
为了使后端可以重定向到任何社交视图,只需要:
backend = current_partial.backend通过下列语句可以覆盖默认的参数名称:
SOCIAL_AUTH_PARTIAL_PIPELINE_TOKEN_NAME = '...示例:
https://github.com/python-social-auth/social-examples
Extending the Pipeline(扩展管道)
扩展一个管道需要:
1.编写功能函数
2.把功能函数放到可引用的地方
3.用新管道覆盖默认的管道
strategy:当前strategy实例
backend:当前backend实例
uid:社交服务商提供的uid,这个uid是当前用户在社交服务商处的唯一标识。
response:{}或者object()
服务器user-details response,取决于使用的协议(),通常是一个包含用户个人信息详情的dict,这已经包含了许多的用户详情,有时scope会增加信息的总量
details = {}
- 后端产生的用户的基本信息,用来去创建或者更新user model(字典中包含username,email.first_name,last_name 和fullname等值)
user = None
- user实例,如果没有创建用户或者在数据库中没有找到则是None
social = None
- 给出用户已关联的社交认证,如果用户没有创建或者数据库中不存在则返回None
通常自定义管道功能函数时,只需要从response中获得一些参数,但是你可以通过调用其他api去获取更多用户信息,然后存储到其他地方。
下面是一个创建用户Profile实例的一个例子,profile实例存储的是一些社交服务商返回的信息,(如Facebook,Facebook的response通常如下)
{ 'username': 'foobar', 'access_token': 'CAAD...', 'first_name': 'Foo', 'last_name': 'Bar', 'verified': True, 'name': 'Foo Bar', 'locale': 'en_US', 'gender': 'male', 'expires': '5183999', 'email': 'foo@bar.com', 'updated_time': '2014-01-14T15:58:35+0000', 'link': 'https://www.facebook.com/foobar', 'timezone': -3, 'id': '100000126636010', }假如我们想存储用户的个人简介链接,性别和时区到数据库,则功能管道可以如下:
def save_profile(backend, user, response, *args, **kwargs): if backend.name == 'facebook': profile = user.get_profile() if profile is None: profile = Profile(user_id=user.id) profile.gender = response.get('gender') profile.link = response.get('link') profile.timezone = response.get('timezone') profile.save()
现在我们只需要让soical-auth使用我们的管道即可,因为这个管道需要user实例,我们需要把他放到social_core.pipeline.user.create_user
之后,可以确保有一个用户。
SOCIAL_AUTH_PIPELINE = ( 'social_core.pipeline.social_auth.social_details', 'social_core.pipeline.social_auth.social_uid', 'social_core.pipeline.social_auth.auth_allowed', 'social_core.pipeline.social_auth.social_user', 'social_core.pipeline.user.get_username', 'social_core.pipeline.user.create_user', 'path.to.save_profile', # <--- set the path to the function 'social_core.pipeline.social_auth.associate_user', 'social_core.pipeline.social_auth.load_extra_data', 'social_core.pipeline.user.user_details', )
目前我们创建的管道功能函数返回时None,即被看做返回的是{},如果你想要profile能在下个管道中使用,那你只需要返回{'profile':profile}
.
六、异常处理
在认证过程中会产生许多异常,需要处理,此时可以利用django Middleware来解决。
django social auth提供了一个基础中间件,通过Django消息框架向用户提供消息,然后通过重定向到一个中间件方法中定义的URL来处理SocialAuthBaseException。
中间件在social_django.middleware.SocialAuthExceptionMiddleware。 其中的任何方法都可以被覆盖,但为了简单起见,建议使用这两种方法:
get_message(request, exception)
get_redirect_uri(request, exception)
默认情况下,消息是异常消息,重定向的URL是由LOGIN_ERROR_URL设置指定的位置。
如果'strategy()'装饰器检测到有效的后端,则它将在request.strategy.backend中可用,并且process_exception()将使用它来构建后端相关的重定向URL,但如果未定义,则将其回退到默认值。
如果下列设置中,任意一项被定义为True,那么异常处理都是不能够使用:
<backend name>_SOCIAL_AUTH_RAISE_EXCEPTIONS = True
SOCIAL_AUTH_RAISE_EXCEPTIONS = True
RAISE_EXCEPTIONS = True
DEBUG = True
重定向的目的会得到两个参数:
message = ''
- 来自触发异常处的消息,在某些情况下,它是在验证过程中由社交运营商返回的消息
- Message from the exception raised, in some cases it’s the message returnedby the provider during the auth process.
backend = ''
- 正在使用的后端(backend),前提是合法的后端
中间件将尝试使用Django内置消息应用程序来存储异常消息,并使用社会认证和后端名称进行标记。
如果应用程序未启用或发生MessageFailure错误,则应用程序将默认为上述URLformat。