关于本站
1、基于Django+Bootstrap开发
2、主要发表本人的技术原创博客
3、本站于 2015-12-01 开始建站
上次开发完成Github第三方登录之后,接下来需要整理一下3个第三方账号登录的代码。
但发现现有的登录和注册的代码影响到OAuth第三方登录代码整理,需要对登录和注册的代码进行优化。
之前写的登录、注册为了图方便就利用了Bootstrap的模态框。在最底层的模版页面加了两个form,以及对这两个form的ajax提交写了不少的js代码。具体可以参考前面写的登入登出相关博文。
这样处理看上去是方便,每次登录之后直接刷新页面即可,不用重新跳转定位到未登录之前的页面。
而且页面也好看:
这么处理也造成两个问题:
1)由于这些代码都是写在最底层的模版页中,导致每个页面的代码量比较多;
2)由于写在最底层的模版页中,也导致没办法给每个请求返回最新的crsf码,导致登录和注册没有crsf保护。
综上所述,所以需要对登录/注册该部分的功能进行优化。
初步优化方案:登录和注册使用同一个form表单,减少代码冗余。另外所有的登录和注册行为都跳转到这个form表单页面中,不再写在最底层的模版中,减少每个页面的html代码。这个方案可以充分优化该功能。
重点还需要解决一个问题:登录或注册之后,重新跳转回点击登录或注册之前的页面。
统计了一下,一共有4个地方需要跳转处理:
1)使用我的网站账号登录之后的跳转;
2)在我的网站直接注册之后的跳转;
3)使用第三方账号登录之后的跳转;
4)初次使用第三方账号登录,绑定用户之后的跳转。
这4种情况可以归纳为两种类型:我的网站账号和第三方账号登录。
一开始请求打开登录或注册的页面时,可以利用 referrer = request.META.get('HTTP_REFERER','/') 得到跳转之前的页面地址。
再把这个地址当作OAuth的state参数值,这样就可以保留这个地址。登录或注册完成之后,再跳转到这个地址即可。
最后,我的登录和注册的页面做成这样的效果,如下图:
登录和注册使用到的邮箱和密码框都使用同一个表单。去掉样式,核心的html代码如下:
<form id="user_form" class="main_form" method='post'> {%csrf_token%} <input id="reback_url" type="hidden" value="{{reback_url}}"> <span>邮箱:</span><input type="text" id="user_name" name="user_name"> <span>密码:</span><input type="password" id="user_pwd" name="user_pwd"> <span class="other_account">第三方账号登录:</span> <a href="/oauth/qq_login?state={{reback_url}}" title="QQ登录"> <img src="/static/img/logo_qq.png" alt="QQ登录"> </a> <a href="/oauth/sina_login?state={{reback_url}}" title="微博登录"> <img src="/static/img/logo_sina.png" alt="微博登录"> </a> <a href="/oauth/github_login?state={{reback_url}}" title="Github登录"> <img src="/static/img/logo_github.png" alt="Github登录"> </a> <p id="tip_text" style="color:red;"></p> <input type="submit" class="btn btn-primary" value="登录"/> <a class="btn btn-default" href="{% url 'password_lost' %}">忘记密码</a> <input id="user_reg" type="button" class="btn btn-primary" value="注册"/> </form>
其中,{{reback_url}}是跳转进来之前页面的网址。把这个记录在一个hidden字段中。另外第三方登录的按钮都加入了state参数,用于跳转。其他的url都是我之前写的登录和注册的ajax代码。具体可以参考我之前写的用户认证博文。
这个前端页面再写ajax的代码即可,这里登录或注册成功之后,再跳转到原来的页面:
<script type="text/javascript"> $(function(){ //登录 $('#user_form').submit(function(){ //验证 var tip=$('#tip_text'); tip.text(''); if($('#user_name').val()==''){ tip.text('请输入邮箱'); return false; }; if($('#user_pwd').val()==''){ tip.text('请输入密码'); return false; }; //登录 $.ajax({ type: "POST", data: $('#user_form').serialize(), url: "{% url 'user_login' %}", cache: false, dataType: "json", success: function(json, textStatus){ var is_success = json['success']; if(is_success){ tip.text('登录成功,页面处理中...'); //跳转回原来的页面 var reback_url = $('#reback_url').val(); if(!reback_url){reback_url='/';} window.location.href = reback_url; }else{ tip.text('邮箱或者密码错误,请重试'); }; }, error: function (XMLHttpRequest, textStatus, errorThrown) { tip.text("登录出错,请重试 "+errorThrown); } }); return false; }); //注册 $('#user_reg').click(function(){ //验证 var tip=$('#tip_text'); tip.text(''); var reg_name=$('#user_name').val(); var reg_pwd=$('#user_pwd').val(); if(reg_name==''){ tip.text('邮箱不能为空'); return false; }; if(!reg_name.match(/^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+((\.[a-zA-Z0-9_-]{2,3}){1,2})$/)){ tip.text('请输入正确的邮箱格式'); return false; } if(reg_pwd==''){ tip.text('密码不能为空'); return false ; }; if(reg_pwd.length<6){ tip.text('密码不能少于6位'); } //注册 tip.text('注册中,请稍后...'); $.ajax({ type: "POST", data: $('#user_form').serialize(), url: "{% url 'user_reg' %}", cache: false, dataType: "json", success: function(json, textStatus){ var is_success = json['success']; if(is_success){ tip.text('注册成功,页面处理中...'); $('#user_control').text(json['message']); window.setTimeout(function(){ var reback_url = $('#reback_url').val(); if(!reback_url){reback_url='/';} window.location.href = reback_url; },3000); }else{ tip.text(json['message']); }; }, error: function (XMLHttpRequest, textStatus, errorThrown) { tip.text("注册出错,请重试 "+errorThrown); } }); return false; }); }); </script>
另外把最底层模版页中的登录和注册按钮修改成a标签并指向这个页面。前端页面这样就可以了。
打开后台的代码,找到登录和注册对应处理代码的位置。
首先是打开这个登录页面的处理方法,通过META的HTTP_REFERER得到网址,记得添加这个页面的路由设置。
from django.shortcuts import render_to_response from django.template import RequestContext def login_page(request): """打开登录页面""" data = {} #得到跳转进来的页面 referrer = request.META.get('HTTP_REFERER','/') data['reback_url'] = referrer return render_to_response("user/login.html", data, context_instance=RequestContext(request))
另外原本的登录和注册的处理方法,不用修改,主要注意表单的参数正确即可。
我的网站账号处理完成之后,还需要第三方账号登录之后的跳转。
之前的第三方登录代码可以参考前面的博文(内容比较多,这里就不说了。内容太多,也不方便说。)
以QQ登录为例,只需要修改参数中的state即可。
def qq_login(request): state = request.GET.get('state') #获取state参数 config = settings.OAUTH_QQ_CONFIG if state: config['state'] = state #修改state参数 oauth_qq = OAuth_QQ(config) #获取 得到Authorization Code的地址 url = oauth_qq.get_auth_url() return HttpResponseRedirect(url)
这样,QQ服务器认证通过之后,就会把state原封不动的返回回来。登录完成之后,再跳转到这个state记录的网址即可。同样,中途若需要打开绑定的用户的页面时,只需要把这个state参数传递过去即可。
完整的代码,等我优化整理OAuth第三方登录之后,再一并挂出来。其实比较简单,再页面跳转的过程中不要丢掉这个参数即可。
杨仕航
优化之后,整体的页面加载速度变快了一些
2016-09-20 14:28 回复