Shiro 單點(diǎn)登錄

2020-12-04 09:44 更新

單點(diǎn)登錄

Shiro 1.2 開(kāi)始提供了 Jasig CAS 單點(diǎn)登錄的支持,單點(diǎn)登錄主要用于多系統(tǒng)集成,即在多個(gè)系統(tǒng)中,用戶(hù)只需要到一個(gè)中央服務(wù)器登錄一次即可訪(fǎng)問(wèn)這些系統(tǒng)中的任何一個(gè),無(wú)須多次登錄。此處我們使用 Jasig CAS v4.0.0-RC3 版本:
https://github.com/Jasig/cas/tree/v4.0.0-RC3

Jasig CAS 單點(diǎn)登錄系統(tǒng)分為服務(wù)器端和客戶(hù)端,服務(wù)器端提供單點(diǎn)登錄,多個(gè)客戶(hù)端(子系統(tǒng))將跳轉(zhuǎn)到該服務(wù)器進(jìn)行登錄驗(yàn)證,大體流程如下:

  1. 訪(fǎng)問(wèn)客戶(hù)端需要登錄的頁(yè)面 http://localhost:9080/client/,此時(shí)會(huì)跳到單點(diǎn)登錄服務(wù)器 https://localhost:8443/server/login?service=https://localhost:9443/client/cas;
  2. 如果此時(shí)單點(diǎn)登錄服務(wù)器也沒(méi)有登錄的話(huà),會(huì)顯示登錄表單頁(yè)面,輸入用戶(hù)名 / 密碼進(jìn)行登錄;
  3. 登錄成功后服務(wù)器端會(huì)回調(diào)客戶(hù)端傳入的地址:https://localhost:9443/client/cas?ticket=ST-1-eh2cIo92F9syvoMs5DOg-cas01.example.org,且?guī)е粋€(gè) ticket;
  4. 客戶(hù)端會(huì)把 ticket 提交給服務(wù)器來(lái)驗(yàn)證 ticket 是否有效;如果有效服務(wù)器端將返回用戶(hù)身份;
  5. 客戶(hù)端可以再根據(jù)這個(gè)用戶(hù)身份獲取如當(dāng)前系統(tǒng)用戶(hù) / 角色 / 權(quán)限信息。

本章使用了和《第十四章 SSL》一樣的數(shù)字證書(shū)。

服務(wù)器端

我們使用了 Jasig CAS 服務(wù)器 v4.0.0-RC3 版本,可以到其官方的 github 下載:https://github.com/Jasig/cas/tree/v4.0.0-RC3 下載,然后將其 cas-server-webapp 模塊封裝到 shiro-example-chapter15-server 模塊中,具體請(qǐng)參考源碼。

1、數(shù)字證書(shū)使用和《第十四章 SSL》一樣的數(shù)字證書(shū),即將 localhost.keystore 拷貝到 shiro-example-chapter15-server 模塊根目錄下;

2、在 pom.xml 中添加 Jetty Maven 插件,并添加 SSL 支持:

<plugin>
  <groupId>org.mortbay.jetty</groupId>
  <artifactId>jetty-maven-plugin</artifactId>
  <version>8.1.8.v20121106</version>
  <configuration>
    <webAppConfig>
      <contextPath>/${project.build.finalName}</contextPath>
    </webAppConfig>
    <connectors>
      <connector implementation="org.eclipse.jetty.server.nio.SelectChannelConnector">
        <port>8080</port>
      </connector>
      <connector implementation="org.eclipse.jetty.server.ssl.SslSocketConnector">
        <port>8443</port>
        <keystore>${project.basedir}/localhost.keystore</keystore>
       <password>123456</password>
        <keyPassword>123456</keyPassword>
      </connector>
    </connectors>
  </configuration>
</plugin>

3、修改 src/main/webapp/WEB-INF/deployerConfigContext.xml,找到 primaryAuthenticationHandler,然后添加一個(gè)賬戶(hù):

<entry key="zhang" value="123"/>

其也支持如 JDBC 查詢(xún),可以自己定制;具體請(qǐng)參考文檔。

4、mvn jetty:run 啟動(dòng)服務(wù)器測(cè)試即可:
訪(fǎng)問(wèn) https://localhost:8443/chapter15-server/login 將彈出如下登錄頁(yè)面:

輸入用戶(hù)名 / 密碼,如 zhang/123,將顯示登錄成功頁(yè)面:

到此服務(wù)器端的簡(jiǎn)單配置就完成了。

客戶(hù)端

1、首先使用 localhost.keystore 導(dǎo)出數(shù)字證書(shū)(公鑰)到 D:\localhost.cer

keytool -export -alias localhost -file D:\localhost.cer -keystore D:\localhost.keystore&nbsp;

2、因?yàn)?CAS client 需要使用該證書(shū)進(jìn)行驗(yàn)證,需要將證書(shū)導(dǎo)入到 JDK 中:

cd D:\jdk1.7.0_21\jre\lib\security
keytool -import -alias localhost -file D:\localhost.cer -noprompt -trustcacerts -storetype jks -keystore cacerts -storepass 123456

