<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    隨筆-124  評論-194  文章-0  trackbacks-0

    CAS多點登陸之非主流配置方式

    場景

    想要用到的場景:用戶訪問WEB服務,WEB訪問非WEB服務1,服務1又再訪問2、3,合并計算后,把數據返回給WEB及前端用戶。想讓訪問鏈上的所有服務都能得到認證和鑒權,認為本次請求確實是來自用戶的。所以想到用CAS,讓用戶在一點登錄,所有服務都到此處認證和鑒權。

    clip_image001[34]

    CAS小介紹

    下面是兩張圖,來自網上兩個PPT(猛戳下載),其中一個還有動畫演示,感謝原分享者。

    clip_image003[28]

    clip_image005[26]

    用我的話解釋下就是:

    1、 用戶先訪問http://adm/index.html服務,因為沒有登陸被重定向到CAS去輸入用戶名密碼,這個好理解。注意重定向地址:
    https://cas.company.com/login?service=http://adm/index.html
    問號前是CAS服務地址,后面跟了原來要訪問的服務,方便CAS把你再重定向回來。

    2、 登陸完成后,CAS會寫一個COOKIE(CASTGC),它的作用是下次再認證時不用再輸入密碼。同時,CAS把用戶重定向回原來訪問地址:
    http://adm/index.html?ticket= ST-1-qRPh34B1xhe4dquzz
    注意后面多了個ticket

    3、 這時候,ADM這個WEB服務,再用ticket去CAS做認證,CAS報告OK,它即認為用戶登陸了。

    4、 如:用戶再訪問下一個AMS的WEB服務時,因為帶有CASTGC這個COOKIE,被重定向到CAS后,它就會用這個COOKIE直接生成一個ticket(就沒有讓用戶登陸的過程了哦!),AMS拿到ticket后再去認證就可以了。

    clip_image007[20]

    開頭場景遇到的問題

    開始我們的場景如果全部照搬CAS的應用,會存在如下問題:

    1、 和CAS的交互全走HTTPS,要在JRE中生成和導入證書(網上搜配置tomcat的https一大把),用戶認證時會被提示證書不可信。如果是一個直接交付給終端的產品,誰來配置這些東東?讓用戶看到這種提示又情何以堪?

    2、 每當訪問一個新服務都要和CAS產生兩次交互,申請簽發TICKET,再去認證TICKET

    3、 默認的ticket有效時間很短,重定向回來后,要馬上去認證,并且一個ticket只能去CAS認證一次就失效了

    4、 Ticket是和原始的URL綁定的,兩者都要提供給CAS才能認證通過,即你不能用AMS服務簽發的ticket,去用在ADM服務的認證上

    5、 如果是非WEB的服務要認證,需要用到CAS的代理模式,過程比較繁復

    結論是開頭場景要用CAS是很艱難的。

    變通后的方案

    這是我想到的一些改動來滿足開頭場景:

    1、 改為HTTP驗證方式

    2、 由WEB服務去CAS簽發一次TICKET,后繼的非WEB服務全部用這一個TICKET到CAS做認證,它和用戶登陸后有效期一致,也沒有使用次數限制

    3、 提供一個FILTER來為WEB層所有頁面統一提供認證服務

    4、 用戶名、密碼、鑒權信息(用戶角色)存到數據庫

    下面就介紹這種非主流的改法,可能已經安全性大大降低,但至少能RUN啦。。。

    下載安裝

    下載并解壓CAS安裝包:(不要問為啥下載JASIG的,因為網上全是它。。)

    http://www.jasig.org/cas_server_3_5_1_release

    解壓后帶源碼,后面步驟還會用到。

    把其中modules目錄下的這個WAR包布到tomcat的webapp目錄,重啟下就算安裝好了:

    cas-server-3.5.1/modules/cas-server-webapp-3.5.1.war

    改配置解決HTTP和TICKET生命期

    1、 加長TICKET的生命期和使用次數:

    clip_image009[14]

    2、 改為使用HTTP:

    clip_image011[12]

    clip_image013[12]

    從JDBC認證和鑒權

    1、 把默認的用戶名密碼相同即通過的認證方式注釋掉:

    clip_image015[8]

    替換為下面這段從數據庫讀取:

    <bean class="org.jasig.cas.adaptors.jdbc.QueryDatabaseAuthenticationHandler">

    <property name="sql" value="select passwd from t_admin where nickname=?"/>

    <property name="dataSource" ref="dataSource"/>

    </bean>

    2、 把默認的鑒權信息獲取方式注釋掉:

    clip_image017[6]

    替換為:

    這種是一個用戶僅一個角色(SingleRow):

    <bean id="attributeRepository" class="org.jasig.services.persondir.support.jdbc.SingleRowJdbcPersonAttributeDao">

    <constructor-arg index="0" ref="dataSource"/>

    <constructor-arg index="1" value="SELECT g.id,g.groupname,role.role FROM t_group AS g LEFT OUTER JOIN t_group_role AS grouprole ON (g.id = grouprole.groupid) LEFT OUTER JOIN t_role AS role ON (role.id = grouprole.roleid) LEFT OUTER JOIN t_group_user AS groupuser on (g.id = groupuser.groupid) LEFT OUTER JOIN t_admin ON (t_admin.id = groupuser.userid) WHERE t_admin.nickname = ?"/>

    <!--這里的key需寫username,value對應數據庫用戶名字段 -->

    <property name="queryAttributeMapping">

    <map>

    <entry key="username" value="nickname"/>

    </map>

    </property>

    <!--key對應數據庫字段,value對應客戶端獲取參數 -->

    <property name="resultAttributeMapping">

    <map>

    <entry key="role" value="authorities"/>

    </map>

    </property>

    </bean>

    3、 多行模式(和上面的單行模式二選一)

    這種是一個用戶對應多個角色(MultiRow):(這里這個attr_name繞了我半天。。。這里有點解釋

    <bean id="attributeRepositoryMulti" class="org.jasig.services.persondir.support.jdbc.MultiRowJdbcPersonAttributeDao">

    <constructor-arg index="0" ref="dataSource"/>

    <constructor-arg index="1" value="SELECT g.id,g.groupname,'authorities' as attr_name,role.role FROM t_group AS g LEFT OUTER JOIN t_group_role AS grouprole ON (g.id = grouprole.groupid) LEFT OUTER JOIN t_role AS role ON (role.id = grouprole.roleid) LEFT OUTER JOIN t_group_user AS groupuser on (g.id = groupuser.groupid) LEFT OUTER JOIN t_admin ON (t_admin.id = groupuser.userid) WHERE t_admin.nickname = ?"/>

    <!--這里的key需寫username,value對應數據庫用戶名字段 -->

    <property name="queryAttributeMapping">

    <map>

    <entry key="username" value="nickname"/>

    </map>

    </property>

    <property name="nameValueColumnMappings">

    <map>

    <entry key="attr_name" value="role" />

    </map>

    </property>

    </bean>

    如果要用多行模式把相應的引用的類要變一下:

    clip_image019[4]

    4、 鑒權信息要能輸出到前端,還要改下JSP:

    clip_image021[4]

    在上圖所示位置加下:

    <c:if test="${fn:length(assertion.chainedAuthentications[fn:length(assertion.chainedAuthentications)-1].principal.attributes)> 0}">

    <cas:attributes>

    <c:forEach

    var="attr"

    items="${assertion.chainedAuthentications[fn:length(assertion.chainedAuthentications)-1].principal.attributes}"

    varStatus="loopStatus"

    begin="0"

    end="${fn:length(assertion.chainedAuthentications[fn:length(assertion.chainedAuthentications)-1].principal.attributes)-1}"

    step="1">

    <cas:${fn:escapeXml(attr.key)}>${fn:escapeXml(attr.value)}</cas:${fn:escapeXml(attr.key)}>

    </c:forEach>

    </cas:attributes>

    </c:if>

    5、 加上數據源定義

    clip_image023[4]

    如下:

    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">

    <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>

    <property name="url" value="jdbc:mysql://127.0.0.1:3306/uu?autoReconnect=true&amp;useUnicode=true&amp;characterEncoding=utf-8&amp;zeroDateTimeBehavior=convertToNull&amp;transformedBitIsBoolean=true"></property>

    <property name="username" value="root"></property>

    <property name="password" value="xxxxx"></property>

    </bean>

    6、 建表:(表結構來自此處)

    SET FOREIGN_KEY_CHECKS=0;

    ------------------------------

    -- 創建管理員帳號表t_admin

    -- ----------------------------

    CREATE TABLE `t_admin` (

    `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,

    `passwd` varchar(12) NOT NULL DEFAULT '' COMMENT '用戶密碼',

    `nickname` varchar(20) NOT NULL DEFAULT '' COMMENT '用戶名字',

    `phoneno` varchar(32) NOT NULL DEFAULT '' COMMENT '電話號碼',

    PRIMARY KEY (`id`)

    ) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;

    -- ----------------------------

    -- 添加3個管理帳號

    -- ----------------------------

    INSERT INTO `t_admin` VALUES ('1', 'admin', 'admin', '');

    INSERT INTO `t_admin` VALUES ('4', '123456', 'test', '');

    INSERT INTO `t_admin` VALUES ('5', '111111', '111111', '');

    -- ----------------------------

    -- 創建權限表t_role

    -- ----------------------------

    CREATE TABLE `t_role` (

    `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,

    `role` varchar(40) NOT NULL DEFAULT '',

    `descpt` varchar(40) NOT NULL DEFAULT '' COMMENT '角色描述',

    `category` varchar(40) NOT NULL DEFAULT '' COMMENT '分類',

    PRIMARY KEY (`id`)

    ) ENGINE=InnoDB AUTO_INCREMENT=60 DEFAULT CHARSET=utf8;

    -- ----------------------------

    -- 加入4個操作權限

    -- ----------------------------

    INSERT INTO `t_role` VALUES ('1', 'ROLE_ADMIN', '系統管理員', '系統管理員');

    INSERT INTO `t_role` VALUES ('2', 'ROLE_UPDATE_FILM', '修改', '影片管理');

    INSERT INTO `t_role` VALUES ('3', 'ROLE_DELETE_FILM', '刪除', '影片管理');

    INSERT INTO `t_role` VALUES ('4', 'ROLE_ADD_FILM', '添加', '影片管理');

    -- ----------------------------

    -- 創建權限組表

    -- ----------------------------

    CREATE TABLE `t_group` (

    `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,

    `groupname` varchar(50) NOT NULL DEFAULT '',

    PRIMARY KEY (`id`)

    ) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;

    -- ----------------------------

    -- 添加2個權限組

    -- ----------------------------

    INSERT INTO `t_group` VALUES ('1', 'Administrator');

    INSERT INTO `t_group` VALUES ('2', '影片維護');

    -- ----------------------------

    -- 創建權限組對應權限表t_group_role

    -- ----------------------------

    CREATE TABLE `t_group_role` (

    `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,

    `groupid` bigint(20) unsigned NOT NULL,

    `roleid` bigint(20) unsigned NOT NULL,

    PRIMARY KEY (`id`),

    UNIQUE KEY `groupid2` (`groupid`,`roleid`),

    KEY `roleid` (`roleid`),

    CONSTRAINT `t_group_role_ibfk_1` FOREIGN KEY (`groupid`) REFERENCES `t_group` (`id`),

    CONSTRAINT `t_group_role_ibfk_2` FOREIGN KEY (`roleid`) REFERENCES `t_role` (`id`)

    ) ENGINE=InnoDB AUTO_INCREMENT=83 DEFAULT CHARSET=utf8;

    -- ----------------------------

    -- 加入權限組與權限的對應關系

    -- ----------------------------

    INSERT INTO `t_group_role` VALUES ('1', '1', '1');

    INSERT INTO `t_group_role` VALUES ('2', '2', '2');

    INSERT INTO `t_group_role` VALUES ('4', '2', '4');

    -- ----------------------------

    -- 創建管理員所屬權限組表t_group_user

    -- ----------------------------

    CREATE TABLE `t_group_user` (

    `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,

    `userid` bigint(20) unsigned NOT NULL,

    `groupid` bigint(20) unsigned NOT NULL,

    PRIMARY KEY (`id`),

    KEY `userid` (`userid`),

    KEY `groupid` (`groupid`),

    CONSTRAINT `t_group_user_ibfk_2` FOREIGN KEY (`groupid`) REFERENCES `t_group` (`id`),

    CONSTRAINT `t_group_user_ibfk_3` FOREIGN KEY (`userid`) REFERENCES `t_admin` (`id`)

    ) ENGINE=InnoDB AUTO_INCREMENT=18 DEFAULT CHARSET=utf8;

    -- ----------------------------

    -- 將管理員加入權限組

    -- ----------------------------

    INSERT INTO `t_group_user` VALUES ('1', '1', '1');

    INSERT INTO `t_group_user` VALUES ('2', '4', '2');

    -- ----------------------------

    -- 創建管理員對應權限表t_user_role

    -- 設置該表可跳過權限組,為管理員直接分配權限

    -- ----------------------------

    CREATE TABLE `t_user_role` (

    `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,

    `userid` bigint(20) unsigned NOT NULL,

    `roleid` bigint(20) unsigned NOT NULL,

    PRIMARY KEY (`id`),

    KEY `userid` (`userid`),

    KEY `roleid` (`roleid`),

    CONSTRAINT `t_user_role_ibfk_1` FOREIGN KEY (`userid`) REFERENCES `t_admin` (`id`),

    CONSTRAINT `t_user_role_ibfk_2` FOREIGN KEY (`roleid`) REFERENCES `t_role` (`id`)

    ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;

    與spring-security結合使用

    我們是自己開發filter,但順便把spring-security的配置方法帶一下:

    1、 Web.xml加上spring-security的filter:

    <!-- spring 配置文件 -->

    <context-param>

    <param-name>contextConfigLocation</param-name>

    <param-value>classpath*:/applicationContext-security-ns.xml</param-value>

    </context-param>

    <!-- spring 默認偵聽器 -->

    <listener>

    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>

    </listener>

    <listener>

    <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>

    </listener>

    <!-- Filter 定義 -->

    <!-- spring security filter -->

    <filter>

    <filter-name>springSecurityFilterChain</filter-name>

    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>

    </filter>

    <filter-mapping>

    <filter-name>springSecurityFilterChain</filter-name>

    <url-pattern>/*</url-pattern>

    </filter-mapping>

    2、 application參考配置以及修改的vote文件(這里

    在單行模式下,權限信息可以正常解析到detail變量中,但在多行的時候,CAS傳過來的多個角色是這種格式:[ROLE_1,ROLE_2],帶有中括號,原有VOTE是精確比對,附件里改了個index函數來比對:

    clip_image024[4]

    有了上面這些Spring-security就可以正常運作,但是場景里要使用非WEB服務多次驗證,所以其實不能用spring-security的filter,我們還是要自己寫的。

    Go on…

    解除TICKET與SERVICE的綁定,要改代碼哦!

    Cas服務器ticket和service驗證時,要和來簽發時的service url一致才行,否則就報下面的錯誤:

    org.jasig.cas.client.validation.TicketValidationException:

    ticket 'ST-14-SYa99tdAMhI31ZehfSTW-cas01.example.org' does not match supplied service

    為了我們多個不同服務可以重復使用一個ticket,CAS的源碼上做個小小改動即可:

    去到之前解壓的CAS目錄,搜出這句話的java文件:

    grep -R "does not match supplied service" .

    修改這個文件:

    vi ./cas-server-core/src/main/java/org/jasig/cas/CentralAuthenticationServiceImpl.java

    注釋掉驗證服務URL的相應行就好了:

    clip_image025[4]

    編譯一下:

    mvn compile

    mvn -DskipTests=true package

    把編好的:

    cas-server-3.5.1/cas-server-core/target/cas-server-core-3.5.1.jar

    拷到下列位置替換原來的:

    /usr/local/apache-tomcat-7.0.32/webapps/cas/WEB-INF/lib/

    重啟下TOM的小貓就可以了。

    自制的FILTER

    自己制作的filter要達到目標是:

    1、 在沒認證時可以重定向

    2、 重定向回來的時候,要去認證并把ticket寫COOKIE

    3、 有COOKIE時,取出來直接去做認證

    Filter的一個比較清晰易懂的基礎介紹

    一個講COOKIE的文章

    最后是完整代碼:點我

    Filter的代碼是CasFilter.java,相當簡單,下面這段就是用CAS提供的客戶端去驗證TICKET,因為service不驗了,所以validate的第二個參數已經不重要。

    Attributes就是權限信息:

    clip_image026[4]

    非WEB服務的認證和鑒權

    我們開頭場景中的WEB服務在訪問后端時,把TICKET信息也帶上,這樣后端的非WEB服務也可以用剛才提到的函數去認證,并且獲取到權限信息,做自己的鑒權。

    服務1再訪問服務2時,也一樣帶上TICKET,這樣因為我們已經延長了TICKET有效次數和期限,它也不會過期。

    但是,一旦用戶LOGOUT了,這個TICKET也就失效,此時所有服務都將驗證不通過,WEB自然又會把用戶重定向到CAS去登陸了。

    羅里八嗦講一大堆,用了好多歪門斜道,不值一提,只是為今后有個查閱的地方。

    posted on 2012-12-01 10:43 我愛佳娃 閱讀(9803) 評論(3)  編輯  收藏 所屬分類: 服務配置

    評論:
    # re: CAS多點登陸之&ldquo;非主流&rdquo;配置方式 2012-12-02 11:12 | rox
    分析的真詳細,非常感謝!  回復  更多評論
      
    # re: CAS多點登陸之&ldquo;非主流&rdquo;配置方式 2012-12-06 18:14 | 來如風
    能否簡要總結一下,主要改進了那些功能!  回復  更多評論
      
    # re: CAS多點登陸之&ldquo;非主流&rdquo;配置方式[未登錄] 2016-04-28 17:02 | Jack
    感謝感謝!!  回復  更多評論
      
    主站蜘蛛池模板: 亚洲电影日韩精品| 四虎永久精品免费观看| 亚洲av综合avav中文| 国产精品九九久久免费视频| 亚洲高清成人一区二区三区| 亚洲欧美国产国产一区二区三区 | 一边摸一边桶一边脱免费视频 | 久久亚洲美女精品国产精品| 国产人在线成免费视频| 日韩精品亚洲专区在线影视| 中文字幕久无码免费久久| 亚洲码一区二区三区| 免费中文字幕在线观看| 99在线在线视频免费视频观看| 国产亚洲精品自在久久| 成年女人免费碰碰视频| 久久久久国产精品免费看| 精品久久久久久久久亚洲偷窥女厕| 亚洲AV无码第一区二区三区| 免费国产成人午夜电影| 中文字幕免费视频一| 九九久久国产精品免费热6| 亚洲一区在线视频| 成人免费无码大片A毛片抽搐色欲| 中国在线观看免费的www| 亚洲综合国产成人丁香五月激情 | 亚洲黄色网址大全| 亚洲午夜久久久影院伊人 | 亚洲欧洲在线观看| 99热这里有免费国产精品| 一区二区在线视频免费观看| 亚洲小说图区综合在线| 亚洲视频在线一区二区三区| 久久伊人亚洲AV无码网站| 无码国模国产在线观看免费| 精品国产sm捆绑最大网免费站| 日韩精品无码免费专区午夜不卡| 国产精品久久久久久亚洲小说| 亚洲精品456人成在线| 亚洲男人天堂影院| 91亚洲国产在人线播放午夜|