1. 程式人生 > >Shiro 第二十三章 多專案集中許可權管理及分散式會話

Shiro 第二十三章 多專案集中許可權管理及分散式會話

在做一些企業內部專案時或一些網際網路後臺時;可能會涉及到集中許可權管理,統一進行多專案的許可權管理;另外也需要統一的會話管理,即實現單點身份認證和授權控制。

學習本章之前,請務必先學習《第十章 會話管理》和《第十六章 綜合例項》,本章程式碼都是基於這兩章的程式碼基礎上完成的。

本章示例是同域名的場景下完成的,如果跨域請參考《第十五章 單點登入》和《第十七章 OAuth2整合》瞭解使用CAS或OAuth2實現跨域的身份驗證和授權。另外比如客戶端/伺服器端的安全校驗可參考《第二十章 無狀態Web應用整合》。

部署架構 

1、有三個應用:用於使用者/許可權控制的Server(埠:8080);兩個應用App1(埠9080)和App2(埠10080);

2、使用Nginx反向代理這三個應用,nginx.conf的server配置部分如下:  

Java程式碼  收藏程式碼
  1. server {  
  2.     listen 80;  
  3.     server_name  localhost;  
  4.     charset utf-8;  
  5.     location ~ ^/(chapter23-server)/ {  
  6.         proxy_pass http://127.0.0.1:8080;   
  7.         index /;  
  8.             proxy_set_header Host $host;  
  9.     }  
  10.     location ~ ^/(chapter23-app1)/ {  
  11.         proxy_pass http://127.0.0.1:9080;   
  12.         index /;  
  13.             proxy_set_header Host $host;  
  14.     }  
  15.     location ~ ^/(chapter23-app2)/ {  
  16.         proxy_pass http://127.0.0.1:10080;   
  17.         index /;  
  18.             proxy_set_header Host $host;  
  19.     }  
  20. }   

Nginx的安裝及使用請自行搜尋學習,本文不再闡述。 

專案架構 

1、首先通過使用者/許可權Server維護使用者、應用、許可權資訊;資料都持久化到MySQL資料庫中;

2、應用App1/應用App2使用客戶端Client遠端呼叫使用者/許可權Server獲取會話及許可權資訊。

此處使用Mysql儲存會話,而不是使用如Memcached/Redis之類的,主要目的是降低學習成本;如果換成如Redis也不會很難;如: 


使用如Redis還一個好處就是無需在使用者/許可權Server中開會話過期排程器,可以藉助Redis自身的過期策略來完成。

模組關係依賴




  

1、shiro-example-chapter23-pom模組:提供了其他所有模組的依賴;這樣其他模組直接繼承它即可,簡化依賴配置,如shiro-example-chapter23-server:  

Java程式碼  收藏程式碼
  1. <parent>  
  2.     <artifactId>shiro-example-chapter23-pom</artifactId>  
  3.     <groupId>com.github.zhangkaitao</groupId>  
  4.     <version>1.0-SNAPSHOT</version>  
  5. </parent>  

2、shiro-example-chapter23-core模組:提供給shiro-example-chapter23-server、shiro-example-chapter23-client、shiro-example-chapter23-app*模組的核心依賴,比如遠端呼叫介面等;

3、shiro-example-chapter23-server模組:提供了使用者、應用、許可權管理功能;

4、shiro-example-chapter23-client模組:提供給應用模組獲取會話及應用對應的許可權資訊;

5、shiro-example-chapter23-app*模組:各個子應用,如一些內部管理系統應用;其登入都跳到shiro-example-chapter23-server登入;另外許可權都從shiro-example-chapter23-server獲取(如通過遠端呼叫)。  

shiro-example-chapter23-pom模組

其pom.xml的packaging型別為pom,並且在該pom中加入其他模組需要的依賴,然後其他模組只需要把該模組設定為parent即可自動繼承這些依賴,如shiro-example-chapter23-server模組: 

Java程式碼  收藏程式碼
  1. <parent>  
  2.     <artifactId>shiro-example-chapter23-pom</artifactId>  
  3.     <groupId>com.github.zhangkaitao</groupId>  
  4.     <version>1.0-SNAPSHOT</version>  
  5. </parent>   

簡化其他模組的依賴配置等。 

shiro-example-chapter23-core模組

提供了其他模組共有的依賴,如遠端呼叫介面:  

Java程式碼  收藏程式碼
  1. public interface RemoteServiceInterface {  
  2.     public Session getSession(String appKey, Serializable sessionId);  
  3.     Serializable createSession(Session session);  
  4.     public void updateSession(String appKey, Session session);  
  5.     public void deleteSession(String appKey, Session session);  
  6.     public PermissionContext getPermissions(String appKey, String username);  
  7. }   

提供了會話的CRUD,及根據應用key和使用者名稱獲取許可權上下文(包括角色和許可權字串);shiro-example-chapter23-server模組服務端實現;shiro-example-chapter23-client模組客戶端呼叫。

另外提供了com.github.zhangkaitao.shiro.chapter23.core.ClientSavedRequest,其擴充套件了org.apache.shiro.web.util.SavedRequest;用於shiro-example-chapter23-app*模組當訪問一些需要登入的請求時,自動把請求儲存下來,然後重定向到shiro-example-chapter23-server模組登入;登入成功後再重定向回來;因為SavedRequest不儲存URL中的schema://domain:port部分;所以才需要擴充套件SavedRequest;使得ClientSavedRequest能儲存schema://domain:port;這樣才能從一個應用重定向另一個(要不然只能在一個應用內重定向):  

Java程式碼  收藏程式碼
  1.     public String getRequestUrl() {  
  2.         String requestURI = getRequestURI();  
  3.         if(backUrl != null) {//1  
  4.             if(backUrl.toLowerCase().startsWith("http://") || backUrl.toLowerCase().startsWith("https://")) {  
  5.                 return backUrl;  
  6.             } else if(!backUrl.startsWith(contextPath)) {//2  
  7.                 requestURI = contextPath + backUrl;  
  8.             } else {//3  
  9.                 requestURI = backUrl;  
  10.             }  
  11.         }  
  12.         StringBuilder requestUrl = new StringBuilder(scheme);//4  
  13.         requestUrl.append("://");  
  14.         requestUrl.append(domain);//5  
  15.         //6  
  16.         if("http".equalsIgnoreCase(scheme) && port != 80) {  
  17.             requestUrl.append(":").append(String.valueOf(port));  
  18.         } else if("https".equalsIgnoreCase(scheme) && port != 443) {  
  19.             requestUrl.append(":").append(String.valueOf(port));  
  20.         }  
  21.         //7  
  22.         requestUrl.append(requestURI);  
  23.         //8  
  24.         if (backUrl == null && getQueryString() != null) {  
  25.             requestUrl.append("?").append(getQueryString());  
  26.         }  
  27.         return requestUrl.toString();  
  28.     }  

1、如果從外部傳入了successUrl(登入成功之後重定向的地址),且以http://或https://開頭那麼直接返回(相應的攔截器直接重定向到它即可);

2、如果successUrl有值但沒有上下文,拼上上下文;

3、否則,如果successUrl有值,直接賦值給requestUrl即可;否則,如果successUrl沒值,那麼requestUrl就是當前請求的地址;

5、拼上url前邊的schema,如http或https;

6、拼上域名;

7、拼上重定向到的地址(帶上下文);

8、如果successUrl沒值,且有查詢引數,拼上;

9返回該地址,相應的攔截器直接重定向到它即可。

shiro-example-chapter23-server模組

簡單的實體關係圖  


簡單資料字典

使用者(sys_user)

名稱

型別

長度

描述

id

bigint

編號 主鍵

username

varchar

100

使用者名稱

password

varchar

100

密碼

