Shiro登錄中遇到了問題


Shiro登錄中遇到了問題

記錄二次開發中遇到的問題, 如果系統學習Shiro, 推薦跟我學Shrio

問題

  1. 項目是要將驗證從本地改為LDAP驗證, 但是因為jeecms的驗證和授權中, 用戶和角色以及權限的信息都來自本地, 大量的改造不適合.
  2. 域名的攔截, 因為IP被重置

HTTP 302

HTTP 302 Found 重定向狀態碼表明請求的資源被暫時的移動到了由Location 頭部指定的 URL 上。就是請求的資源被重定向到了新的地址。

要查看Filter是否對關鍵詞路徑進行了攔截。

Shiro

項目中用的jeecms, 安全驗證用的shiro,架構是Spring+SpringMVC,查看了一下web.xml,有關於shiro的filter,用的DelegatingFilterProxy類做代理,在SpringContext中配置Shiro的bean。

DelegatingFilterProxy

是對於servlet filter的代理,通過spring容器管理filter的生命周期,可以通過Spring容器注入需要的bean,以及讀取需要的配置文件。

【web.xml】

<filter>
    <filter-name>shiroFilter</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
    <filter-name>shiroFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

【shiroContext.xml】

<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
</bean>

攔截器鏈

  1. 先執行Shiro自己的Filter鏈
  2. 執行Servlet的Filter鏈

自定義攔截器

  1. 可以根據自己的需求擴展攔截器,繼承要擴展的攔截器
  2. 對相應的方法進行擴展
  3. 在配置文件中修改對應的Filter類名
// 代碼參考<跟我學shiro>
public class MyAccessControlFilter extends AccessControlFilter { protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception { System.out.println("access allowed"); return true; } protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception { System.out.println("訪問拒絕也不自己處理,繼續攔截器鏈的執行"); return true; } }
[filters]  
myFilter4=com.github.zhangkaitao.shiro.chapter8.web.filter.MyAccessControlFilter [urls] /** 代碼參考<跟我學shiro>

執行過程

  1. ShiroFilter提供Shiro的入口
  2. AdviceFilter提供AOP的風格,分為preHandler,postHandle,afterCompletion三個方法針對AOP的前后增強(預處理和進行后處理)
  3. PathMatchingFilter匹配ANT風格的請求路徑,解析攔截器參數
    1. onPreHandle方法將路徑綁定參數配置傳給mappedValue,然后進行一些驗證
  4. AccessControlFilter訪問控制功能,如是否允許訪問,拒絕后如何處理等
    1. isAccessAllowed表示是否允許訪問
    2. onAccessDenied表示當拒絕訪問時是否處理了
  5. 如果擴展訪問控制可以繼承AccessControlFilter,如果添加通用數據可以繼承PathMatchingFilter

DelegatingSubject

shiro通過FormAuthenticationFilter來進行表單驗證,如果在驗證前要進行其他比如驗證碼的驗證,可以自定義一個繼承的子類.擴展對應的方法.

這里是對訪問控制的擴展,繼承關系為:FormAuthenticationFilter --> AuthenticatingFilter --> AuthenticationFilter --> AccessControlFilter.

可以在擴展的攔截器中重寫executeLogin方法,在里面擴展對訪問的控制

// 源碼
protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
        AuthenticationToken token = createToken(request, response); if (token == null) { String msg = "createToken method implementation returned null. A valid non-null AuthenticationToken " + "must be created in order to execute a login attempt."; throw new IllegalStateException(msg); } try { Subject subject = getSubject(request, response); subject.login(token); return onLoginSuccess(token, subject, request, response); } catch (AuthenticationException e) { return onLoginFailure(token, e, request, response); } }

這里的執行是獲取當前的subject --> 根據請求創建token -->執行subject.login()方法 --> 返回成功或者失敗的處理

login()方法執行中,會進行一系列的處理,首先是交給DelegatingSubject,后面的沒有詳細研究,最后調用realm的doGetAuthenticationInfo方法獲取身份驗證的相關信息,通過SimpleAuthenticationInf返回AuthenticationInfo.

SimpleAuthenticationInfo

用來返回AuthenticationInfo信息,而AuthenticationInfo有兩個作用

  1. 如果Realm是AuthenticatingRealm子類,則提供給AuthenticatingRealm內部使用的CredentialsMatcher進行憑據驗證
  2. 提供給SecurityManager來創建Subject(提供身份信息)

credentialsMatcher

protected void assertCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) throws AuthenticationException {
        CredentialsMatcher cm = getCredentialsMatcher(); if (cm != null) { if (!cm.doCredentialsMatch(token, info)) { //not successful - throw an exception to indicate this: String msg = "Submitted credentials for token [" + token + "] did not match the expected credentials."; throw new IncorrectCredentialsException(msg); } } else { throw new AuthenticationException("A CredentialsMatcher must be configured in order to verify " + "credentials during authentication. If you do not wish for credentials to be examined, you " + "can configure an " + AllowAllCredentialsMatcher.class.getName() + " instance."); } }

CredentialsMatcher實現了token和info的憑證驗證.

授權問題

關於授權, 一開始進入了一個誤區, 認為在通過驗證之后會走授權的部分, 當頁面出現授權問題的時候, 發現打過的斷點沒有進去, 仔細看了下開濤大神的講解,  是當要訪問對應的資源時, 才會驗證當前用戶的授權情況. 

如果想對資源設置權限, 可以應用Spring的AOP注解, 在對應的AnnotationController控制器(你的Controller)上加@RequiresRoles("admin"), 如果拋出異常, 可以用Spring的ExceptionHandler攔截處理.   

判斷是否已通過驗證

// 獲取到當前subject
Subject subject = SecurityUtils.getSubject();
// 判斷是否已通過驗證或者已經記住
if (subject.isAuthenticated()|| subject.isRemembered()) {
    ...
}

 

 

問題1解決方式

不改造本地驗證和授權方式, 讓ldap用戶落地, 但是不包括密碼, 每次根據user和password去判斷驗證的方式和是否新增用戶.

在繼承了AuthorizingRealm的CmsAuthorizingRealm類中修改doGetAuthenticationInfo方法即可.

 

問題2解決方式

原因

因為登陸后Cookie里面設置了域名,如果當前訪問的域名和這個Cookie的域名不一致,將無法登陸成功。

但是域名和IP本地做了映射,並且在系統中做了配置,應該不是因為域名和IP而導致的創建了不同的session,更何況session的唯一標示是Jsessionid。

但是cookie是根據域名匹配的,如果域名被修改,那么就會導致后台拿不到JSESSIONID,也就會顯示當前未登錄。

而現在系統的部署,域名是虛擬生成的,任何訪問的客戶端,本地域名都不會被看到,推斷是因為這個原因導致。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM