SSO的基本原理与Java代码实现
推荐阅读:单点登录原理与简单实现
基本原理
Http无状态协议。浏览器使用http协议对服务器发出的每一次请求,服务器都会独立处理,不与之前或之后的请求产生关联,即无状态。所以为了保护服务器的某些资源,必须限制浏览器请求,鉴定请求的合法性。既然http无状态,就让服务器和浏览器共同维护一个状态,即会话机制。
会话。浏览器第一次请求服务器,服务器创建一个会话,并将会话的id作为响应的一部分发送给浏览器。浏览器存储该id,并在第二次第三次请求时带上该id,服务器取得请求中的会话id就知道是不是同一个用户了。
会话机制。服务器在内存中保存session对象。浏览器在cookie中保存sessionId,在Tomcat中sessionId用的是
JSESSIONID
,流程如下:浏览器第一次输入帐密,服务器拿到帐密去数据库比对,比对正确说明是合法用户,将该会话标记为“已授权”或“已登录”的状态,该会话状态被服务器保存在会话对象中,当用户再次访问时,服务器在会话对象中查看登录状态,判断是否合法,合法后才允许访问。
单系统登录解决方案的核心是cookie,cookie携带会话id在浏览器和服务器之间维护会话状态,但cookie受到域的限制(通常对应网站的域名)。浏览器在发送http请求时会自动携带与该域匹配的cookie,而不是所有cookie。。需要注意的是,曾经流行过的顶级域名的方式虽然可行,但面临着应用群域名不统一,技术不同,共享cookie无法跨语言平台登录,cookie本身不安全等诸多问题。
单点登录(SSO)。举例:淘宝网上登录后,打开天猫、闲鱼、飞猪等网站会自动登录,无需重复进行身份验证,好处是方便管理、保障安全、节省登录时间。
认证中心。相比单系统登录,sso需要一个独立的认证中心,只有认证中心能接受帐密等安全信息,其他系统不提供登录入口。
间接授权ticket。sso认证中心验证用户的帐密没有问题,创建授权令牌ticket,在接下来的跳转中,授权令牌作为参数发送给各个子系统,子系统拿到令牌,即得到授权,可以借此创建局部session,局部session的登录方式与单系统登录方式相同。
- 用户访问系统1的受保护资源,系统1发现用户未登录,跳转至sso认证中心,并将自己的地址作为参数
- sso认证中心发现用户未登录,将用户引导至登录页面
用户输入用户名密码提交登录申请
3. sso认证中心校验用户信息,创建用户与sso认证中心之间的会话,称为全局会话,同时创建授权令牌
4. sso认证中心带着令牌跳转回最初的请求地址(系统1)
5. 系统1拿到令牌,去sso认证中心校验令牌是否有效
6. sso认证中心校验令牌,返回有效,注册系统1
7. 系统1使用该令牌创建与用户的会话,称为局部会话,返回受保护资源
8. 用户访问系统2的受保护资源
9. 系统2发现用户未登录,跳转至sso认证中心,并将自己的地址作为参数
10. sso认证中心发现用户已登录,跳转回系统2的地址,并附上令牌
11. 系统2拿到令牌,去sso认证中心校验令牌是否有效
12. sso认证中心校验令牌,返回有效,注册系统2
13. 系统2使用该令牌创建与用户的局部会话,返回受保护资源
用户登录成功之后,会与SSO认证中心及各个子系统建立会话,用户与SSO认证中心建立全局会话,用户与子系统建立局部会话。局部会话存在,全局会话一定存在;但全局会话存在,局部会话不一定存在
单点注销。在一个子系统中注销,所有子系统的会话都将被注销。sso认证中心一直监听全局会话的状态,一旦全局会话销毁,监听器将通知所有注册系统执行注销操作。如下图:
- 用户向系统1发起注销请求
- 系统1根据用户与系统1建立的会话id拿到令牌,向sso认证中心发起注销请求
- sso认证中心校验令牌有效,销毁全局会话,同时取出所有用此令牌注册的系统地址
- sso认证中心向所有注册系统发起注销请求
- 各注册系统接收sso认证中心的注销请求,销毁局部会话
- sso认证中心引导用户至登录页面
代码实现
课程网址:慕课网——Java实现单点登录
文件结构(仅列出关键部分):
1 | 1.SSO_Server |
- 采用一个认证服务器,两个应用服务器来模拟SSO;
- 使用idea作IDE,并使用Tomcat的local模拟,SSO_server使用
localhost:8080
,WebApp1使用localhost:8081
,WebApp1使用localhost:8082
LoginServlet.java
1 | package com.imooc.sso.servlet; |
- 对login和ssoLogin两种不同路径采用不同的处理方式。若login路径,则取出帐密和source(url中从头到端口号的位置);
- 如果帐密验证通过,获取随机ticket,与init时获取的domains(替换掉source部分)一起放入url中重定向到main页面;
- 如果帐密验证失败,则将source加入属性,并转发到login页面;
- 如果是ssoLogin路径,则转发到login页面。
- 如果时ssoLogout路径,则携带source和domains转发到各WebApp的logout路径
SSO Server的web.xml
1 |
|
- 建立ssoLogin和login的servlet,并设置初始化参数domains;
- domains中包含所有WebApp的地址,如代码块中的
http://127.0.0.1:8081
和http://127.0.0.1:8082
UserFilter.java
1 | package com.imooc.sso.filter; |
- UserFilter用来过滤登录操作和注销操作;
- 当cookie中有该用户且ticket不为空时,为请求放行;(经过app1,ssoServer,login成功之后,访问app2的情形)
- 当cookie没有该用户且ticket不为空时,说明登录成功,从请求的url中获取该ticket参数,放入cookie中,为请求放行;(经过app1,ssoServer,login登录success的进行时)
- 当cookie没有该用户,且ticket为空时,说明登录不成功,在url中添加上server和app参数,重定向到ssoLogin中;(未经过ssoServer认证,直接路径链接到功能页面的非法情形)
- 仅当ssoLogout路径进来时请求会被转发到ssoServer最终抵达本app的MainServlet.java中完成剩下的注销步骤,其余情况会被拦截
MainServlet.java
1 | package com.imooc.sso.servlet; |
- 先看service方法,如果是main路径,说明是经过LoginServlet.java的登录成功操作后转发过来的,需要先将domains中的各个app的cookie中写入ticket;如果是setCookie路径,则只能是上一步写入ticket的后续操作;(例:app1登录成功进入service方法的main路径,将其他app的server进入syncCookie方法,其他app会进入setCookie路径)
- 如果是logout路径,将cookie置空,将其他WebApp调用syncCookie方法将对应cookie依次置空;
- 再看setCookie方法,使用线程池的方法,使用HttpClient进行申请异步Http服务;
WebApp1的Web.xml
1 |
|
- 配置UserFilter和MainServlet;
- 为UserFilter设置初始化参数server和app