salt

varchar

50

locked

bool

賬戶是否鎖定

應用(sys_app)

名稱

型別

長度

描述

id

bigint

編號 主鍵

name

varchar

100

應用名稱

app_key

varchar

100

應用key(唯一)

app_secret

varchar

100

應用安全碼

available

bool

是否鎖定

授權(sys_authorization)

名稱

型別

長度

描述

id

bigint

編號 主鍵

user_id

bigint

所屬使用者

app_id

bigint

所屬應用

role_ids

varchar

100

角色列表

使用者:比《第十六章 綜合例項》少了role_ids,因為本章是多專案集中許可權管理;所以授權時需要指定相應的應用;而不是直接給使用者授權;所以不能在使用者中出現role_ids了;

應用:所有集中許可權的應用;在此處需要指定應用key(app_key)和應用安全碼(app_secret),app在訪問server時需要指定自己的app_key和使用者名稱來獲取該app對應使用者許可權資訊;另外app_secret可以認為app的密碼,比如需要安全訪問時可以考慮使用它,可參考《第二十章 無狀態Web應用整合》。另外available屬性表示該應用當前是否開啟;如果false表示該應用當前不可用,即不能獲取到相應的許可權資訊。

授權:給指定的使用者在指定的app下授權,即角色是與使用者和app存在關聯關係。

因為本章使用了《第十六章 綜合例項》程式碼,所以還有其他相應的表結構(本章未使用到)。

表/資料SQL

具體請參考

sql/ shiro-schema.sql (表結構)

sql/ shiro-data.sql  (初始資料)

實體

具體請參考com.github.zhangkaitao.shiro.chapter23.entity包下的實體,此處就不列舉了。

DAO

具體請參考com.github.zhangkaitao.shiro.chapter23.dao包下的DAO介面及實現。

Service

具體請參考com.github.zhangkaitao.shiro.chapter23.service包下的Service介面及實現。以下是出了基本CRUD之外的關鍵介面: 

Java程式碼  收藏程式碼
  1. public interface AppService {  
  2.     public Long findAppIdByAppKey(String appKey);// 根據appKey查詢AppId   
  3. }  
Java程式碼  收藏程式碼
  1. public interface AuthorizationService {  
  2.     //根據AppKey和使用者名稱查詢其角色  
  3.     public Set<String> findRoles(String appKey, String username);  
  4.     //根據AppKey和使用者名稱查詢許可權字串  
  5.     public Set<String> findPermissions(String appKey, String username);  
  6. }   

根據AppKey和使用者名稱查詢使用者在指定應用中對於的角色和許可權字串。

UserRealm  

Java程式碼  收藏程式碼
  1. protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {  
  2.     String username = (String)principals.getPrimaryPrincipal();  
  3.     SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();  
  4.     authorizationInfo.setRoles(  
  5.         authorizationService.findRoles(Constants.SERVER_APP_KEY, username));  
  6.     authorizationInfo.setStringPermissions(  
  7.     authorizationService.findPermissions(Constants.SERVER_APP_KEY, username));  
  8.     return authorizationInfo;  
  9. }   

此處需要呼叫AuthorizationService的findRoles/findPermissions方法傳入AppKey和使用者名稱來獲取使用者的角色和許可權字串集合。其他的和《第十六章 綜合例項》程式碼一樣。

ServerFormAuthenticationFilter 

Java程式碼  收藏程式碼
  1. public class ServerFormAuthenticationFilter extends FormAuthenticationFilter {  
  2.     protected void issueSuccessRedirect(ServletRequest request, ServletResponse response) throws Exception {  
  3.         String fallbackUrl = (String) getSubject(request, response)  
  4.                 .getSession().getAttribute("authc.fallbackUrl");  
  5.         if(StringUtils.isEmpty(fallbackUrl)) {  
  6.             fallbackUrl = getSuccessUrl();  
  7.         }  
  8.         WebUtils.redirectToSavedRequest(request, response, fallbackUrl);  
  9.     }  
  10. }   

