1. 程式人生 > >shiro許可權框架詳解06-shiro與web專案整合(上)

shiro許可權框架詳解06-shiro與web專案整合(上)

shiro和web專案整合,實現類似真實專案的應用

本文中使用的專案架構是springMVC+mybatis,所以我們是基於搭建好的專案進行改造的。

  • 將shiro整合到web應用中
  • 登入
  • 退出
  • 認證資訊在頁面展現,也就是顯示選單
  • shiro的過濾器

將shiro整合到web應用中

資料庫腳步

sql腳步放到專案中,專案上傳到共享的資源中,文章最後給出共享url。

去除專案中不使用shiro實現認證的攔截器

    <!--攔截器 -->
    <!-- <mvc:interceptors> <mvc:interceptor> -->
<!-- 使用者認證攔截 --> <!-- <mvc:mapping path="/**" /> <bean class="cn.itcast.ssm.controller.interceptor.LoginInterceptor"></bean> </mvc:interceptor> <mvc:interceptor> --> <!-- 授權攔截 --> <!-- <mvc:mapping path="/**" /> <bean class="cn.itcast.ssm.controller.interceptor.PermissionInterceptor"></bean> </mvc:interceptor> </mvc:interceptors> -->

新增shiro的jar包

除了前面文章提到的shiro-core相關的jar包,還需要如下:

  • 與web整合的 shiro-web-1.2.3.jar
  • 與spring整合的 shiro-spring-1.2.3.jar
  • 與ehcache整合的 shiro-ehcache-1.2.3.jar

在web.xml中配置shiro的filter

在web系統中,shiro 也是通過filter進行攔截的。filter攔截後將操作交給filterChain(過濾器煉)。shiro中提供了多個filter,在欄目 shiro的過濾器 會全部介紹
在web中配置filter,如下:

    <!--shirofilter-->
    <!-- shiro過濾器,DelegatingFilterProxy通過代理模式將spring容器的bean和filter關聯起來 -->
    <filter>
        <filter-name>shiroFilter</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
        <!--設定true由servlet容器控制filter的生命週期-->
        <init-param>
            <param-name>targetFilterLifecycle</param-name>
            <param-value>true</param-value>
        </init-param>
        <!--設定spring容器filter的bean id,如果不設定則找與filter-name一致的bean -->
        <init-param>
            <param-name>targetBeanName</param-name>
            <param-value>shiroFilter</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>shiroFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

新建applicationContext-shiro.xml檔案

