8、单点登录原理

前言

主要是看图,其实很详细的

相同域名下实现单点登录太简单,因为A系统和B系统域名相同,在访问A时候登录过,在访问B时,由于域名相同,浏览器会带上cookies,cookies里有票据信息,访问B自然就不用再登录了。
可是如果A系统和B系统域名不同,CAS是怎样做到已经在访问A系统时候登录过,在访问B系统时就不用再重新登录了呢?此时访问B系统,因为域名不同的缘故,浏览器没有带上存储有票据信息的cookies啊,好了此时CAS实现单点登录的原理是这样的,请看图:

WX20180427-190218@2x

下面二者具体看代码
•  TGT(Ticket Grangting Ticket)
        TGT是CAS为用户签发的登录票据,拥有了TGT,用户就可以证明自己在CAS成功登录过。TGT封装了Cookie值以及此Cookie值对应的用户信息。
        用户在CAS认证成功后,CAS生成cookie(叫TGC),写入浏览器,同时生成一个TGT对象,放入自己的缓存,TGT对象的ID就是cookie的值。
        当HTTP再次请求到来时,如果传过来的有CAS生成的cookie,则CAS以此cookie值为key查询缓存中有无TGT ,如果有的话,则说明用户之前登录过,如果没有,则用户需要重新登录。
•  ST(Service Ticket)
        ST是CAS为用户签发的访问某一service的票据。用户访问service时,service发现用户没有ST,则要求用户去CAS获取ST。用户向CAS发出获取ST的请求,如果用户的请求中包含cookie,则CAS会以此cookie值为key查询缓存中有无TGT,如果存在TGT,则用此TGT签发一个ST,返回给用户。用户凭借ST去访问service,service拿ST去CAS验证,验证通过后,允许用户访问资源。



1)用户访问cas-client,被拦截跳转到cas-server进行登录,输入正确的用户信息
2)登录成功后,cas-server签发一个TGC票据,写入浏览器同时生成一个TGT对象,放入自己的缓存,TGT对象的ID就是cookie的值,并再次跳转到cas-client,同时携带着ST票据
cas-client发现有ST票据则拿着ST票据去cas-server验证,如果验证通过,则返回用户名信息
3)cas-client登录成功,用户访问另一个cas-client2时,也会被拦截再次跳转到cas-server发现TGC票据生成的TGT对象的ID值存在则直接验证通过,签发一个ST票据给cas-client2。

WX20180811-154051@2x

1、问:系统A是如何发现该请求需要登录重定向到认证中心的?


答:用户通过浏览器地址栏访问系统A,系统A(也可以称为CAS客户端)去Cookie中拿JSESSION,即在Cookie中维护的当前回话session的id,如果拿到了,说明用户已经登录,如果未拿到,说明用户未登录。

2、问:系统A重定向到认证中心,发送了什么信息或者地址变成了什么?

答:假如系统A的地址为http://a:8080/,CAS认证中心的服务地址为http://cas.server:8080/,那么重点向前后地址变化为:http://a:8080/————>ttp://cas.server:8080/?service=http://a:8080/,由此可知,重点向到认证中心,认证中心拿到了当前访问客户端的地址。

3、问:登录成功后,认证中心重定向请求到系统A,认证通过令牌是如何附加发送给系统A的?

答:重定向之后的地址栏变成:http://a:8080/?ticket=ST-XXXX-XXX,将票据以ticket为参数名的方式通过地址栏发送给系统A

4、问:系统A验证令牌,怎样操作证明用户登录的?

答:系统A通过地址栏获取ticket的参数值ST票据,然后从后台将ST发送给CAS server认证中心验证,验证ST有效后,CAS server返回当前用户登录的相关信息,系统A接收到返回的用户信息,并为该用户创建session会话,会话id由cookie维护,来证明其已登录。

5、问:登录B系统,认证中心是如何判断用户已经登录的?

答:在系统A登录成功后,用户和认证中心之间建立起了全局会话,这个全局会话就是TGT(Ticket Granting Ticket),TGT位于CAS服务器端,TGT并没有放在Session中,也就是说,CAS全局会话的实现并没有直接使用Session机制,而是利用了Cookie自己实现的,这个Cookie叫做TGC(Ticket Granting Cookie),它存放了TGT的id,保存在用户浏览器上。 

用户发送登录系统B的请求,首先会去Cookie中拿JSESSION,因为系统B并未登录过,session会话还未创建,JSESSION的值是拿不到的,然后将请求重定向到CAS认证中心,CAS认证中心先去用户浏览器中拿TGC的值(用户已经登录过),也就是全局会话id,如果存在则代表用户在认证中心已经登录,附带上认证令牌重定向到系统B。
上面登录状态判断也是这个逻辑。

6、问:登出的过程,各个系统对当前用户都做了什么