因為是多專案登入,比如如果是從其他應用中重定向過來的,首先檢查Session中是否有“authc.fallbackUrl”屬性,如果有就認為它是預設的重定向地址;否則使用Server自己的successUrl作為登入成功後重定向到的地址。

MySqlSessionDAO

將會話持久化到Mysql資料庫;此處大家可以將其實現為如儲存到Redis/Memcached等,實現策略請參考《第十章 會話管理》中的會話儲存/持久化章節的MySessionDAO,完全一樣。

MySqlSessionValidationScheduler

和《第十章 會話管理》中的會話驗證章節部分中的MySessionValidationScheduler完全一樣。如果使用如Redis之類的有自動過期策略的DB,完全可以不用實現SessionValidationScheduler,直接藉助於這些DB的過期策略即可。

RemoteService  

Java程式碼  收藏程式碼
  1. public class RemoteService implements RemoteServiceInterface {  
  2.     @Autowired  private AuthorizationService authorizationService;  
  3.     @Autowired  private SessionDAO sessionDAO;  
  4.     public Session getSession(String appKey, Serializable sessionId) {  
  5.         return sessionDAO.readSession(sessionId);  
  6.     }  
  7.     public Serializable createSession(Session session) {  
  8.         return sessionDAO.create(session);  
  9.     }  
  10.     public void updateSession(String appKey, Session session) {  
  11.         sessionDAO.update(session);  
  12.     }  
  13.     public void deleteSession(String appKey, Session session) {  
  14.         sessionDAO.delete(session);  
  15.     }  
  16.     public PermissionContext getPermissions(String appKey, String username) {  
  17.         PermissionContext permissionContext = new PermissionContext();  
  18.         permissionContext.setRoles(authorizationService.findRoles(appKey, username));  
  19.         permissionContext.setPermissions(authorizationService.findPermissions(appKey, username));  
  20.         return permissionContext;  
  21.     }  
  22. }   

將會使用HTTP呼叫器暴露為遠端服務,這樣其他應用就可以使用相應的客戶端呼叫這些介面進行Session的集中維護及根據AppKey和使用者名稱獲取角色/許可權字串集合。此處沒有實現安全校驗功能,如果是區域網內使用可以通過限定IP完成;否則需要使用如《第二十章 無狀態Web應用整合》中的技術完成安全校驗。

然後在spring-mvc-remote-service.xml配置檔案把服務暴露出去:  

Java程式碼  收藏程式碼
  1. <bean id="remoteService"  
  2.   class="com.github.zhangkaitao.shiro.chapter23.remote.RemoteService"/>  
  3. <bean name="/remoteService"   
  4.   class="org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter">  
  5.     <property name="service" ref="remoteService"/>  
  6.     <property name="serviceInterface"   
  7.       value="com.github.zhangkaitao.shiro.chapter23.remote.RemoteServiceInterface"/>  
  8. </bean>  

Shiro配置檔案spring-config-shiro.xml 

和《第十六章 綜合例項》配置類似,但是需要在shiroFilter中的filterChainDefinitions中新增如下配置,即遠端呼叫不需要身份認證:  

Java程式碼  收藏程式碼
  1. /remoteService = anon  

對於userRealm的快取配置直接禁用;因為如果開啟,修改了使用者許可權不會自動同步到快取;另外請參考《第十一章 快取機制》進行快取的正確配置。

伺服器端資料維護

2、輸入預設的使用者名稱密碼:admin/123456登入

3、應用管理,進行應用的CRUD,主要維護應用KEY(必須唯一)及應用安全碼;客戶端就可以使用應用KEY獲取使用者對應應用的許可權了。 


4、授權管理,維護在哪個應用中使用者的角色列表。這樣客戶端就可以根據應用KEY及使用者名稱獲取到對應的角色/許可權字串列表了。 



