CAS自定义登录

CAS系统介绍

CAS是单点登录系统(SSO)的一种,在多个应用系统中,只需要登录一次,就可以访问所有的系统。

一般会采用CAS统一登录页面,通过修改CAS中casLogin.jsp页面进行登录页面的改造。如果所有系统都是web端系统,建议采用这种方式。但在app登录或者个性化web需求中,就不得不考虑自定义登录了。

CAS自定义登录两种思路

  1. 修改login-webflow.xml文件,通过加入参数等方式,走不同的state,以获取Login Ticket(LT),在submit的时候,带上该LT,走正常的登录流程。且称为LT方式。
  2. 不走login-webflow,rest接口直接传入username、password,获取TGT。且称为TGT方式。

由于LT存放在CAS服务器本地,因此在集群部署的时候,我在项目中,使用的第二种方式。

LT方式

CAS统一登录页面的原理是,首先进入页面时,会获取到LT和execution,submit的时候,会对该LT进行验证。因此LT方式的目标是,自定义页面中,首先也获取到LT和execution,后续过程和统一的登录页面基本没区别。

So, 第一步:获取到LT和execution。一般如下流程:

  1. webflow中,添加action-state,根据传入的参数,判断走自定义的流程分支;
  2. 自定义流程分支中,要调用UniqueTicketIdGenerator生成LT,参考自带state:generateLoginTicket;
  3. 自定义流程分支中,要添加view-state及对应jsp页面,通过jsp页面做response返回。如:<%out.print(“json String”)%>

需要注意的是,此处如果是web端自定义登录页面,会有跨域问题。因此一般通过jsonp方式或iframe等,本人对前端不是太熟悉,不多提。

第一步获取LT后,应该能正常登录了。后续过程和统一的登录页面刚说基本没区别,但还是稍稍有区别:登录失败情况的跳转。

So,第二步:修改登录失败时候的跳转:

  1. 修改AuthenticationViaFormAction.submit方法,登录验证失败的逻辑中,根据request中自定义参数返回不同的内容;
  2. 根据上面返回内容,webflow中处理为不同的state,按第一步中同样的view-state方式,返回错误的信息。

其中1中修改AuthenticationViaFormAction对原有类有侵入,可模仿该类自定义验证Action用于替换,即修改cas-servlet.xml中bean。

参考文章《CAS自定义客户端登录界面》以及《SSO CAS单点系列之支持Web应用跨域登录CAS》,两篇文章中由于项目需要,我只试了获取LT的一部分。第一篇文章会对CAS有比较大的侵入,第二篇则基本不改变原CAS类的代码,结构上控制的比较好,但我试着一直有重定向问题,时间有限未定位出具体原因,估计是我自己这边的环境问题。

TGT方式

CAS提供restful接口,直接传入username、password进行验证后,获取TGT;后续的请求在参数或者cookie中带入该TGT,在CAS filter中对该TGT进行验证。

  • web.xml自定义rest servlet
    <servlet>
        <servlet-name>restletservlet-name>
        <servlet-class>com.noelios.restlet.ext.spring.RestletFrameworkServletservlet-class>
        <load-on-startup>1load-on-startup>
    servlet>

    <servlet-mapping>
        <servlet-name>restletservlet-name>
        <url-pattern>/v1/*url-pattern>
    servlet-mapping>
  • restlet-servlet.xml里面定义好map关系
    <bean id="root" class="org.restlet.ext.spring.SpringRouter">
        <property name="attachments">
            <map>
                <entry key="/tickets">
                    <bean class="org.restlet.ext.spring.SpringFinder">
                        <lookup-method name="createResource" bean="ticketResource" />
                    bean>
                entry>
            map>
        property>
    bean>
    <bean id="ticketResource" class="org.jasig.cas.integration.restlet.TicketResource" scope="prototype" />
  • 实现对应的acceptRepresentation方法。上例中TicketResource为CAS自带类,可仿照该类写出自定义的获取TGT或其他所需的rest接口
  • TGT获取是需要用username和password验证的。能获取到TGT的话,说明登录成功了。后续请求时需要带上该TGT作为cookie或者参数值。在经过CAS的filter时,自定义验证TGT的值

分布式部署问题

CAS默认会将login ticket、TGT、service Ticket等存放在本地缓存ConcurrentHashMap,因此在分布式部署时会出现验证失败的问题。

此时需要考虑改变ticket存储策略,比如换用redis等共享的缓存。此时需要自定义TicketRegistry替换DefaultTicketRegistry,然后将bean ticketRegistry指向自定义TicketRegistry类。

但改了此处后,貌似登录时所用的login ticket存放本地缓存的问题仍然无法解决,暂时的解决方案是改变NG的负载均衡策略为hash策略,以保证两次请求分布到同一台服务器。

你可能感兴趣的:(CAS)