天天看點

從Shiro-cas切換pac4j

從Shiro-cas切換pac4j

  • ​​開發​​

1060

切換原因

在​

​shiro-1.3.x​

​以後的版本中,​

​shiro-cas​

​包裡面的所有類都被辨別為​

​deprecated​

​,詳細:​​https://github.com/apache/shiro/pull/33​​。

個人認為不繼續維護​

​shiro-cas​

​而切換到​

​pac4j​

​主要有以下幾點原因:​

​shiro-cas​

  1. 非常的不靈活,比如在

​CasFilter​

​onLoginSuccess(..)​

  1. 登入成功事件,就隻考慮到了通過cas跳轉過來的情況,直接就執行了

​issueSuccessRedirect(..)​

  1. 跳轉到儲存的請求位址,并沒有考慮到如果是ajax請求該如何處理。
  2. 認證協定太多,如果需要擴充一個微網誌登入(oauth2)那麼還得加一個

​shiro-oauth​

  1. ?pac4j原生就支援很多的認證協定 OAuth (Facebook, Twitter, Google…) - SAML - CAS - OpenID Connect - HTTP - OpenID - Google App Engine LDAP - SQL - JWT - MongoDB - Stormpath - IP address

pac4j介紹

​​pac4j​​定位是一個java安全引擎(Java security engine),是以不止包含支援各種認證協定進行登入,同時也支援 角色權限的管理、記住登入、CORS、CSRF、輸出Http安全頭 等功能,并且可以和很多著名的架構結合,比如官方列舉的 J2E、Spring Web MVC (Spring Boot)、Spring Security (Spring Boot)、Shiro、Play 2.x、Vertx、Spark Java、Ratpack、Undertow、CAS server、Dropwizard、Knox、Jooby

由于我們的項目使用的​

​Apache shiro​

​,并且也經過了很長時間的開發和對shiro的擴充,是以如果沒有特别大的需求,不會去替換掉​

​shiro​

​架構,是以采用了​​buji-cas4j​​​來提供shiro和pac4j的結合。pac4j大量用到了java8的特性編寫,是以最低配置要求​

​java8​

​以及​

​shiro 1.3.x​

​+。

替換shiro-cas

引入依賴

  1. 移除shiro-cas的依賴
  2. 加入

​pac4j-cas​

  1. 依賴(目前我們使用的​​1.9.2​​),自身已依賴的

​pac4j-core​

  1. 無需再自己添加
  2. 加入

​buji-pac4j​

  1. 依賴(目前我們使用的​​2.0.2​​)

去掉shiro-cas相關bean

各自對shiro-cas擴充程度不同,基本上去掉依賴以後所有ide報錯的地方都需要改,這裡就記錄一下我們項目中修改的東西:

  1. 自定義的Realm不再繼承自CasRealm,修改為

​Pac4jRealm​

  1. 去掉CasFilter過濾器,改用buji-pac4j提供的

​CallbackFilter​

  1. (下面會介紹如果配置這個bean)
  2. 将CasSubjectFactory修改為

​Pac4jsubjectFactory​

配置pac4j-cas

  1. 首先定義

​CasConfiguration(loginUrl,prefixUrl)​

  1. ,loginUrl為完整的cas登入位址,比如client項目的

​https://passport.sqzryang.com/login?service=https://client.sqzryang.com​

  1. ,prefixUrl則為cas路徑字首,根據cas的版本号拼接請求位址,用于驗證sts是否正确并且傳回登入成功後的資訊。
  2. 定義

​CasClient​

  1. ,property

​configuration(CasConfiguration)​

  1. and

​callbackUrl(String)​

  1. ,在pac4j中,每一個client相當于一種認證協定,比如我們需要weibo登入則應該配置一個

​WeiboClient​

  1. ,具體回掉的時候應該采用哪個client進行驗證授權則需要下面配置的

​Clients​

  1. 定義

​Clients​

  1. ,在這裡面可以定義你所有的Client以及預設的client還有關于如何區分回掉的哪個client應該取某個參數的配置,具體詳細看源碼
  2. 定義