shiro-example-chapter23-client模組

Client模組提供給其他應用模組依賴,這樣其他應用模組只需要依賴Client模組,然後再在相應的配置檔案中配置如登入地址、遠端介面地址、攔截器鏈等等即可,簡化其他應用模組的配置。

配置遠端服務spring-client-remote-service.xml      

Java程式碼  收藏程式碼
  1. <bean id="remoteService"   
  2.   class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">  
  3.     <property name="serviceUrl" value="${client.remote.service.url}"/>  
  4.     <property name="serviceInterface"   
  5.       value="com.github.zhangkaitao.shiro.chapter23.remote.RemoteServiceInterface"/>  
  6. </bean>   

client.remote.service.url是遠端服務暴露的地址;通過相應的properties配置檔案配置,後續介紹。然後就可以通過remoteService獲取會話及角色/許可權字串集合了。

ClientRealm  

Java程式碼  收藏程式碼
  1. public class ClientRealm extends AuthorizingRealm {  
  2.     private RemoteServiceInterface remoteService;  
  3.     private String appKey;  
  4.     public void setRemoteService(RemoteServiceInterface remoteService) {  
  5.         this.remoteService = remoteService;  
  6.     }  
  7.     public void setAppKey(String appKey) {  
  8.         this.appKey = appKey;  
  9.     }  
  10.     protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {  
  11.         String username = (String) principals.getPrimaryPrincipal();  
  12.         SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();  
  13.         PermissionContext context = remoteService.getPermissions(appKey, username);  
  14.         authorizationInfo.setRoles(context.getRoles());  
  15.         authorizationInfo.setStringPermissions(context.getPermissions());  
  16.         return authorizationInfo;  
  17.     }  
  18.     protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {  
  19.         //永遠不會被呼叫  
  20.         throw new UnsupportedOperationException("永遠不會被呼叫");  
  21.     }  
  22. }   

ClientRealm提供身份認證資訊和授權資訊,此處因為是其他應用依賴客戶端,而這些應用不會實現身份認證,所以doGetAuthenticationInfo獲取身份認證資訊直接無須實現。另外獲取授權資訊,是通過遠端暴露的服務RemoteServiceInterface獲取,提供appKey和使用者名稱獲取即可。

ClientSessionDAO 

Java程式碼  收藏程式碼
  1. public class ClientSessionDAO extends CachingSessionDAO {  
  2.     private RemoteServiceInterface remoteService;  
  3.     private String appKey;  
  4.     public void setRemoteService(RemoteServiceInterface remoteService) {  
  5.         this.remoteService = remoteService;  
  6.     }  
  7.     public void setAppKey(String appKey) {  
  8.         this.appKey = appKey;  
  9.     }  
  10.     protected void doDelete(Session session) {  
  11.         remoteService.deleteSession(appKey, session);  
  12.     }  
  13.     protected void doUpdate(Session session) {  
  14.         remoteService.updateSession(appKey, session);  
  15. }  
  16. protected Serializable doCreate(Session session) {  
  17.         Serializable sessionId = remoteService.createSession(session);  
  18.         assignSessionId(session, sessionId);  
  19.         return sessionId;  
  20.     }  
  21.     protected Session doReadSession(Serializable sessionId) {  
  22.         return remoteService.getSession(appKey, sessionId);  
  23.     }  
  24. }   

Session的維護通過遠端暴露介面實現,即本地不維護會話。

ClientAuthenticationFilter  