內容下圖:

    <!-- web.xml中shiro的filter對應的bean -->
    <!-- shiro的web過濾器 -->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager" />
        <!-- logiUrl認證提交地址,如果沒有認證通過將會請求此地址進行認證,請求此地址將由formAuthenticationFilter進行表單認證 -->
        <property name="loginUrl" value="/login.action" />
        <!-- 認證成功後統一跳轉到first.action,建議不配置,shiro認證成功自動到上一個連結 -->
        <property name="successUrl" value="/first.action" />
        <!-- 通過unauthorizedUrl指定沒有許可權時跳轉頁面 -->
        <property name="unauthorizedUrl" value="/refuse.jsp" />
        <!-- 過濾器鏈定義,從上向下順序執行,一般將/**放在最後面 -->
        <property name="filterChainDefinitions">
            <value>
                <!--靜態資源可以匿名訪問 -->
                /images/** = anon
                /js/** = anon
                /styles/** = anon
                <!--登入驗證碼匿名訪問-->
                /validatecode.jsp = anon
                <!--任何連結都可以不認證訪問-->
                /** = anon
            </value>
        </property>
    </bean>

    <!--securityManager安全管理器 -->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="realm" ref="customRealm" />
    </bean>

    <!-- 自定義的realm -->
    <bean id="customRealm" class="cn.itcast.ssm.shiro.CustomRealm"/>

上面配置的CustomRealm 的內容和文章shiro許可權框架詳解05-shiro授權的CustomRealm一樣。

測試是否整合成功

在介面輸入CustomRealm 程式碼中的賬號為 zhangsan 密碼為 123 可以進入歡迎頁面。
這裡寫圖片描述

但是並沒有選單和相關使用者資訊

登入功能

原理

由於登入使用的是 org.apache.shiro.web.filter.authc.FormAuthenticationFilter filter實現的,具體流程如下:
如果使用者沒有認證時,請求上面配置的loginUrl進行認證,使用者的身份資訊和密碼提交到loginUrl,FormAuthenticationFilter攔截取出request中的username和password(引數的key是可以進行配置的,下一篇blog介紹)引數值。FormAuthenticationFilter呼叫realm傳入一個token(包含username和password),realm根據username查詢使用者資訊,如果查詢不到,返回null,FormAuthenticationFilter向request域中填充一個引數,key為shiroLoginFailure 記錄異常資訊。如果不為空的話,返回AuthenticationInfo 類。

修改登入頁面

由於FormAuthenticationFilter的身份資訊和密碼的請求引數的key預設是(username和password),修改login.jsp頁面的賬號和密碼輸入框name屬性值,並註釋掉驗證碼的程式碼。

修改controller類

修改LoginControllerlogin 方法如下:

    @RequestMapping("/login")
    public String login(HttpServletRequest request)throws Exception{

        //如果登入失敗從request中獲取認證異常資訊,shiroLoginFailure就是shiro異常類的全限定名
        String exceptionClassName = (String) request.getAttribute("shiroLoginFailure");
        if(exceptionClassName!=null){
            if(UnknownAccountException.class.getName().equals(exceptionClassName)){
                throw new CustomException("賬號不存在");
            }else if(IncorrectCredentialsException.class.getName().equals(exceptionClassName)){
                throw new CustomException("使用者名稱或密碼錯誤");
            }else {
                throw new Exception();//最終在異常處理器生成未知錯誤
            }
        }
        //此方法不處理登入成功(認證成功),shiro認證成功會自動跳轉到上一個請求路徑。
        //登入失敗還到login頁面
        return "login";
    }

這個方法只有校驗不通過的時候才會執行。真正的校驗方法是在自定義的realm中,校驗身份是否正確。

修改認證攔截器

將所有請求都改為需要認證才能訪問的。

<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager" />
        <!-- logiUrl認證提交地址,如果沒有認證通過將會請求此地址進行認證,請求此地址將由formAuthenticationFilter進行表單認證 -->
        <property name="loginUrl" value="/login.action" />
        <!-- 認證成功後統一跳轉到first.action,建議不配置,shiro認證成功自動到上一個連結 -->
        <property name="successUrl" value="/first.action" />
        <!-- 通過unauthorizedUrl指定沒有許可權時跳轉頁面 -->
        <property name="unauthorizedUrl" value="/refuse.jsp" />
        <!-- 自定義filter配置 -->
        <property name="filters">
            <map>
                <entry key="authc" value-ref="formAuthenticationFilter"/>
            </map>
        </property>

        <!-- 過濾器鏈定義,從上向下順序執行,一般將/**放在最後面 -->
        <property name="filterChainDefinitions">
            <value>
                <!--靜態資源可以匿名訪問 -->
                /images/** = anon
                /js/** = anon
                /styles/** = anon
                /validatecode.jsp = anon

                <!-- /**=authc 表示所有的url都需要認證才能訪問 -->
                /** = authc
            </value>
        </property>
    </bean>

驗證登入功能

在登入介面輸入賬號為 zhangsan 密碼為 123 驗證是否登入成功,然後輸入錯誤的賬號,控制檯出現下面的異常資訊:
賬號不存在異常
如果輸入密碼錯誤:
密碼錯誤提示資訊
異常資訊和前面Java專案演示的一樣。

退出

退出不需要我們自己實現,只要去訪問一個退出的url(該url是可以不存在的)即可,由LogoutFilter filter處理,清除session。在applicationContext.xml配置logoutFilter

    <!-- web.xml中shiro的filter對應的bean -->
    <!-- shiro的web過濾器 -->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager" />
        <!-- logiUrl認證提交地址,如果沒有認證通過將會請求此地址進行認證,請求此地址將由formAuthenticationFilter進行表單認證 -->
        <property name="loginUrl" value="/login.action" />
        <!-- 認證成功後統一跳轉到first.action,建議不配置,shiro認證成功自動到上一個連結 -->
        <property name="successUrl" value="/first.action" />
        <!-- 通過unauthorizedUrl指定沒有許可權時跳轉頁面 -->
        <property name="unauthorizedUrl" value="/refuse.jsp" />
        <!-- 自定義filter配置 -->
        <property name="filters">
            <map>
                <entry key="authc" value-ref="formAuthenticationFilter"/>
            </map>
        </property>

        <!-- 過濾器鏈定義,從上向下順序執行,一般將/**放在最後面 -->
        <property name="filterChainDefinitions">
            <value>
                <!--靜態資源可以匿名訪問 -->
                /images/** = anon
                /js/** = anon
                /styles/** = anon
                /validatecode.jsp = anon
                <!-- 請求logout.action地址,shiro去清除session -->
                /logout.action = logout
                <!-- /**=authc 表示所有的url都需要認證才能訪問 -->
                /** = authc
            </value>
        </property>
    </bean>

刪除原來的退出方法。

選單顯示

修改realm從資料庫查詢使用者資訊及使用者擁有的選單設定在AuthenticatorInfo中。具體程式碼如下:

    /**
     * 用於認證
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) 
            throws AuthenticationException {
        //第一步:通過token獲取身份資訊
        String userCode = (String) token.getPrincipal();

    if (!"zhangsan".equals(userCode)) {// 這裡模仿查詢不到
            return null;
        }
        String password = "123";

        //模擬資料activeUser就是使用者身份資訊
        ActiveUser activeUser = new ActiveUser();
        activeUser.setUserid("zhangsan");
        activeUser.setUsercode("zhangsan");
        activeUser.setUsername("張三");

        //查詢選單資訊
        List<SysPermission> menus = null;
        try {
            menus = sysService.findMenuListByUserId(activeUser .getUsercode());
        } catch (Exception e) {
            e.printStackTrace();
        }
        activeUser.setMenus(menus);

        //如果查詢到結果返回AuthenticationInfo
        AuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(activeUser, password, "");
        return authenticationInfo;
    }

上面的程式碼通過模擬從資料庫獲取資料設定到ActiveUser中。

修改FirstAction中的first方法

修改程式碼將獲取身份資訊,並放入request域中用於頁面顯示。

    //系統首頁
    @RequestMapping("/first")
    public String first(Model model)throws Exception{

        Subject subject =SecurityUtils.getSubject();
        ActiveUser activeUser =  (ActiveUser)subject.getPrincipal();
        model.addAttribute("activeUser", activeUser);
        return "/first";
    }

測試

在登入介面,賬號輸入 zhangsan , 密碼輸入 123
如果成功的話,將會看到如下頁面。
顯示選單的截圖

shiro過濾器總結

過濾器簡稱 對應的Java類
anon org.apache.shiro.web.filter.authc.AnonymousFilter
authc org.apache.shiro.web.filter.authc.FormAuthenticationFilter
authcBasic org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter
perms org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter
port org.apache.shiro.web.filter.authz.PortFilter
rest org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter
roles org.apache.shiro.web.filter.authz.RolesAuthorizationFilter
ssl org.apache.shiro.web.filter.authz.SslFilter
user org.apache.shiro.web.filter.authc.UserFilter
logout org.apache.shiro.web.filter.authc.LogoutFilter

各個過濾器的介紹和作用:

anon:例子/admins/**=anon 沒有引數,表示可以匿名使用。

authc:例如/admins/user/**=authc表示需要認證(登入)才能使用,FormAuthenticationFilter是表單認證,沒有引數 。

roles:例子 /admins/user/** =roles[admin],引數可以寫多個,多個時必須加上引號,並且引數之間用逗號分割,當有多個引數時,例如admins/user/** =roles["admin,guest"],每個引數通過才算通過,相當於hasAllRoles()方法。

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

rest:例子/admins/user/**=rest[user],根據請求的方法,相當於/admins/user/**=perms[user:method],其中method為post,get,delete等。

port:例子/admins/user/**=port[8081],當請求的url的埠不是8081是跳轉到schemal://serverName:8081?queryString,其中schmal是協議http或https等,serverName是你訪問的host,8081是url配置裡port的埠,queryString
是你訪問的url裡的?後面的引數。

authcBasic:例如/admins/user/**=authcBasic沒有引數表示httpBasic認證

ssl:例子/admins/user/**=ssl沒有引數,表示安全的url請求,協議為https

user:例如/admins/user/**=user沒有引數表示必須存在使用者, 身份認證通過或通過記住我認證通過的可以訪問,當登入操作時不做檢查

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

blog的專案地址

點選進入下載頁面