• 跨域登录(一)


    跨域登录是一个比较烦人的事情,往往我们需要写文章记录下来,或者探讨,或者抛砖引玉的问题,都是令人头疼的。上次简单得写了一篇关于跨域登录的文章,只讲了大体的实现过程。但是现在碰到了更大的问题,这篇文章将会介绍这个成败,并探讨、实现新方案的可行性。

    跨域登录需要一张通行证,也可以称之为票据。就老衲现在知道和实验的方式一共有三种:

    1、浏览器get参数;
    2、session
    3、cookie

    每个都有特定的条件,以及需要处理的细节,也会带来一些新问题。根据经验,浏览器带参数,将会使系统开发得不像个东西,至少我是这么认为的。需要考虑对这个地址参数处理的各种策略,一开始我就否定了这个方案。在我需要改造的项目中有4个独立域名,跳转来跳转去,将会给用户造成极为不爽的体验。

    session也可以解决问题,但是有一个问题无法解决。

    先看看怎么用session解决问题。假设现在有a.com,b.com,现在开两个子域名: passport.a.com和passport.b.com。然后把这两个域名指向同一个站点,也就是在同一个站点的http投绑定这两个域名。

    那么登录的时候,在 passport.a.com上登录成功,就可以设置一个session,那么在两个系统当中都是可以通过代理文件,访问到这个session的,这个方案确实是可行的。但是session只能保持20分钟,新问题就出来了。假设这个用户20分钟没有去操作,而打开了另外一个域名,那么这个判断就失效了。访问本域是没问题的,cookie还在那里摆着。

    我比较倾向于用cookie来解决问题。上一次设计的系统,可以说极其简单。4个系统,有3个是asp.net的,还有个论坛是asp的(不用说就是动网的了)。现在就有四套登录系统。如果整体上全部改造,老衲认为成本太大了。后来四处逛网站,借鉴了Sohu的登录方式,但是只做了个体验的实现,如果全部实现了就不会现在在这里探讨这个问题了。解决方案就是javascript + iframe实现的。

    本来想用纯javascript实现,然后给src的文件带参数,但是实际开发过程中,应该是我的js水平太菜,所以感觉不到想要的那种效果。后来就采用了javascript + iframe的方式来实现。是无刷新的那种哦,呵呵。

    // JavaScript Document
    //
    <script type="text/javascript" language="javascript">
    function $P(id)
    {
    return document.getElementById(id);
    }
    function HJ_Passport(domain)
    {
    /*private*/
    var me = this;
    var version = "1.1";
    var author = "yurow";
    var iframe = "<iframe id=\"passport_frm_1\" style=\"display:none\"></iframe>";
    var uicode = "<div id=\"passport_login\"><p>请输入帐号和密码</p><span id=\"passport_login_user\">用户:<input id=\"passport_username\" type=\"text\"></span><span id=\"passport_login_pass\">密码:<input id=\"passport_password\" type=\"password\"></span><div id=\"passport_login_action\"><button onclick=\"hj_passport.OnSign();\">登陆</button><button onclick=\"hj_passport.UICode.Hide();\">取消</button></div></div>";
    var _id;
    var _g;
    var intervalId = 0;
    var sites = [["site1","www.a.com","SetLogin.aspx","SetLogout.aspx"],["site2","www.b.com","SetLogin.asp","SetLogout.asp"]];

    /*public*/
    this.passport = "http://passport.c.com/";
    this.passport_ui_id = "passport_ui";
    this.UserName = "";

    /*private set or get*/
    HJ_Passport.prototype
    =
    {
    Author:author,
    Version:version
    }
    this.UICode={
    UICode:uicode
    }
    this.UICode.Set = function(code){
    uicode
    = code;
    }
    this.UICode.Draw = function(){
    document.write(iframe
    + "<div id=\"" + me.passport_ui_id + "\"></div>");
    }
    this.UICode.Show = function(){
    var pobj = $P(me.passport_ui_id);
    if(pobj== null){
    me.UICode.Draw();
    me.UICode.Show();
    }
    else{
    pobj.innerHTML
    = uicode;
    pobj.style.display
    = "";
    }
    }
    this.UICode.Hide = function()
    {
    var pobj = $P(me.passport_ui_id);
    if(pobj!= null){
    pobj.style.display
    = "none";
    }
    };
    this.State =
    {
    ID :
    function(){ return _id; },
    G :
    function(){ return _g; }
    }
    this.State.Set = function(id,g){
    _id
    = id;
    _g
    = g;
    }
    var Checked = function()
    {
    // alert($P("passport_frm_1").readyStatus);
    //
    if($P("passport_frm_1").readyState == "complete")
    //
    {
    //
    alert("登陆成功!");
    //
    clearInterval(intervalId);
    //
    }
    }
    var ObServerLocation = function()
    {
    var hash = window.location.hash;
    if ((hash.length >= 1) && (hash.charAt(0) == '#')){
    hash
    = hash.substring(1);
    if(hash.indexOf("e_usr") > -1 || hash.indexOf("e_pss") > -1)
    {
    var err = "";
    if(hash.indexOf("e_usr")> -1)
    err
    += "用户名不能为空!";
    if(hash.indexOf("e_pss")> -1)
    err
    += "密码不能为空!";
    clearInterval(intervalId);
    alert(err);
    }
    else{
    if(hash.length < 5 || hash == '0|'){
    clearInterval(intervalId);
    alert(
    "用户名或者密码错误,登陆失败!");
    }
    else
    {
    var sp = hash.split('|');
    if(sp.length == 3 && sp[0].length == 36 && !isNaN(sp[1]))
    {
    clearInterval(intervalId);
    // if(encodeURI(me.UserName).toLowerCase() != sp[2].toLowerCase()){
    //
    me.UserName = "";
    //
    alert("登陆失败!");
    //
    }
    //
    else{
    me.State.Set(sp[1],sp[0]);
    //alert("登陆成功!")
    var url = me.passport + "setlogin.aspx?g=" + me.State.G() + "&id=" + me.State.ID();
    $P(
    "passport_frm_1").src = url;
    //intervalId = setInterval(Checked,5000);
    //fn.call();
    var s = new String(location.href);
    location.href
    = s.substring(0,s.indexOf("#")) + "#";
    try{
    if(OnSignEnd)OnSignEnd();
    }
    catch(e){}
    // }
    }
    }
    }
    //clearInterval(intervalId);
    }
    }
    this.OnSign = function(){
    var username = $P("passport_username").value;
    var password = $P("passport_password").value;
    me.UserName
    = username;
    var url = me.passport + "jslogin.aspx?username=" + escape(username) + "&password=" + escape(password) + "&r=" + Math.random();
    $P(
    "passport_frm_1").src = url;

    intervalId
    = setInterval(ObServerLocation, 500);
    }
    var ObServerLogout = function()
    {
    var hash = window.location.hash;
    if ((hash.length >= 1) && (hash.charAt(0) == '#')){
    hash
    = hash.substring(1);
    if(hash == "0|")
    {
    clearInterval(intervalId);
    var url = me.passport + "setlogout.aspx?r=" + Math.random();
    $P(
    "passport_frm_1").src = url;
    try{
    if(OnSignOutEnd)OnSignOutEnd();
    }
    catch(e){}
    }
    }
    }
    this.OnSignOut = function(){
    var url = me.passport + "jslogout.aspx?r=" + Math.random();
    $P(
    "passport_frm_1").src = url;

    intervalId
    = setInterval(ObServerLogout,500);
    };
    }
    //</script>
    
    
    javascript使用很简单,点击登录也就是调用了OnSign方法,将会向passport.c.com/jslogin.aspx发出请求。
    Response.AddHeader("P3P", @"CP=""CURa ADMa DEVa PSAo PSDo OUR BUS UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR""");
    Response.AddHeader(
    "Content-Encoding:", "utf-8");

    string url
    = Request.ServerVariables["HTTP_REFERER"];
    if (!string.IsNullOrEmpty(url) && url.IndexOf('#') > 0)
    {
    url
    = url.Substring(0, url.IndexOf('#'));
    }
    string username
    = Request["username"];
    string password
    = Request["password"];
    Response.Write(
    "<script>var f=parent;/*alert(f.document);*/f.location.href = '" + url + "' + '#' + ");
    bool error
    = false;
    if (string.IsNullOrEmpty(username))
    {
    Response.Write(
    "'e_usr");
    error
    = true;
    }
    if (string.IsNullOrEmpty(password))
    {
    if (error)
    Response.Write(
    "|");
    else
    Response.Write(
    "'");
    Response.Write(
    "e_pss");
    error
    = true;
    }
    if (error)
    {
    Response.Write(
    "';</script>");
    Response.End();
    }
    password
    = HttpUtility.UrlDecode(password, System.Text.Encoding.UTF8);
    password
    = EncryptHelper.MD5(password);
    password
    = password.Substring(8, 16);
    BBSUser bu
    = BBSUserHelper.Current(username, password);
    if (bu.UserID != 0)
    {
    DateTime dt
    = DateTime.Now;
    string save
    = RequestHelper.Get("save");
    //dt = string.IsNullOrEmpty(save) ? dt.AddHours(2) : dt.AddMonths(1);
    dt = dt.AddMonths(1);
    Guid g
    = Guid.NewGuid();
    StatUserHelper.Delete(bu.UserID);
    StatUser su
    = new StatUser();
    su.UserID
    = bu.UserID;
    su.UserName
    = bu.UserName;
    su.ExpireTime
    = dt;
    su.CreateTime
    = DateTime.Now;
    su.Guid
    = g;
    su.Password
    = bu.Password;

    su.ID
    = StatUserHelper.Add(su);
    string cachedate
    = su.Guid.ToString() + "|" + su.ID;
    string cacheuser
    = bu.UserID + "|" + bu.UserName;
    FormsAuthenticationTicket ticket
    = new FormsAuthenticationTicket(1, cachedate, DateTime.Now, dt, true, cacheuser);
    string authTicket
    = FormsAuthentication.Encrypt(ticket);
    HttpCookie UserCookie
    = new HttpCookie(FormsAuthentication.FormsCookieName, authTicket);
    UserCookie.Domain
    = ".c.com";
    UserCookie.Expires
    = ticket.Expiration;
    if (Response.Cookies[FormsAuthentication.FormsCookieName] == null)
    Response.Cookies.Add(UserCookie);
    else
    Response.Cookies.Set(UserCookie);
    Response.Write(
    "'" + cachedate + "|" + HttpUtility.UrlEncode(bu.UserName, Encoding.UTF8) + "';</script>");
    Response.End();
    }
    Response.Write(
    "'0|';</script>");
    Response.End();
    
    
    jslogin的代码就是个验证的过程,加的P3P头,是可以跨域写入Cookie的保证。这里使用的是.Net 的Forms验证,要保持和其它域名加密方式以及名称的统一。假如有两个域同时指向一个站点的话。
    这里返回javascript并且操作iframe的父窗口,改变地址,而引用的js会监视地址栏,发现数据,根据数据的格式,判断是否验证成功,如果成功了,那么会向各个站点下的一个SetLogin文件发出请求,当然被请求的页面需有P3P头。
    这样在一个地方登录,实际上是同时向其它域名写入Cookie,退出的原理也是一样的。
    但是,在Maxthon中Iframe操作父窗口地址这个操作是不允许的,它认为这个不安全,我倒是没觉得。这个问题还不太大,毕竟有Maxthon的用户不是太多,即使用了,告诉他不能用,他也会用IE。
    但是IE8 beat 2这种操作方式将会弹出新窗口。在IE8  beat2中使用Iframe解决方案就会变得体验很不好。而且还给老衲带来了心灵上的伤害,以后不敢什么都写在客户端了,浏览器版本一变,对整体影响太大了。
    下一个可替代方案就是使用反向代理,sohu的无刷新登录就是基于这个实现的据说,目前还在研究中。上一次因为时间急迫,没有时间仔细实验。这次是没办法躲过去了。
  • 相关阅读:
    spring MVC 整合mongodb
    Spring Data MongoDB example with Spring MVC 3.2
    SpringMVC整合Mongodb开发 架构搭建
    spring MVC、mybatis配置读写分离
    淘宝网架构分享总结[转]
    淘宝分布式数据层:TDDL[转]
    基于XMPP实现的Openfire的配置安装+Android客户端的实现[转]
    基于开源 Openfire 聊天服务器
    bootstrapUI
    京东手机webapp商城
  • 原文地址:https://www.cnblogs.com/birdshover/p/1326708.html
Copyright © 2020-2023  润新知