Shiro整合SSH開發2:結合Struts2實現登陸和退出以及Shiro執行流程和原理解析

先說明下Shiro的內置過濾器:
    
     anon(匿名)  org.apache.shiro.web.filter.authc.AnonymousFilter
     anon:例子/admins/**=anon 沒有參數,表示可以匿名使用。



     authc(身份驗證)
     org.apache.shiro.web.filter.authc.FormAuthenticationFilter
     authc:例如/admins/user/**=authc表示需要認證(登錄)才能使用,FormAuthenticationFilter是表單認證,沒有參數 



     authcBasic(http基本驗證)
     org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter
     authcBasic:例如/admins/user/**=authcBasic沒有參數表示httpBasic認證



     logout(退出)
     org.apache.shiro.web.filter.authc.LogoutFilter



     noSessionCreation(不創建session)
     org.apache.shiro.web.filter.session.NoSessionCreationFilter



     perms(許可驗證)
     org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter
     perms:例子/admins/user/**=perms[user:add:*],參數可以寫多個,多個時必須加上引號,並且參數之間用逗號分割,例如/admins/user/**=perms["user:add:*,user:modify:*"],當有多個參數時必須每個參數都通過才通過,想當於isPermitedAll()方法。



     port(端口驗證)  
     org.apache.shiro.web.filter.authz.PortFilter
     port:例子/admins/user/**=port[8081],當請求的url的端口不是8081是跳轉到schemal://serverName:8081?queryString,其中schmal是協議http或https等,serverName是你訪問的host,8081是url配置裏port的端口,queryString
     是你訪問的url裏的?後面的參數。



     rest  (rest方面) 
     org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter
     rest:例子/admins/user/**=rest[user],根據請求的方法,相當於/admins/user/**=perms[user:method] ,其中method爲post,get,delete等。



     roles(權限驗證) 
     org.apache.shiro.web.filter.authz.RolesAuthorizationFilter
     roles:例子/admins/user/**=roles[admin],【表示必須具有admin參數纔可以訪問】參數可以寫多個,多個時必須加上引號,並且參數之間用逗號分割,當有多個參數時,例如admins/user/**=roles["admin,guest"],每個參數通過纔算通過,相當於hasAllRoles()方法。



     ssl (ssl方面)  
     org.apache.shiro.web.filter.authz.SslFilter
     ssl:例子/admins/user/**=ssl沒有參數,表示安全的url請求,協議爲https



     user (用戶方面) 
     org.apache.shiro.web.filter.authc.UserFilter
     user:例如/admins/user/**=user沒有參數表示必須存在用戶, 身份認證通過或通過記住我認證通過的可以訪問,當登入操作時不做檢查



注:
anon,authcBasic,auchc,user是認證過濾器,
perms,roles,ssl,rest,port是授權過濾器
...




此文老貓原創,轉載請加本文連接:http://blog.csdn.net/nthack5730/article/details/51124171

更多有關老貓的文章:http://blog.csdn.net/nthack5730



關於Shiro基礎整合的applicationContext-shiro.xml配置在之前的文章有說明http://blog.csdn.net/nthack5730/article/details/51002218,這裏就不詳說了,注意application-shiro.xml裏面的loginUrl的配置和下面提到的Action的請求地址需要一致

設置靜態資源的匿名訪問:
Edit
<property name="filterChainDefinitions">
    <value>
        <!-- 對靜態資源設置匿名訪問 -->
        /js/** = anon
        /css/**  = anon
        /img/**  = anon
        /fonts/**  = anon
        /scripts/**  = anon  
    </value>
</property>




登陸:
     1.使用FormAuthenticationFilter過濾器實現, 原理如下:
     當用戶沒有認證時,請求loginurl進行認證【在applicationContext-shiro.xml 配置中】,用戶身份和用戶密碼提交數據到loginurl
     從表單提交認證的request中被FormAuthenticationFilter攔截住,取出request中的username和password【這兩個參數的名稱是可以配置的】。
     FormAuthenticationFilter調用realm傳入一個token(username和password【從request中提取的】)
     realm認證時,根據username查詢用戶的信息【包括usercode】
     如果查詢不到,realm返回null,FormAuthenticationFilter向request域中填充參數(記錄了異常信息)



     2.修改頁面,表單提交申請爲post
     由於FormAuthenticationFilter的取的是request中的username和password,所以  
     需要修改表單中的“登陸用戶”和“密碼”的input的name爲"username"和"password"
     我就出了一個錯誤:一定要用【post】,shiro才能攔截你的請求,否則你的請求會被跳過而讓struts2來處理!
     【我用get是爲了看URL接口數據的,沒想到這個弱智的問題讓我對着shiro調了半天!在FormAuthenticationFilter 源碼中可以查找到,FormAuthenticationFilter 需要是POST方法纔會執行認證操作】
Edit
/**
 * This default implementation merely returns <code>true</code> if the request is an HTTP <code>POST</code>,
 * <code>false</code> otherwise. Can be overridden by subclasses for custom login submission detection behavior.
 *
 * @param request  the incoming ServletRequest
 * @param response the outgoing ServletResponse.
 * @return <code>true</code> if the request is an HTTP <code>POST</code>, <code>false</code> otherwise.
 */

