如何通过session控制单点登录中的session

他的最新文章
他的热门文章
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)基于jsessionid的单点登录经验总结
 作者: 俞超 编辑:
  【IT168 评论】目前市面上有许多SSO的产品,实现方式也不尽相同。但这些产品相对于已经投入使用的系统来说,存在诸多不适用之处:一,繁琐的配置不仅增加运维人员的学习成本,更有可能因为操作不当造成稳定运行系统的崩溃;二,目前市面上的SSO产品基本上只解决了用户认证的功能,很少将权限授予考虑进去,即便有也是需要满足其权限授予的标准,这对已经上线的系统很难适用;三,市面上SSO产品一般价格都不低,一定程度上也会增加项目的费用。  现在公司内部已经投入使用了三套系统,分别为1、2和APP3,需要一个SSO的站点不仅要实现一次登录同时使用上述三套系统,还需要在SSO站点建立三套系统的角色、分配用户角色的功能。为了满足上述需求,需要设计一套折中的单点登录方案,尽量减少对已上线系统的侵入,同时又能够保证一次登录所有系统都可以使用,以及分配用户的角色。  本文综合考虑适用性、快速集成性等功能特性,提出了一种以jsessionid为基础的集成方案,通过开放相应接口,子系统只需要实现少量集成的接口就可接入SSO中。HttpClient组件又能够帮助我们在同一个session下完成各种POST/GET请求,例如菜单的抓取、角色分配等,无须修改原系统的任务逻辑,相比于市面上的SSO产品,本方案减少了许多配置工作,对系统的侵入也基本上能够做到零侵入。  1. 基于jsessionid单点登录的实现原理  单点登录(Single Sign On),简称为 SSO,是目前比较流行的企业业务整合的解决方案之一,其实现机制不尽相同,大体可分为Cookie和Session机制两大类。本文提出的单点登录方案本质上是通过session的交互来实现的。利用WEB容器中jsessionid的特性,在不侵入原来系统的认证方案的同时,又能够保证用户一次登录使用所有系统的功能,能够快速地实现身份认证的统一管理。  单点登录功能主要包括以下几步,实现过程见图1:  同步登录  登录SSO时,需要同时向各个子系统发送登录请求,登录成功后将用户拥有的权限菜单上发到SSO进行展示。超级管理员能够新增、修改、删除角色,同时能够为用户分配角色。发送到APP端的登录请求是不经过过滤器拦截的,因此可直接进行身份认证。  URL重写  身份认证成功后,跳转到子系统的URL中需要注入jsessionid,这样才保证与登录时的session相同。  Session过期处理  当子系统session过期后,SSO端能够感知,并重新登录,其过程与同步登录相同。除了登录请求,Session过期处理实质是由一个Filter来完成对所有请求的拦截,从而判断该请求是否是新的Session创建。  Session同步  保证子系统的session变更后,SSO主系统能够感知。而SSO的session需要子系统通过不段刷新机制,保证其session保持在一定的数量。▲图1基于jsessionid单点登录的整体流程  2. 利用HttpClient组件实现系统登录  单点登录的第一步就是同步登录,在登录主系统SSO的同时能够各个子系统,这里采用HttpClient组件来模拟实现HTTP请求的发送。HttpClient 是 Apache Jakarta Common 下的子项目,用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,并且它支持 HTTP 协议最新的版本和建议。  利用HttpClient,登录主系统SSO成功后,携带用户信息发送请求到子系统的登录Action,同时返回子系统的SessionID,以子系统的系统名作为Key,SessionID作为Value进行Map映射,并保存在SSO的Session中,同时,SSO的sessionid保留在APP的Session中用于后续的刷新,其实现过程见图2。▲图2 HttpClient登录流程  为了便于操作,将HttpClient发送HTTP请求封装在了一个类的静态方法,使用的时候只需要传递APPNAME,APP_SESSIONID,URL,参数封装的MAP。public class HttpUtil {private static final String HTTP_CONTENT_CHARSET = &utf-8&;public static final int MAX_TIME_OUT = <span style="color: #00*<span style="color: #; //最大连接时间为30秒public static final int MAX_IDLE_TIME_OUT = <span style="color: #000;public static final int MAX_CONN = <span style="color: #0;public static String sendSimplePostRequest(String url,Map param,String appSessionId) {...}}  而处理所有SSO发送的HTTP请求统一由继承AbsAppService的Servlet实现,该抽象类的核心方法perform处理所有http://app_name/upfw的请求。private String perform(HttpServletRequest req, HttpServletResponse resp){...Long threadId = Thread.currentThread().getId();try {HTTP_REQ_MAP.put(Thread.currentThread().getId(), req);Object ret = method.invoke(this, params);HTTP_REQ_MAP.remove(threadId);String resultInfo=&&;if(ret instanceof AppLoginInfo){Result result=Result.genOkResult();result.setData((AppLoginInfo)ret);Type type = new TypeToken&(){}.getType();resultInfo=JsonUtil.toJson(result, type);}else{Result result=Result.genOkResult(ret);resultInfo=JsonUtil.toJson(result, Result.class);}return resultI} catch (Throwable e) {HTTP_REQ_MAP.remove(threadId);Result result =Result.genExceptionResult(e);return JsonUtil.toJson(result, Result.class);}}  3. 基于jsessionid的URL重写  当程序需要为某个客户端的请求创建一个session的时候,首先检查这个客户端的请求里是否已包含了一个session标识,称为 session id,如果已包含一个session id则说明以前已经为此客户端创建过session,就按照session id把这个session检索出来使用(如果检索不到,可能会新建一个),如果客户端请求不包含session id,则为此客户端创建一个session并且生成一个与此session相关联的session id,session id的值应该是一个既不会重复,又不容易被找到规律以仿造的字符串,这个 session id将被在本次响应中返回给客户端保存。  保存这个session id的方式可以采用cookie,这样在交互过程中浏览器可以自动的按照规则把这个标识发挥给服务器。由于cookie可以被人为的禁止,必须有其他机制以便在cookie被禁止时仍然能够把session id传递回服务器。经常被使用的一种技术叫做URL重写,就是把session id直接附加在URL路径的后面,附加方式也有两种,一种是作为URL路径的附加信息,表现形式为http://...../jsessionid=ByOK3vjFD75aPnrF7C2H。  利用这一特性,我们在登录SSO后,就不需要再进行身份认证就可以访问子系统的URL。由于子系统的session id一直都在变化,因此URL重写时添加的jsessionid需要以AJAX的方式根据子系统的KEY值动态地从SSO的Session中获取,具体实现见图3。▲图3 URL重写步骤  4 Session同步机制与Session过期处理  4.1 Session同步  session同步机制本质是就是定时刷新SSO的Session保证其不过期。为了记录SSO的session,我们定义了一个抽象类AbsAppHttpSession,主要用来方便获取SSO端的sessionid,登录的用户信息,及其他session生命周期相关的辅助信息。public abstract class AbsAppHttpSession {protected HttpSpublic AbsAppHttpSession(HttpSession session) {this.session =}/**该session是否关联了登录用户信息,关联了表示登录,未关联表示游客身份登录*/public abstract boolean isUserLogin();/**是否是upfw登录的,有可能是本地app系统入口登录的*/public abstract boolean isUpfwLogin();/**返回upfwSessionId*/public abstract String getUpfwSessionId();/**返回登录用户Id*/public abstract Long getUserId();/**返回登录用户名*/public abstract String getLoginName();/**返回登录用户姓名*/public abstract String getUserName();/**最近session访问时间,按单位秒返回*/public long getLastAccessedTime(){return this.session.getLastAccessedTime()/<span style="color: #00;}/**该session会话超时时间,按单位秒返回*/public int getSessionTimeOut(){return this.session.getMaxInactiveInterval();}/**当前session剩余存活时间,按单位秒返回*/public long getRemainLiveTime(){long currentTime = System.currentTimeMillis();long lastAccessedTime = this.session.getLastAccessedTime();return (currentTime- lastAccessedTime)/<span style="color: #00;}/**返回appSessionId*/public String getAppSessionId(){return this.session.getId();}public HttpSession getHttpSession(){return this.}}  而Session的创建与销毁都由抽象类AbsAppSessionListener来监听,该抽象类实现HttpSessionListener接口,这样就记录下为SSO端的Session列表,能够轻松地实现Session刷新,本质就是对SSO发起一个空的请求。SSO端刷新由abstract class AbsUpfwTaskServlet extends HttpServlet implements Runnable定时地去刷新,其核心方法即实现Runnable接口的方法run。@Overridepublic void run() {Timer timer = new Timer();TimerTask timerTask = new TimerTask() {@Overridepublic void run() {...}};timer.schedule(timerTask, delay,period);}  4.2 Session过期处理  由于SSO的Session一直由APP在定时刷新,因此不存在过期现象;而APP的session过期时则由SSO重新登录生成新的Session,其过程类似于同步登录的过程。该过程主要由拦截器abstract class AbsUPFWAppFilter来实现,其核心方法如下。@Overridepublic void doFilter(ServletRequest request, ServletResponse response,FilterChain chain) throws IOException, ServletException {...boolean b = httpRequest.isRequestedSessionIdFromURL();if(b){Cookie cookie =new Cookie(&JSESSIONID&, httpRequest.getSession().getId());httpResponse.addCookie(cookie);String upfwSessionId =httpRequest.getParameter(Constants.REQ_UPFW_SESSIONID_KEY);if(upfwSessionId != null && !&&.equals(upfwSessionId)){...}}b = this.hasSessionUser(httpRequest);if(b)//如果用户已经登录则放行chain.doFilter(request, response);else{//该请求是否允许游客访问b = this.canVisitorAccess(httpRequest);if(b)//允许允许游客访问则放行chain.doFilter(request, response);else{//判断cookie中是否包含upfwSessionIdb = this.hasCookieUpfwSession(httpRequest);if(!b){this.appLoginTimeOut(httpRequest, httpResponse);}else{//判断upfwSession是否过期b = this.isUpfwSessionTimeOut(this.getUpfwSessionIdFromCookie(httpRequest));if(b)//upfwSession过期,则跳到upfwSession过期处理this.upfwLoginTimeOut(httpRequest, httpResponse);else{//重新登录成功后继续放行this.reLoginUpfw(httpRequest, httpResponse);chain.doFilter(request, response);}}}}}  5. 小结  本文使用Web容器中jsessionid特性实现了一种折中的单点登录方案,即不侵入原有系统的认证方案,同时又能保证SSO登录其他APP都可以使用。这一单点登录方案,主要包括同步登录、URL重写、Session同步、Session过期处理,每一步都存在SSO的sessionid与APP的sessionid的交换,SSO的session因为有APP端的定时刷新不存在过期现象,而APP端session过期后,SSO会重新登录APP,从而建立起SSO与APP的session映射。这一方案对快速集成已经投入使用的系统身份认证具有一定的参考意义。  6. 作者简介  俞超 软件开发工程师  任职于某大型IT外资企业,主要从事J2EE开发、设计工作。  参考资料  http://www.cnblogs.com/kevinGao/archive//2671010.html  http://www.cnblogs.com/zsuxiong/archive//2241622.html  http://wentao365.iteye.com/blog/1242768  http://sunxboy.iteye.com/blog/217262
IT168企业级如何通过session控制单点登录
我的图书馆
如何通过session控制单点登录
&&&&&&&&& &web服务器为每一个浏览器实例对应一个session。这个session有自己的一个独立id,这个id保存在浏览器的cookie中(这个cookie貌似随着这个浏览器实例的关闭而清除),访问web服务器的时候,web服务会根据你cookie中的sessionId来决定重新创建一个session还是使用已经存在的session。如果使用桌面的ie图标打开一个IE窗口,这个窗口属于一个新的浏览器实例(其中不包含sessionid信息),这时候用这个IE访问web服务器的时候web服务器会为这个浏览器实例新创建一个httpsession,sessionId也是新的(sessionId保存到本地的cookie中),不会对你以前打开的窗口中的session产生覆盖,关闭ie窗口的时候cookie中的信息也就清除掉了。 如果是在打开的ie窗口中按&#8220;ctrl+n&#8221;打开一个ie窗口,这个窗口于刚才的窗口是同一个实例,与刚才的窗口共用session,所有的同一个ie实例的窗口都关掉后,会清除掉sessionId。
补充一点,可以通过人为的输入参数sessionId通知web服务器你使用的是哪个session(如果服务器存在这个session的话)。
*****************************************************************************************************************************************
上面已经提到了,需要通过控制session,对web服务中所有session进行遍历操作,那么你肯定会想到application这个大对象了(当然你也可以采用缓存或者数据库),具体实现方法是这样的,首先在application中创建一个List&HttpSession&,用来保存每一次会话(session)对象,系统在验证用户登录请求时,通过遍历该list并加以判断,最后决定是否让该用户成功登录。OK,需求和设计都在上面了,接下来就是编码工作了哈。不过在这里还需要考虑几个问题:
1、如果登录后关闭了浏览器,想立即打开一个新的浏览器并登录将会失败,因为前一次登录后的session还没销毁,你需要等其销毁后方可再次登录成功,这种问题如何解决???哈哈,你是不是已经想到要用javascript处理一下下,即在关闭浏览器的时候触发一个事件,该事件就是通知来销毁当前session的。
2、第二个问题其实是接上一个问题而讨论的,用javascript触发浏览器关闭事件并不是一个好办法,因为有很多因素可能导致这个事件触发不成功,还有Firefox和IE这两个东东意见有时候并不统一,最后有一个比较可恶的问题就是,我在同一台机器上不能打开两个浏览器窗口去登录相同的帐号,因为一个浏览器对应一个session啊!这就是为什么我在文章一开始要引用一些基本知识。
既然问题很严重,那就动脑壳去想办法啊!我在这里自言自语了半天,我不打算用中文继续讲下去了,直接code上场吧:
&action="verifyLogin.do"&method="post"&name="form"&&
&align="center"&&
&colspan="6"&align="left"&STYLE='border:0;padding:14filter:glow(color=#0080ff,strength=3);letter-spacing:2'&STYLE='font:12px/14color:#font-weight:'请输入帐号和密码:&&
&&&&&align="right"用户名:&&
&&&&&type="text"&&name="name"&style="{&width:&100height:&15}"&&&
&&&&&align="right"密&&码:&&
&&&&&type="password"&name="pwd"&style="{&width:&100height:&15}"&&
%--&&&&&name="authority"&size="1"--%&&
%--&&&&&&&value="11"管理员--%&&
%--&&&&&&&value="00"普通用户--%&&
%--&&&&--%&&
&&&&&&type="hidden"&name="authority"&value="11"&&&
&align="center"&&
&colspan="6"&align="center"&&
&name=""&type="button"&value="提&&&&交"&onMouseOver="this.style.backgroundColor='red';"&onMouseOut="this.style.backgroundColor='';"&class="button"&onClick="check()"&&&& &&
&name=""&type="reset"&value="重&&&&置"&onMouseOver="this.style.backgroundColor='red';"&onMouseOut="this.style.backgroundColor='';"&class="button"&&
&form action="verifyLogin.do" method="post" name="form"&
&tr align="center"&
&td colspan="6" align="left"&&fieldset STYLE='border:0;padding:14filter:glow(color=#0080ff,strength=3);letter-spacing:2'&&font STYLE='font:12px/14color:#font-weight:'&请输入帐号和密码:&/font&&/fieldset&
&td align="right"&用户名:&/td&
&td&&input type="text"
name="name" style="{ width: 100height: 15}" &&/td&
&td align="right"&密&&码:&/td&
&td&&input type="password" name="pwd" style="{ width: 100height: 15}"&&/td&
&select name="authority" size="1"&--%&
&option value="11"&管理员&/option&--%&
&option value="00"&普通用户&/option&--%&
&/select&--%&
&input type="hidden" name="authority" value="11" &
&tr align="center"&
&td colspan="6" align="center"&
&input name="" type="button" value="提&&&&交" onMouseOver="this.style.backgroundColor='red';" onMouseOut="this.style.backgroundColor='';" class="button" onClick="check()"&&&&&
&input name="" type="reset" value="重&&&&置" onMouseOver="this.style.backgroundColor='red';" onMouseOut="this.style.backgroundColor='';" class="button"&
VerifyLogin类处理请求:
String&name=request.getParameter("name"); &&
&String&pwd=request.getParameter("pwd"); &&
&String&ip&=&request.getRemoteHost(); &&
&User&user=UserHelp.getUserByName(name); &&
&String&err=""; &&
&if(user!=null){ &&
&&&&&if&(user.getPwd().equals(pwd)){ &&
&&&&&&&&&&&&
&&&&&&&&&&boolean&flag&=&true; &&
&&&&&&&&&&List&HttpSession&&sessions&=&(List&HttpSession&)application.getAttribute("sessionlist"); &&
&&&&&&&&&&for(int&i&=&0;i&sessions.size();i++){ &&
&&&&&&&&&&&&HttpSession&ses&=&null; &&
&&&&&&&&&&&&try{ &&
&&&&&&&&&&&&&&&ses&=&sessions.get(i); &&
&&&&&&&&&&&&&&&User&usr&=&(User)ses.getAttribute("user"); &&
&&&&&&&&&&&&&&&String&cuip&=&(String)ses.getAttribute("remoteHost"); &&
&&&&&&&&&&&&&&&if(null!=usr&&usr.getName().equals(user.getName())&&!ip.equals(cuip)){&&
&&&&&&&&&&&&&&&&&&flag&=&false; &&
&&&&&&&&&&&&&&&&&&err="&center&&font&color='red'&对不起该用户已经有人登录,您不能重复登录!&/font&&/center&&br&&br&"; &&
&&&&&&&&&&&&&&&&&&out.println(err); &&
&&&&&&&&&&&&&&&&&&break; &&
&&&&&&&&&&&&&&&} &&
&&&&&&&&&&&&}catch(Exception&e){ &&
&&&&&&&&&&&&&&&sessions.remove(i); &&
&&&&&&&&&&&&} &&
&&&&&&&&&&} &&
&&&&&&&&&&if(flag){ &&
&&&&&&&&&&&&&&session.setAttribute("user",user); &&
&&&&&&&&&&&&&&session.setAttribute("remoteHost",ip); &&
&&&&&&&&&&&&&&sessions.add(session); &&
&&&&&&&&&&&&&&application.setAttribute("sessionlist",sessions); &&
&&&&&&&&&&} &&
&&&&&&&&&if(flag&&user.getAuthority().equals("11")){ &&
&&&&&&&&&&&&response.sendRedirect("manageFrames2.htm"); &&
&&&&&&&&&&} &&
&&&&&&&&&if(flag&&user.getAuthority().equals("00")){ &&
&&&&&&&&&&&&response.sendRedirect("manageFrames1.htm"); &&
&&&&&&&&&&} &&
&&&&&else{ &&
&&&&&&&&&err="&center&密码错误,请重新登录!&/center&&br&&br&"; &&
&&&&&&&&&&out.println(err); &&
&&&err="&center&无此用户,请重新登录!&/center&&br&&br&";&& &&
&&&&&out.println(err); &&
TA的推荐TA的最新馆藏[转]&
喜欢该文的人也喜欢如何通过session控制单点登录_百度知道
如何通过session控制单点登录
我有更好的答案
& 单点登录要控制session共享 判断session存在,那么就可以重新生成session,之前的session失效
为您推荐:
其他类似问题
&#xe675;换一换
回答问题,赢新手礼包&#xe6b9;
个人、企业类
违法有害信息,请在下方选择后提交
色情、暴力
我们会通过消息、邮箱等方式尽快将举报结果通知您。jsonp+session实现单点登录
jsonp+session实现单点登录
&form action=&login.php& method=&#39;post&#39;&
username:&input type=&#39;text&#39; name=&#39;username&#39;&&br&
password:&input type=&#39;text&#39; name=&#39;password&#39;&&br&
&input type=&button& value=&#39;submit&#39;&
&script src=&./jq.js&&&/script&
$(function(){
$(&input[type=&#39;button&#39;]&).click(function(){
var username = $(&input[type=&#39;text&#39;]&).eq(0).val();
var password = $(&input[type=&#39;text&#39;]&).eq(1).val();
// alert(username);
type:&#39;post&#39;,
url:&#39;login.php&#39;,
data:{&#39;username&#39;:username,&#39;pwd&#39;:password},
dataType:&#39;json&#39;,
success:function(data){
// alert(data);
// console.log(data.code1);
$.each(data.data,function(i,item){
$.getJSON(item,{},function($data){
if(data.code == 200)
alert(&#39;success&#39;);
header(&content-type:text/charset=utf-8&);
$pdo = new PDO(&MySQL:host=127.0.0.1;dbname=test&,&#39;root&#39;,&#39;root&#39;);
//接收用户名及密码
$username = $_POST[&#39;username&#39;];
$password = md5($_POST[&#39;pwd&#39;]);
$obj = $pdo-&query(&select * from test where username=&#39;$username&#39; and pwd=&#39;$password&#39;&);
$arr = $obj-&fetch(PDO::FETCH_ASSOC);
// echo &#39;1&#39;;
$uid = $arr[&#39;id&#39;];
//生成token
//失效时间+用户id+用户其他信息
$invalidTime = date(&#39;YmdHis&#39;,time()+3600);
$tokenPre = $uid.$invalidT
$token = $tokenPre.md5(md5($tokenPre.&#39;;));
//在进行base64_encode token
$token = base64_encode($token);
$data = json_encode(array(&#39;code&#39;=&200,&#39;data&#39;=&array(&#39;https://www.b.com/index.php?token=&#39;.$token.&#39;&callback=?&#39;)));
header(&#39;content-type:text/json&#39;);
// print_r($pdo);

我要回帖

更多关于 cas 单点登录 session 的文章

 

随机推荐