Java程式碼  收藏程式碼
  1. public class ClientAuthenticationFilter extends AuthenticationFilter {  
  2.     protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {  
  3.         Subject subject = getSubject(request, response);  
  4.         return subject.isAuthenticated();  
  5.     }  
  6.     protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {  
  7.         String backUrl = request.getParameter("backUrl");  
  8.         saveRequest(request, backUrl, getDefaultBackUrl(WebUtils.toHttp(request)));  
  9.         return false;  
  10.     }  
  11.     protected void saveRequest(ServletRequest request, String backUrl, String fallbackUrl) {  
  12.         Subject subject = SecurityUtils.getSubject();  
  13.         Session session = subject.getSession();  
  14.         HttpServletRequest httpRequest = WebUtils.toHttp(request);  
  15.         session.setAttribute("authc.fallbackUrl", fallbackUrl);  
  16.         SavedRequest savedRequest = new ClientSavedRequest(httpRequest, backUrl);  
  17.         session.setAttribute(WebUtils.SAVED_REQUEST_KEY, savedRequest);  
  18. }  
  19.     private String getDefaultBackUrl(HttpServletRequest request) {  
  20.         String scheme = request.getScheme();  
  21.         String domain = request.getServerName();  
  22.         int port = request.getServerPort();  
  23.         String contextPath = request.getContextPath();  
  24.         StringBuilder backUrl = new StringBuilder(scheme);  
  25.         backUrl.append("://");  
  26.         backUrl.append(domain);  
  27.         if("http".equalsIgnoreCase(scheme) && port != 80) {  
  28.             backUrl.append(":").append(String.valueOf(port));  
  29.         } else if("https".equalsIgnoreCase(scheme) && port != 443) {  
  30.             backUrl.append(":").append(String.valueOf(port));  
  31.         }  
  32.         backUrl.append(contextPath);  
  33.         backUrl.append(getSuccessUrl());  
  34.         return backUrl.toString();  
  35.     }  
  36. }   

ClientAuthenticationFilter是用於實現身份認證的攔截器(authc),當用戶沒有身份認證時;

1、首先得到請求引數backUrl,即登入成功重定向到的地址;

2、然後儲存儲存請求到會話,並重定向到登入地址(server模組);

3、登入成功後,返回地址按照如下順序獲取:backUrl、儲存的當前請求地址、defaultBackUrl(即設定的successUrl);

ClientShiroFilterFactoryBean  

Java程式碼  收藏程式碼
  1. public class ClientShiroFilterFactoryBean extends ShiroFilterFactoryBean implements ApplicationContextAware {  
  2.     private ApplicationContext applicationContext;  
  3.     public void setApplicationContext(ApplicationContext applicationContext) {  
  4.         this.applicationContext = applicationContext;  
  5.     }  
  6.     public void setFiltersStr(String filters) {  
  7.         if(StringUtils.isEmpty(filters)) {  
  8.             return;  
  9.         }  
  10.         String[] filterArray = filters.split(";");  
  11.         for(String filter : filterArray) {  
  12.             String[] o = filter.split("=");  
  13.             getFilters().put(o[0], (Filter)applicationContext.getBean(o[1]));  
  14.         }  
  15.     }  
  16.     public void setFilterChainDefinitionsStr(String filterChainDefinitions) {  
  17.         if(StringUtils.isEmpty(filterChainDefinitions)) {  
  18.             return;  
  19.         }  
  20.         String[] chainDefinitionsArray = filterChainDefinitions.split(";");  
  21.         for(String filter : chainDefinitionsArray) {  
  22.             String[] o = filter.split("=");  
  23.             getFilterChainDefinitionMap().put(o[0], o[1]);  
  24.         }  
  25.     }  
  26. }   

1、setFiltersStr:設定攔截器,設定格式如“filterName=filterBeanName; filterName=filterBeanName”;多個之間分號分隔;然後通過applicationContext獲取filterBeanName對應的Bean註冊到攔截器Map中;

2、setFilterChainDefinitionsStr:設定攔截器鏈,設定格式如“url=filterName1[config],filterName2; url=filterName1[config],filterName2”;多個之間分號分隔;

Shiro客戶端配置spring-client.xml

提供了各應用通用的Shiro客戶端配置;這樣應用只需要匯入相應該配置即可完成Shiro的配置,簡化了整個配置過程。  