​Config​

  1. ,在config裡面還有關于權限方面的配置以及session存儲的一些配置,由于這部分我交給shiro去管理,是以隻傳入了

​clients​

  1. 即可
  2. 以上四個都是

​pac4j​

  1. 的配置,接下來配置一個由

​buji-pac4j​

  1. 提供用于和shiro結合的filter:

​CallbackFilter​

  1. ,直接傳入

​config​

  1. 即可
  2. 定義好CallbackFilter以後,在

​ShiroFilterFactoryBean​

  1. 中注冊好

​filters​

  1. ,并且配置好

​filterChainDefinitions​

具體各個bean的一些基本概念​​Main concepts and components​​,一個稍微全面點的配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40      
<bean id="casClientConfiguration" class="org.pac4j.cas.config.CasConfiguration">
"loginUrl" value="${shiro.loginUrl}"/>
"prefixUrl" value="${shiro.casServerUrlPrefix}"/>
</bean>

<bean id="casClient" class="org.pac4j.cas.client.CasClient">
"configuration" ref="casClientConfiguration"/>
"callbackUrl" value="${shiro.casService}"/>
</bean>

<bean id="casClients" class=" org.pac4j.core.client.Clients">
"clients">
        <util:list>
ref bean="casClient"/>
        </util:list>
    </property>
"defaultClient" ref="casClient"/>
</bean>

<bean id="casConfig" class="org.pac4j.core.config.Config">
"clients" ref="casClients"/>
</bean>

<bean name="casCallbackFilter" class="io.buji.pac4j.filter.CallbackFilter">
"config" ref="casConfig"/>
</bean>

<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"
ref="securityManager"
"/cas = cas\n/logout = logout"
"${shiro.loginUrl}"
"${shiro.successUrl}">
"filters">
        <util:map>
            <!-- ... -->
"cas" value-ref="casCallbackFilter"/> <!-- 注冊filter -->
            <!-- ... -->
        </util:map>
    </property>
</bean>      

其他修改地方

  • 登入後 Principal 為

​Pac4jPrincipal​

  • 對象,擷取cas傳遞回來的username,通過:

​String username = pac4jPrincipal.getProfile().getId();​

  • 如果開啟了緩存,應重寫權限緩存以及認證緩存的key值,在

​AuthorizingRealm​

  • 中的

​getAuthorizationCacheKey​

  • 以及

​getAuthenticationCacheKey​

  • ,推薦使用username來作為緩存key
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25      
@Override
protected Object getAuthorizationCacheKey(PrincipalCollection principals){
    Pac4jPrincipal pac4jPrincipal = (Pac4jPrincipal) principals.getPrimaryPrincipal();
return
}

@Override
protected Object getAuthenticationCacheKey(AuthenticationToken token){
if (token instanceof
        Pac4jToken pac4jToken = (Pac4jToken) token;
        Object principal = pac4jToken.getPrincipal();
if (principal instanceof
@SuppressWarnings("unchecked") Optional<CasProfile> casProfileOptional = (Optional<CasProfile>) principal;
return
        }
    }
return super.getAuthenticationCacheKey(token);
}      
  • 通過預設的

​CallbackFilter​

  • 登入成功以後,會直接

​redirectToOriginallyRequestedUrl​

  • ,但是在pac4j裡面沒有再去讀取被shiro userfilter檢測到未登入後存在session中的

​SavedRequest​

  • ,而是讀取
  • ,是以重寫UserFilter中的
  • 适配pac4j
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25      
public class UserFilter extends org.apache.shiro.web.filter.authc.UserFilter{


@Override
protected void saveRequest(ServletRequest request){
// 還是先執行着shiro自己的方法
super.saveRequest(request);
        Session session = SecurityUtils.getSubject().getSession();
        session.setAttribute(Pac4jConstants.REQUESTED_URL, toHttp(request).getRequestURI());
    }


}      

最後

參考資料

  • ​​https://github.com/bujiio/buji-pac4j​​
  • ​​http://www.pac4j.org/docs/index.html​​