@SuppressWarnings({"UnusedDeclaration"})
protected boolean isLoginSubmission(ServletRequest request, ServletResponse response) {
    return (request instanceof HttpServletRequest) && WebUtils.toHttp(request).getMethod().equalsIgnoreCase(POST_METHOD);
}

此文老貓原創,轉載請加本文連接:http://blog.csdn.net/nthack5730/article/details/51124171

更多有關老貓的文章:http://blog.csdn.net/nthack5730



     下面po出登陸頁面的表單代碼:【有h5基礎的可以自行化簡,action可以留空,表示提交到本頁面(bean文件中loginUrl裏面的地址)】
<form action="${pageContext.request.contextPath }/login.action" method="post">
    <div>
        <input type="text" name="username" class="username"
            placeholder="用戶名 / UID" autocomplete="off" required/>
    </div>
    <div>
        <input type="password" name="password" class="password"
            placeholder="密碼" oncontextmenu="return false"
            onpaste="return false" required/>
    </div>
    <button id="submit" type="submit" class="btn btn-success btn-block loginbtn">登陸</button>
</form>



     3.配置Action以及對應的struts配置文件
     Shiro的功能是可以對在applicationContext-shiro.xml中的loginUrl裏面配置好的action地址進行自動攔截登陸表單發送過來的request並取出在request中的username和password進行驗證【也就是說Action的登陸請求是和Shiro關聯的,Shiro在登陸認證的時候也只是對這個loginUrl進行攔截(如果有更加高級的用法以後學到就會給大家進一步的說明)】。
     Shiro將攔截到的username和password交給realm進行自定義的認證和授權,並且將授權的信息交給shiro的緩存管理器進行管理。

     Shiro源碼在FormAuthenticationFilter 中創建token方法:
Edit
protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) {
    String username = getUsername(request);
    String password = getPassword(request);
    return createToken(username, password, request, response);
}
     上面的createToken調用了另一個抽象類裏面的方法,返回一個AuthenticationToken。。。。blahblahblah,最後扔給realm處理。

     登陸的Action是爲了讓服務器跳轉到登陸的頁面讓用戶登陸(如果是首頁即要登陸可以不寫)。至於Action的寫法不變,Action只需要處理好Shiro在登陸失敗的時候返回的異常信息即可,以前寫好了Action的登陸操作就可以暫時屏蔽掉。也就是說,如果不需要處理異常信息,可以使用動態方法調用,連Action都省掉了。不過爲了登陸反饋信息全面,還是建議大家寫的。下面會給大家一步步說明【暫時不說動態方法調用】。
     其實說那麼多就是想把我自己對Shiro和SSH整合後認證和授權的流程的理解用自己的話語給大家理順一遍,我也是新手,所以我覺得有必要在學習的過程中進行一步步的總結。希望和大家一起進步。

     好了,廢話不多說,下面給出我登陸的Action代碼:【簡化版的Action,不對返回信息作任何處理的,在下面會給出處理信息版的】
public String login() {
    return  "login";
}
    
      還需要對Action進行Controller的註解支持:
@Controller("LoginAction")
@Scope("prototype")
    
     好了,下面給出我的Action對應的Struts配置文件:
