談談Shiro的原理及在SSM和SpringBoot兩種環境下的使用姿勢(下篇)

FuyunWang發表於2019-03-04

在上一篇中,我已經對Shiro中認證和授權模組基本認證做了介紹,本篇主要介紹Shiro在SSM的工程中的整合使用方式和在SpringBoot工程中的使用方式。

接上篇:談談Shiro的原理及在SSM和SpringBoot兩種環境下的使用姿勢(上篇)

##首先是在SSM工程中的整合

之前我們在SSM工程中作為身份認證和許可權攔截的模組是通過攔截器的方式來實現的。現在我們去掉攔截器,使用Shiro整合搭建工程。

  1. 首先搭建基本的SSM工程。我這裡還是採用了傳統的SSM工程結構。利用maven建立一個骨架為web的工程中,然後在pom檔案中引入依賴。

  2. 配置web.xml檔案,注意這裡由於我們是採用Shiro作為安全模組,所以我們這裡會在web.xml中配置一個代理的Filter,這個代理的Filter由Shiro實現並在Spring中配置。這個Filter將會攔截我們系統中所有的請求,然後進行處理。也就是起到了原來攔截器的作用。

     	<!-- 這裡需要配置一個Filter,將由Shiro實現 -->
       	<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>
    複製程式碼
  3. 在src/main/resources中引入db.properties和Mybatis的配置檔案SqlMapConfig.xml。然後我們對工程中進行MyBatis配置。

     	<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
     	"http://mybatis.org/dtd/mybatis-3-config.dtd">
     	<configuration>
     		<!-- 定義 別名 -->
     		<typeAliases>
     			<package name="com.beautifulsoup.shiro.ssmdemo.entity"/>
     		</typeAliases>
     	</configuration>
    複製程式碼
  4. 配置Spring的環境和SpringMVC的環境。在src/main/resources目錄下建立spring目錄,然後依次建立applicationContext-dao.xml,applicationContext-service.xml,applicationContext-trans.xml,springmvc.xml檔案。之後在這些檔案中進行SSM框架整合的配置。

  5. 在第3步的spring目錄下建立一個applicationContext-shiro.xml的配置檔案,我們在web.xml中配置所需的Filter將在這裡註冊。
    首先在web.xml中我們已經指定了filter bean的名字是shiroFilter,所以我們註冊的filter的beanname也應該是shiroFilter。此外,在上一篇的基本介紹中我們也知道了,Shiro中的核心是SecurityManager,SecurityManager最終交給Realm進行認證和授權,所以這些我們也應該在Spring配置檔案中配置,交給Spring容器管理。

     			<!-- 配置Realm -->
     			<bean id="shiroDemoRealm" class="com.beautifulsoup.shiro.ssmdemo.realm.ShiroDemoRealm"/>
     			<!-- 配置安全管理器 -->
     			<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
     				<property name="realm" ref="shiroDemoRealm" />
     			</bean>
     			<!-- Shiro 的Web過濾器 -->
     			<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
     				<property name="securityManager" ref="securityManager" />
     				<!-- 如果沒有認證將要跳轉的登陸地址 -->
     				<property name="loginUrl" value="/login.action" />
     				<!-- 沒有許可權跳轉的地址 -->
     				<property name="unauthorizedUrl" value="/refuse.jsp" />
     				<property name="filterChainDefinitions">
     					<value>
     						/** = anon
     					</value>
     				</property>
     			</bean>
    複製程式碼

為了簡單,我直接使用了上一次所介紹的realm類。

可以看到,這裡我們主要是配置了一個ShiroFilterFactoryBean,這是一個工廠類,它主要用於生產ShiroFilter,我們可以在這個工廠Bean中定義一系列我們所需要的Filter鏈。Shiro這個框架本身就為我們提供了很多的過濾器,通過使用這些已經內建的過濾器已經能夠很好的實現我們所需要實現的功能。上面我們配置的/**=anon表示對於該工程中所有的url都可以以匿名的方式來訪問,anon表示一個過濾器的縮寫,除此之外,Shiro還提供了其他的一系列的過濾器。如下:

		過濾器簡稱	對應的實際過濾器
		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
複製程式碼

接下來,我們就通過這些攔截器來實現我們SSM工程的認證和授權。

認證
程式碼在(v0.3標籤下)
登入和退出:
配置如下:

		<bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
			<property name="hashAlgorithmName" value="md5"/>
			<property name="hashIterations" value="3"/>
		</bean>
		<bean id="shiroDemoRealm" class="com.beautifulsoup.shiro.ssmdemo.realm.ShiroDemoRealm">
			<property name="credentialsMatcher" ref="credentialsMatcher"></property>
		</bean>
		<!-- 配置安全管理器 -->
		<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
			<property name="realm" ref="shiroDemoRealm" />
		</bean>
		<!-- Shiro 的Web過濾器 -->
		<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
			<property name="securityManager" ref="securityManager" />
			<!-- 如果沒有認證將要跳轉的登陸地址 -->
			<property name="loginUrl" value="/login.action" />
			<!-- 沒有許可權跳轉的地址 -->
			<property name="unauthorizedUrl" value="/refuse.jsp" />
			<property name="filterChainDefinitions">
				<value>
					<!-- 定義退出的路徑 -->
					/logout.action = logout
					/** = authc					
				</value>
			</property>
		</bean>
複製程式碼

主要通過FormAuthenticationFilter實現。這裡需要注意的是,我們提交認證的表單的引數預設為username和password,記住我的引數預設為:rememberMe。原因如下:

Aaron Swartz

注意,當我們在系統中採用FormAuthenticationFilter作為配置時,FormAuthenticationFilter會將表單的提交引數取出,並呼叫realm進行認證。如果認證失敗,則系統自動跳轉到我們配置的loginUrl的連結地址,同時會將認證失敗的異常資訊新增到request中,我們可以在Controller中定義一個方法接受loginUrl配置的連結地址,然後從中取出異常資訊進行二次處理。而如果我們認證成功了,系統的成功處理器會預設跳轉到我們將要訪問的url路徑。我們可以使用類似loginUrl的配置配置一個successUrl實現自定義的成功跳轉邏輯。

授權:

程式碼在v0.4標籤下

上面提到了,Shiro的web模組主要是為我們提供了一系列的過濾器Filter,在傳統的ssm專案整合過程中,我們是通過將Shiro交給Spring進行管理然後在容器中配置一個過濾器鏈。

上面提到的認證是通過配置過濾器FormAuthenticationFilter完成的,這裡的授權是通過配置PermissionsAuthorizationFilter完成的。

配置如下:

		<bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
			<property name="hashAlgorithmName" value="md5"/>
			<property name="hashIterations" value="3"/>
		</bean>
		<bean id="shiroDemoRealm" class="com.beautifulsoup.shiro.ssmdemo.realm.ShiroDemoRealm">
			<property name="credentialsMatcher" ref="credentialsMatcher"></property>
		</bean>
		<!-- 配置安全管理器 -->
		<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
			<property name="realm" ref="shiroDemoRealm" />
		</bean>
		<!-- Shiro 的Web過濾器 -->
		<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
			<property name="securityManager" ref="securityManager" />
			<!-- 如果沒有認證將要跳轉的登陸地址 -->
			<property name="loginUrl" value="/login.action" />
			<!-- 沒有許可權跳轉的地址 -->
			<property name="unauthorizedUrl" value="/refuse.action" />
			<property name="filterChainDefinitions">
				<value>
					<!-- 定義所需的許可權資訊 -->
					/item/query.action=perms[item:query]
					/item/delete02.action=perms[item:delete:02]
					<!-- 定義退出的路徑 -->
					/logout.action = logout
					/** = authc					
				</value>
			</property>
		</bean>
複製程式碼

除了xml中的配置,shiro還提供了註解的授權的方法。由於註解的授權方式本質上是Spring的AOP的代理方式,所以我們需要在Spring的配置檔案中開啟AOP的支援。:

		<aop:config proxy-target-class="true"></aop:config>
		<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
			<property name="securityManager" ref="securityManager" />
		</bean>
		
		在Controller中使用
		
		@RequestMapping("/delete02")
		@RequiresPermissions("item:delete:02")
		public String itemDelete(){
			
			
			return "delete02item";
		}
		
		@RequiresPermissions("item:query")
		@RequestMapping("/query")
		public String ItemQuery(){
			return "itemquery";
		}
複製程式碼

我們在每次查詢許可權資訊的時候,控制檯總會彈出警告:

Aaron Swartz

這是提示我們沒有新增快取,這會使得我們頻繁查詢資料庫導致效率低下。在上一節的基本概念中也已經提到過幾個重要的概念,其中CacheManager就是實現快取管理的,SessionManager是實現的Session的管理。

這裡我們使用Shiro整合EhCache來實現快取的管理。

	<!-- 快取管理器 -->
	<bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
		<property name="cacheManagerConfigFile" value="classpath:shiro-ehcache.xml"/>
	</bean>
	
	ehcache的配置檔案:
	<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
		<!--diskStore:快取資料持久化的目錄 地址  -->
		<diskStore path="D:Temp" />
		<defaultCache 
			maxElementsInMemory="1000" 
			maxElementsOnDisk="10000000"
			eternal="false" 
			overflowToDisk="false" 
			diskPersistent="false"
			timeToIdleSeconds="120"
			timeToLiveSeconds="120" 
			diskExpiryThreadIntervalSeconds="120"
			memoryStoreEvictionPolicy="LRU">
		</defaultCache>
	</ehcache>
複製程式碼

Shiro在SSM傳統工程中的整合工程大多就這些,下面簡單說一下關於SpringBoot整合Shiro的使用方式。

##然後是在SpringBoot工程中的整合:

SpringBoot是對Spring傳統專案的簡化,自然Shiro與SpringBoot的整合也是Shiro與Spring整合的簡化。SpringBoot與Shiro的整合類似於SSM工程與Shiro的整合。這裡只介紹關於整合的不同之處。對於SSM的整合和SpringBoot整合的完整程式碼都已經上傳Github。

	主要是將原來的XML中的配置提取到了Java配置中,核心的Java Config的類如下:
		@Bean("credentialMatcher")
	    public CredentialMatcher credentialMatcher(){
	        return new CredentialMatcher();
	    }
	
	    @Bean("authRealm")
	    public AuthRealm authRealm(@Qualifier("credentialMatcher")CredentialMatcher matcher){
	        AuthRealm authRealm=new AuthRealm();
	        authRealm.setCredentialsMatcher(matcher);
	        return authRealm;
	    }
	
	    @Bean("securityManager")
	    public SecurityManager securityManager(@Qualifier("authRealm") AuthRealm authRealm){
	        DefaultWebSecurityManager securityManager=new DefaultWebSecurityManager();
	        securityManager.setRealm(authRealm);
	        return securityManager;
	    }
	
	    @Bean
	    public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("securityManager")SecurityManager securityManager){
	        ShiroFilterFactoryBean shiroFilterFactoryBean=new ShiroFilterFactoryBean();
	        shiroFilterFactoryBean.setSecurityManager(securityManager);
	        shiroFilterFactoryBean.setLoginUrl("/login");
	        shiroFilterFactoryBean.setSuccessUrl("/index");
	        shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorized");
	
	        LinkedHashMap<String,String> filterChainDefinitionMap=new LinkedHashMap<>();
	        filterChainDefinitionMap.put("/login","anon");
	        filterChainDefinitionMap.put("/index","authc");
	        filterChainDefinitionMap.put("/loginUser","anon");
	        //filterChainDefinitionMap.put("/**","user");//配置shiro只要使用者登入就可以檢視所有url
	        filterChainDefinitionMap.put("/admin", "roles[role3]");
	        filterChainDefinitionMap.put("/edit", "perms[item:create:01]");
	        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
	        return shiroFilterFactoryBean;
	    }
	
	    @Bean
	    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("securityManager")SecurityManager securityManager){
	        AuthorizationAttributeSourceAdvisor advisor=new AuthorizationAttributeSourceAdvisor();
	        advisor.setSecurityManager(securityManager);
	        return advisor;
	    }
	
	    @Bean
	    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator(){
	        DefaultAdvisorAutoProxyCreator creator=new DefaultAdvisorAutoProxyCreator();
	        creator.setProxyTargetClass(true);
	        return creator;
	    }
複製程式碼

這裡可以看到,其實SpringBoot中我們完全可以使用Java配置替代XML配置,具體的整合思路還是一樣的。

最後:
###SSM整合Shiro的程式碼地址:github.com/fuyunwang/S…

###SpringBoot整合Shiro的程式碼地址:github.com/fuyunwang/S…

如果對您有過幫助,希望您隨手一個star。

相關文章