Java程式碼  收藏程式碼
  1. <context:property-placeholder location=   
  2.     "classpath:client/shiro-client-default.properties,classpath:client/shiro-client.properties"/>   

提供給客戶端配置的properties屬性檔案,client/shiro-client-default.properties是客戶端提供的預設的配置;classpath:client/shiro-client.properties是用於覆蓋客戶端預設配置,各應用應該提供該配置檔案,然後提供各應用個性配置。

Java程式碼  收藏程式碼
  1. <bean id="remoteRealm" class="com.github.zhangkaitao.shiro.chapter23.client.ClientRealm">  
  2.     <property name="cachingEnabled" value="false"/>  
  3.     <property name="appKey" value="${client.app.key}"/>  
  4.     <property name="remoteService" ref="remoteService"/>  
  5. </bean>   

appKey:使用${client.app.key}佔位符替換,即需要在之前的properties檔案中配置。 

Java程式碼  收藏程式碼
  1. <bean id="sessionIdCookie" class="org.apache.shiro.web.servlet.SimpleCookie">  
  2.     <constructor-arg value="${client.session.id}"/>  
  3.     <property name="httpOnly" value="true"/>  
  4.     <property name="maxAge" value="-1"/>  
  5.     <property name="domain" value="${client.cookie.domain}"/>  
  6.     <property name="path" value="${client.cookie.path}"/>  
  7. </bean>   

Session Id Cookie,cookie名字、域名、路徑等都是通過配置檔案配置。  

Java程式碼  收藏程式碼
  1. <bean id="sessionDAO"   
  2.   class="com.github.zhangkaitao.shiro.chapter23.client.ClientSessionDAO">  
  3.     <property name="sessionIdGenerator" ref="sessionIdGenerator"/>  
  4.     <property name="appKey" value="${client.app.key}"/>  
  5.     <property name="remoteService" ref="remoteService"/>  
  6. </bean>   

SessionDAO的appKey,也是通過${ client.app.key }佔位符替換,需要在配置檔案配置。

Java程式碼  收藏程式碼
  1. <bean id="sessionManager"   
  2.   class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">  
  3.         <property name="sessionValidationSchedulerEnabled" value="false"/>//省略其他  
  4. </bean>   

其他應用無須進行會話過期排程,所以sessionValidationSchedulerEnabled=false。  

Java程式碼  收藏程式碼
  1. <bean id="clientAuthenticationFilter"   
  2.   class="com.github.zhangkaitao.shiro.chapter23.client.ClientAuthenticationFilter"/>   

應用的身份認證使用ClientAuthenticationFilter,即如果沒有身份認證,則會重定向到Server模組完成身份認證,身份認證成功後再重定向回來。 

Java程式碼  收藏程式碼
  1. <bean id="shiroFilter"   
  2.   class="com.github.zhangkaitao.shiro.chapter23.client.ClientShiroFilterFactoryBean">  
  3.     <property name="securityManager" ref="securityManager"/>  
  4.     <property name="loginUrl" value="${client.login.url}"/>  
  5.     <property name="successUrl" value="${client.success.url}"/>  
  6.     <property name="unauthorizedUrl" value="${client.unauthorized.url}"/>  
  7.     <property name="filters">  
  8.         <util:map>  
  9.             <entry key="authc" value-ref="clientAuthenticationFilter"/>  
  10.         </util:map>  
  11.     </property>  
  12.     <property name="filtersStr" value="${client.filters}"/>  
  13.     <property name="filterChainDefinitionsStr" value="${client.filter.chain.definitions}"/>  
  14. </bean>   

ShiroFilter使用我們自定義的ClientShiroFilterFactoryBean,然後loginUrl(登入地址)、successUrl(登入成功後預設的重定向地址)、unauthorizedUrl(未授權重定向到的地址)通過佔位符替換方式配置;另外filtersStr和filterChainDefinitionsStr也是使用佔位符替換方式配置;這樣就可以在各應用進行自定義了。