如果導(dǎo)入失敗,可以先把 security 目錄下的 cacerts 刪掉;

3、按照服務(wù)器端的 Jetty Maven 插件的配置方式配置 Jetty 插件;

4、在 shiro-example-chapter15-client 模塊中導(dǎo)入 shiro-cas 依賴(lài),具體請(qǐng)參考其 pom.xml;

5、自定義 CasRealm:

public class MyCasRealm extends CasRealm {
    private UserService userService;
    public void setUserService(UserService userService) {
        this.userService = userService;
    }
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        String username = (String)principals.getPrimaryPrincipal();
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        authorizationInfo.setRoles(userService.findRoles(username));
        authorizationInfo.setStringPermissions(userService.findPermissions(username));
        return authorizationInfo;
    }
}

CasRealm 根據(jù) CAS 服務(wù)器端返回的用戶(hù)身份獲取相應(yīng)的角色 / 權(quán)限信息。

6、spring-shiro-web.xml 配置:

<bean id="casRealm" class="com.github.zhangkaitao.shiro.chapter13.realm.MyCasRealm">
    <property name="userService" ref="userService"/>
    ……
    <property name="casServerUrlPrefix" value="https://localhost:8443/chapter14-server"/>
    <property name="casService" value="https://localhost:9443/chapter14-client/cas"/>
</bean>

casServerUrlPrefix:是 CAS Server 服務(wù)器端地址;
casService:是當(dāng)前應(yīng)用 CAS 服務(wù) URL,即用于接收并處理登錄成功后的 Ticket 的;

如果角色 / 權(quán)限信息是由服務(wù)器端提供的話(huà),我們可以直接使用 CasRealm:

<bean id="casRealm" class="org.apache.shiro.cas.CasRealm">
    ……
    <property name="defaultRoles" value="admin,user"/>
    <property name="defaultPermissions" value="user:create,user:update"/>
    <property name="roleAttributeNames" value="roles"/>
    <property name="permissionAttributeNames" value="permissions"/>
    <property name="casServerUrlPrefix" value="https://localhost:8443/chapter14-server"/>
    <property name="casService" value="https://localhost:9443/chapter14-client/cas"/>
</bean>

defaultRoles/ defaultPermissions:默認(rèn)添加給所有 CAS 登錄成功用戶(hù)的角色和權(quán)限信息; roleAttributeNames/ permissionAttributeNames:角色屬性 / 權(quán)限屬性名稱(chēng),如果用戶(hù)的角色 / 權(quán)限信息是從服務(wù)器端返回的(即返回的 CAS Principal 中除了 Principal 之外還有如一些 Attributes),此時(shí)可以使用 roleAttributeNames/ permissionAttributeNames 得到 Attributes 中的角色 / 權(quán)限數(shù)據(jù);請(qǐng)自行查詢(xún) CAS 獲取用戶(hù)更多信息。

<bean id="casFilter" class="org.apache.shiro.cas.CasFilter">
    <property name="failureUrl" value="/casFailure.jsp"/>
</bean>

CasFilter 類(lèi)似于 FormAuthenticationFilter,只不過(guò)其驗(yàn)證服務(wù)器端返回的 CAS Service Ticket。

<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
    <property name="securityManager" ref="securityManager"/>
    <property name="loginUrl" value="https://localhost:8443/chapter14-server/login?service=https://localhost:9443/chapter14-client/cas"/>
    <property name="successUrl" value="/"/>
    <property name="filters">
        <util:map>
            <entry key="cas" value-ref="casFilter"/>
        </util:map>
    </property>
    <property name="filterChainDefinitions">
        <value>
            /casFailure.jsp = anon
            /cas = cas
            /logout = logout
            /** = user
        </value>
    </property>
</bean>

loginUrl:https://localhost:8443/chapter15-server/login 表示服務(wù)端端登錄地址,登錄成功后跳轉(zhuǎn)到?service 參數(shù)對(duì)于的地址進(jìn)行客戶(hù)端驗(yàn)證及登錄;
“/cas=cas”:即 /cas 地址是服務(wù)器端回調(diào)地址,使用 CasFilter 獲取 Ticket 進(jìn)行登錄。

7、測(cè)試,輸入 http://localhost:9080/chapter15-client 地址進(jìn)行測(cè)試即可,可以使用如 Chrome 開(kāi)這 debug 觀(guān)察網(wǎng)絡(luò)請(qǐng)求的變化。

如果遇到以下異常,一般是證書(shū)導(dǎo)入錯(cuò)誤造成的,請(qǐng)嘗試重新導(dǎo)入,如果還是不行,有可能是運(yùn)行應(yīng)用的 JDK 和安裝數(shù)字證書(shū)的 JDK 不是同一個(gè)造成的:

Caused by:   sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target  

at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:385)
at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:292)
at sun.security.validator.Validator.validate(Validator.java:260)
at sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:326)
at sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:231)
at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:126)
at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1323)
   ... 67 more  

Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:196)
at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:268)
at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:380)
... 73 more
以上內(nèi)容是否對(duì)您有幫助:
在線(xiàn)筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)