JOSSO原理探索和使用

互联网app多了,老要登录注册是个大麻烦,各大SNS都提供自己的profile来支持第三方的身份认证,免去注册和登录,是一个不错的方案。一个品牌旗下的多个网站,也可以一个账号多处登录,典型的是taobao.com和tmall.com,跨域登录大约是通过专门的session管理服务实现。前一阵子flacro开始研究JOSSO,Java Open Single Sign-On,一个开源的跨域登录解决方案。后来flacro抱怨文档太少,用起来摸不着头脑,这两天和他一起研究了一下,大约能够跑通了。

JOSSO的跨域原理

跨域登录就是在taobao.com登录了,在tmall.com也登录了,首先浏览器的cookie就不能跨域访问,只能依赖session,所以需要独立的session管理服务器。JOSSO通过一个单独的authentication服务器(简称josso服务器),和一个和app放在一起的agent服务来实现session的共享管理。

JOSSO把资源(就是html等)分为两类,一类是不能随意访问的,需要登录,是protected资源,另一类是可以随便访问的。而其原理则是用agent的来判断,请求的资源是否是protected,如果是,就转向authentication服务器让用户登录,不是就正常返回。所以,JOSSO只解决认证即authentication的问题,而不能解决授权即authorization的问题(例如我不能访问你的数据),这部分应用自己去处理。


跑起来JOSSO的示例程序研究之后,发现过程如图所示。

  1. 用户用浏览器访问app.com/user/1/content/,这是一个protected的资源,注意,这是在app.com这个域
  2. Agent拒绝直接访问,转发给JOSSO Authentication服务器,这是在auth.com这个域
  3. JOSSO服务器返回一个登录页面,auth.com/josso/login.html
  4. 这时用户填写登录表单,然后提交POST,注意,提交给auth.com
  5. auth.com收到POST数据,验证用户有效性,无论真假,都会将请求转发给app.com的agent,并把结果附在转发的请求中
  6. agent接受到转发的请求后,如果用户已经登录了,服务器上就会建立session信息,同时agent把原始的请求(/user/1/content/)发给真正的应用服务
  7. 应用服务返回相应的页面给浏览器,此时,用户状态已经登录。

实际情境的使用

一般的应用情境其实用上面的方法已经能够cover了,即登录后的页面认为是受保护的,登录前是公共的就可以了。问题在于,用户就是单纯的登录,然后建立session怎么办?这种情况在ajax遍地跑的今天很常见,常常用户在一个页面上已经回答了一个问题,或者修改了某个内容,要提交时,再提示用户登录。这个时候,即页面本身就不是protected,上面的情境就不太适用了。特别是,现在登录往往都做在右上角,搭配吊顶导航条,点login按钮出一个弹窗,登录完了页面不变(youku就是类似的),这连跳转都没有哦亲。

解决方案

方案1:(失败)

既然在4步要提交表单,那就不要前面那些步骤,直接在app.com的页面写表单,然后提交给auth.com,得到结果后页面更新(不是刷新),即异步提交跨域表单。这样做首先的问题是,这是跨域ajax啊!一跨域就悲剧啊!跨域ajax有一些解决方案,例如用一个代理服务器,额,太笨重;还有JSONP,动态向app.com的页面上写auth.com的js,然后得到数据,但是,只有GET。唯一能够把表单提交给auth.com的方法是,表单的action,这玩意是可以跨域提交的。可是这是同步的,提交完了,页面就刷新吧……

从第4步开始还有一个问题,那就是,本来是要访问一个protected资源的,现在没访问了,然后JOSSON的行为就变的莫名其妙了!它仍然继续走到第7步,但是链接进行了奇怪的拼接,然后直接404。我觉得应该是可以配置的,但是完全找不到地方(糟糕的文档啊)。这个方案就先放下了。

方案2:(成功)

然后就想到了iframe。

在app.com的页面里嵌入一个专门负责登录的iframe页面,这个页面可以请求一个protected的资源,即仍然从1开始走,不过外围的页面仍然是公共可以直接访问的。这样可以在iframe里面走一通,让用户登录,修改auth.com提供的表单的样式就好了。接下来的问题是,登录之后外围的页面怎知道登录了呢?这里用了两个trick

  • 那个protected资源,我们可以设置成一个特殊的资源,例如一个login.jsp,因为已经登录了,这个jsp可以拿到用户信息,例如id, username等等,这时它不返回新的页面,而是返回json数据
  • Iframe接受到了返回的数据,拼接到了内部,这时外部页面通过绑定iframe的onload事件,因为iframe得到数据后进行了一次刷新,所以onload得以调用,这时,进入iframe内部拿数据,更新页面,然后把iframe删掉。这里还有一个安全策略的问题,iframe是不能拿到跨域的网站的内容的,但是此时因为一系列跳转,返回给浏览器的json仍然在app.com的域下,所以可以拿到。好惊险= =

总算有了一个大致可用的解决方案,还没有具体实现,估计实现起来还会有新的苦难,吼吼。欢迎有用过JOSSO的讨论下。

4 thoughts on “JOSSO原理探索和使用

      1. tian

        是集成josso后才会出现中文乱码问题,如果去掉tomcat server.xml文件<Host>标签内添加<Valve appName="josso" className="org.josso.tc60.agent.SSOAgentValve" debug="1"/> 后,就正常了,应该是josso处理编码的问题

        Reply
  1. tian

    在tomcat server.xml文件<Host>标签内添加<Valve appName="josso" className="org.josso.tc60.agent.SSOAgentValve" debug="1"/> 后,就会出现form表单中文乱码问题

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>