<!-- user名空間 -->
<package name="user" namespace="/user" extends="struts-default"> <!-- 登陸提交的地址,和applicationContext-shiro.xml中配置的loginurl一致 -->
<action name="login" class="com.my.action.LoginAction" method="login">
<result name="login">/WEB-INF/jsp/login.jsp</result>
</action>
</package>
 
     爲什麼這樣配置呢,其實很簡單,在上面也說了原理,在這裏再說一次吧(以本項目爲例):
     當你提交表單到/user/login.action的時候,FormAuthenticationFilter攔截了你提交的請求。
     然後FormAuthenticationFilter提取出request中的username和password字段,經過一系列的封裝操作封裝爲Token交給realm。
     之後realm根據你自定義的認證查詢流程進行認證,並將認證結果返回給FormAuthenticationFilter 。
     如果認證失敗,FormAuthenticationFilter將認證信息填充到request的“shiroLoginFailure”中;
     如果認證成功,FormAuthenticationFilter會讓瀏覽器重新訪問上一個路徑(關於“上一個路徑”上面有解釋)

     那麼我們需要處理信息的話,Action就這麼寫:
public String login() throws Exception {
    //從request中獲取FormAuthenticationFilter填充的異常信息,就是ShiroLoginFailure的全限定名
    String exceptionClassName = (String) request.get("shiroLoginFailure");

    //根據Shiro返回的異常類信息判斷,拋出並處理這個異常信息
    if (UnknownAccountException.class.getName().equals(exceptionClassName)) {
        error = "用戶不存在,請覈對用戶名";//如果UnknownAccountException拋出這個異常,表示賬號不存在

    } else if (IncorrectCredentialsException.class.getName().equals(
            exceptionClassName)) {
        error = "用戶名/密碼錯誤";
    } else if (exceptionClassName != null) {
        error = "其他錯誤:" + exceptionClassName;
    } 

    //此方法不處理登陸成功,shiro認證成功會跳轉到上一個路徑 
    //登陸失敗,還到login頁面
    return "login";
}

     上面主要是通過“shiroLoginFailure ”返回的String字段和異常類的名字作比較,根據不同的異常類做出相應的處理。
     UnknownAccountException:表示用戶不存在。
     IncorrectCredentialsException:表示用戶能找到但是密碼錯誤。
     好了,登陸需要對應處理的Struts的Action和配置文件代碼已經都寫完了。如果大家在配置過程中出現什麼問題,可能是某些細節方面沒有注意好。特別是在配置shiro和spring的bean配置文件中的loginUrl一定要和Struts2裏面的登陸頁面的Action匹配。

注:我的Action中的request對象是通過BaseAction類實現RequestAware接口來得到的。BaseAction部分代碼如下:
public class BaseAction<Textends ActionSupport implements RequestAware,
        SessionAwareApplicationAwareModelDriven<T
{
    /**
     * 
     */

    private static final long serialVersionUID = 1L;
    protected T model; // 這裏使用protected是爲了可以封裝 也可以繼承

    public Map<String, Object> application;
    public Map<String, Object> request;
    public Map<String, Object> session;
    ...
}

此文老貓原創,轉載請加本文連接:http://blog.csdn.net/nthack5730/article/details/51124171

更多有關老貓的文章:http://blog.csdn.net/nthack5730





     4.配置認證攔截過濾器
     在applicationContext-shiro.xml中配置:【包括靜態資源的匿名訪問】
Edit
<property name="filterChainDefinitions">
    <value>
        <!-- 對靜態資源設置匿名訪問 -->
        /js/** = anon
        /css/**  = anon
        /img/**  = anon
        /fonts/**  = anon
        /scripts/**  = anon

        <!-- /** = authc 表示所有URL都必須認證纔可以通過訪問 -->
        /** = authc 

    </value>
</property>


此文老貓原創,轉載請加本文連接:http://blog.csdn.net/nthack5730/article/details/51124171

更多有關老貓的文章:http://blog.csdn.net/nthack5730




退出:
     不用我們去實現退出,只要去訪問一個退出的url(該url可以不存在),由LogoutFilter攔截住,清除session
     在applicationContext-shiro.xml中配置:【在上邊加入】
<!-- 請求logout.action地址,shiro去清除session -->
/logout.action = logout

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章