答:认证中心清除当前用户的全局会话TGT,同时清掉cookie中TGT的id:TGC; 
然后是各个客户端系统,比如系统A、系统B,清除局部会话session,同时清掉cookie中session会话id:jsession
  • cookie 和 session机制都是为了解决HTTP协议的无状态性而生的
  • cookie 是客户端保存用户信息的机制
  • session 是服务器端保存用户信息的机制
  • cookie其实就是保存在浏览器中的一小段文本,当浏览器第一次访问jsp时,jsp response会给浏览器生成一个cookie,然后浏览器会存储它

  • cookie 具有不可跨域性

  • cookie 和session 联合使用可以解决跨域记录用户信息的问题
  • 为了使浏览器记住服务器上的session,浏览器会生成一个id , 这个id就叫做jsessionid
  • 另外,由于一些浏览器禁用cookie, Java EE标准 退出了URL重写,使url里面记录jssessionid、单点的概率的jsessionid是存在单点登录服务器端的

  • 上述让浏览器记住服务器上的session的形式就叫做会话cookie,会话cookie在用户登出或者浏览器关闭后自动删除,这样就叫做一次会话。

理解了上面的基本概念和标准,对于理解cas的实现和基本流程很有帮助,cas这样的sso解决方案本来就是构建在cookie和session标准之上的。

1.1、从cookie中获取jssensionid

Cookie[] cookies = request.getCookies();
for(Cookie cookie:cookies){
    if(cookie.getName().equals("JSESSIONID")){
        log.info("cookie:"+cookie.getName()+"value:"+cookie.getValue());
        loginResultData.setJSESSIONID(cookie.getValue());
    }
}

cookieName:JSESSIONID-value:E5E7A0895E652C49CE066220680E243E

1.2.1、获取session

根据结果很明显我们可以看session中存放的是单点登录携带的信息


//获取session
HttpSession session   =   request.getSession();
// 获取session中所有的键值
Enumeration<String> attrs = session.getAttributeNames();
    // 遍历attrs中的
while(attrs.hasMoreElements()){
    // 获取session键值
    String name = attrs.nextElement().toString();
    // 根据键值取session中的值
    Object vakue = session.getAttribute(name);
    // 打印结果
    System.out.println("------" + name + ":" + vakue +"--------\n");
}

sessionName::_const_cas_assertion_-value:org.jasig.cas.client.validation.AssertionImpl@12d08532

1.2.2、从session中获取信息

Assertion assertion = session != null ? (Assertion) session.getAttribute(AbstractCasFilter.CONST_CAS_ASSERTION):null;

// 判断是否登录过,如果已经登录过,进入if并且退出
if (assertion != null){
    log.info("session中获取的"+assertion.getPrincipal().toString());
}

session中获取的test@test.com

2、客户端验证是否登录

  )
    @ResponseBody
    @GetMapping(value = "healerjean/validate",produces="application/json;charset=utf-8" )
    public ResponseBean invalidate(HttpServletRequest request){


            Cookie[] cookies = request.getCookies();
            String  JSESSIONID = null;
            String loginSessionId = null ;
            for(Cookie cookie:cookies){
                if(cookie.getName().equals("JSESSIONID")){
                    JSESSIONID = cookie.getValue();
                }
                if(cookie.getName().equals("loginSessionId")){
                    loginSessionId = cookie.getValue();
                }
            }


            LoginResultData loginResultData = new LoginResultData() ;
            if(StringUtils.isNotBlank(JSESSIONID)&&StringUtils.isNotBlank(loginSessionId)&&StringUtils.equals(JSESSIONID,loginSessionId )){

                loginResultData.setJSESSIONID(JSESSIONID);

                return ResponseBean.buildSuccess(loginResultData);

            }


        return ResponseBean.buildFailure("401","未认证,请重新登录");
    }



@Value("${server.login.redirect.url}")
private String server_login_redirect_url;
/**
 * 首页面进入之后,跳转到前端url界面,如果是前后端分离用的两个域名的情况下
 * @return
 */
@GetMapping(value = {"/",""} )
public String loginDev(HttpServletRequest request,HttpServletResponse response){

    Cookie cookieLogin = new Cookie("loginSessionId", getJsessionId(request));
    cookieLogin.setComment("存储客户端登录状态,防止服务器重启之后,session自动刷新");    // Cookie描述
    cookieLogin.setMaxAge(24*60*60);            // Cookie有效时间 24小时
    response.addCookie(cookieLogin); //给客户端添加cookie

    return  "redirect://"+server_login_redirect_url;
}


public String getJsessionId(HttpServletRequest request){
    Cookie[] cookies = request.getCookies();
    for(Cookie cookie:cookies){
        if(cookie.getName().equals("JSESSIONID")){
            return cookie.getValue();
        }
    }
    return  null ;
}






如果满意,请打赏博主任意金额,感兴趣的请下方留言吧。可与博主自由讨论哦

支付包 微信 微信公众号
支付宝 微信 微信公众号
posted @ 2018-04-27 19:06  HealerJean  阅读(189)  评论(0编辑  收藏  举报