shiro 單點登入原理 例項
Shiro 1.2開始提供了Jasig CAS單點登入的支援,單點登入主要用於多系統整合,即在多個系統中,使用者只需要到一箇中央伺服器登入一次即可訪問這些系統中的任何一個,無須多次登入。
Jasig CAS單點登入系統分為伺服器端和客戶端,伺服器端提供單點登入,多個客戶端(子系統)將跳轉到該伺服器進行登入驗證,大體流程如下:
1、訪問客戶端需要登入的頁面http://localhost:9080/ client/,此時會跳到單點登入伺服器https://localhost:8443/ server/login?service=https://localhost:9443/ client/cas;
原因:客戶端訪問任何地址,都會被shirofilter 攔截,shiro 攔截後發現, 應該會使用authc 進行認證 發現使用者沒有登入,所以會跳轉到 shiroFilter 配置的loginUrl 頁面
2、如果此時單點登入伺服器也沒有登入的話,會顯示登入表單頁面,輸入使用者名稱/密碼進行登入;
4、客戶端會把ticket提交給伺服器來驗證ticket是否有效;如果有效伺服器端將返回使用者身份;
解釋:這個時候 訪問/cas就會被casFileter 攔截 ,casFilter 內部建立 帶ticket 的token,然後 呼叫subject.login方法,這時候 繼承casRealm 的myrealm 進行處理。
預設認證機制 會去伺服器端進行ticket 驗證。
5、客戶端可以再根據這個使用者身份獲取如當前系統使用者/角色/許可權資訊。
伺服器端
我們使用了Jasig CAS伺服器v4.0.0-RC3版本,可以到其官方的github下載:https://github.com/Jasig/cas/tree/v4.0.0-RC3下載,然後將其cas-server-webapp模組封裝到shiro-example-chapter15-server模組中,具體請參考原始碼。
1、數字證書使用和《第十四章 SSL》一樣的數字證書,即將localhost.keystore拷貝到shiro-example-chapter15-server模組根目錄下;
2、在pom.xml中新增Jetty Maven外掛,並新增SSL支援:
Java程式碼- <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,然後新增一個賬戶:
Java程式碼- <entry key="zhang" value="123"/>
其也支援如JDBC查詢,可以自己定製;具體請參考文件。
4、mvn jetty:run啟動伺服器測試即可:
訪問https://localhost:8443/chapter15-server/login將彈出如下登入頁面:
輸入使用者名稱/密碼,如zhang/123,將顯示登入成功頁面:
到此伺服器端的簡單配置就完成了。
客戶端
1、首先使用localhost.keystore匯出數字證書(公鑰)到D:\localhost.cer
Java程式碼- keytool -export -alias localhost -file D:\localhost.cer -keystore D:\localhost.keystore
2、因為CAS client需要使用該證書進行驗證,需要將證書匯入到JDK中:
Java程式碼- 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
如果匯入失敗,可以先把security 目錄下的cacerts刪掉;
3、按照伺服器端的Jetty Maven外掛的配置方式配置Jetty外掛;
4、在shiro-example-chapter15-client模組中匯入shiro-cas依賴,具體請參考其pom.xml;
5、自定義CasRealm:
Java程式碼- 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根據CAS伺服器端返回的使用者身份獲取相應的角色/許可權資訊。
6、spring-shiro-web.xml配置:
Java程式碼- <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伺服器端地址;
casService:是當前應用CAS服務URL,即用於接收並處理登入成功後的Ticket的;
如果角色/許可權資訊是由伺服器端提供的話,我們可以直接使用CasRealm:
Java程式碼- <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:預設新增給所有CAS登入成功使用者的角色和許可權資訊;
roleAttributeNames/ permissionAttributeNames:角色屬性/許可權屬性名稱,如果使用者的角色/許可權資訊是從伺服器端返回的(即返回的CAS Principal中除了Principal之外還有如一些Attributes),此時可以使用roleAttributeNames/ permissionAttributeNames得到Attributes中的角色/許可權資料;請自行查詢CAS獲取使用者更多資訊。
Java程式碼- <bean id="casFilter" class="org.apache.shiro.cas.CasFilter">
- <property name="failureUrl" value="/casFailure.jsp"/>
- </bean>
CasFilter類似於FormAuthenticationFilter,只不過其驗證伺服器端返回的CAS Service Ticket。
Java程式碼- <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表示服務端端登入地址,登入成功後跳轉到?service引數對於的地址進行客戶端驗證及登入;
“/cas=cas”:即/cas地址是伺服器端回撥地址,使用CasFilter獲取Ticket進行登入。
7、測試,輸入http://localhost:9080/chapter15-client地址進行測試即可,可以使用如Chrome開這debug觀察網路請求的變化。
如果遇到以下異常,一般是證書匯入錯誤造成的,請嘗試重新匯入,如果還是不行,有可能是執行應用的JDK和安裝數字證書的JDK不是同一個造成的:
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