1. 程式人生 > >shiro一之例項運用

shiro一之例項運用

參考:http://hnbcjzj.iteye.com/blog/1706600

簡介: Shiro 是一個 Apache Incubator 專案,旨在簡化身份驗證和授權。是一個很不錯的安全框架。 

借用別人寫的一個shiro框架的例項來對shiro的應用做下記錄。

1.基本概念

首先理解兩個概念:認證和授權

我開始接觸許可權的時候,並沒有明確區分認證和授權,把這兩個概念混為一團,導致在開始運用shi'ro的時候有些地方沒有理解透徹。

認證,簡單來說,就是指使用者身份是否合法,通俗的理解就是使用者是否是登入使用者,登入的使用者為認證使用者,為登入的使用者為非認證使用者。

授權,一般來說,認證是第一步,使用者經過認證以後,只能說明使用者是系統的合法使用者,但是許可權是分級別的,使用者身份合法並不代表使用者就對所有資源擁有操作許可權,要對某一個資源操作,還需要相應的許可權管理,這就是授權要就解決的問題。

2.配置

結合DEMO進行說明。

先將shiro引入web.xml

	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>classpath:applicationContext.xml,classpath:spring-shiro.xml</param-value>
	</context-param>

	<!-- apache shiro許可權 -->
	<filter>
		<filter-name>shiroFilter</filter-name>
		<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
		<init-param>
			<param-name>targetFilterLifecycle</param-name>
			<param-value>true</param-value>
		</init-param>
	</filter>

	<filter-mapping>
		<filter-name>shiroFilter</filter-name>
		<url-pattern>*.do</url-pattern>
	</filter-mapping>
	<filter-mapping>
		<filter-name>shiroFilter</filter-name>
		<url-pattern>*.jsp</url-pattern>
	</filter-mapping>

shiro的配置檔案是spring-shiro.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util"
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
	http://www.springframework.org/schema/beans/spring-beans-3.0.xsd   
    http://www.springframework.org/schema/util 
    http://www.springframework.org/schema/util/spring-util-3.0.xsd">
	<description>Shiro 配置</description>
	<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
		<property name="securityManager" ref="securityManager" />
		<property name="loginUrl" value="/login.jsp" />
		<property name="successUrl" value="/login.jsp" />
		<property name="unauthorizedUrl" value="/error/noperms.jsp" />
		<property name="filterChainDefinitions">
			<value>
				/login.jsp* = anon
				/login.do* = anon
				/index.jsp*= anon
				/error/noperms.jsp*= anon
				/*.jsp* = authc
				/*.do* = authc
			</value>
		</property>
	</bean>

	<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
		<!--設定自定義realm -->
		<property name="realm" ref="monitorRealm" />
	</bean>

	<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />

	<!--自定義Realm 繼承自AuthorizingRealm -->
	<bean id="monitorRealm" class="com.shiro.service.MonitorRealm"></bean>
	<!-- securityManager -->
	<bean
		class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
		<property name="staticMethod"
			value="org.apache.shiro.SecurityUtils.setSecurityManager" />
		<property name="arguments" ref="securityManager" />
	</bean>

	<!-- Enable Shiro Annotations for Spring-configured beans. Only run after -->
	<!-- the lifecycleBeanProcessor has run: -->
	<bean
		class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
		depends-on="lifecycleBeanPostProcessor" />
	<bean
		class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
		<property name="securityManager" ref="securityManager" />

	</bean>

</beans>

在這個配置檔案中有兩個重點要關注的地方:

1)過濾器配置。在裡面有兩個型別的過濾器配置,anon和authc,anon,要注意的是anon必須配置在authc配置項的前面,否則anon配置項會失效。

Anon:不指定過濾器,不錯是這個過濾器是空的,什麼都沒做,跟沒有一樣。 
Authc:驗證,這些頁面必須驗證後才能訪問,也就是我們說的登入後才能訪問。 

簡單來講,anon就是指所有使用者(匿名使用者)都可以訪問的資源,authc是指認證使用者(登入使用者)才可以訪問的資源。
這裡還有其他的過濾器,我沒用,比如說授權,這個比較重要,但是這個過濾器有個不好的地方,就是要帶一個引數,所以如果配在這裡就不是很合適,因為每個頁面,或是.do的許可權不一樣,而我們也沒法事先知道他需要什麼許可權。所以這裡不配,我們在程式碼中再授權。這裡.do和.jsp後面的*表示引數,比如login.jsp?main這種,是為了匹配這種。好行了,繼續往下吧。 

2)自定義realm

realm的作用就是自己來定義認證和授權的業務邏輯。可以靈活的控制權限。這裡自定義了一個monitorRealm用來自己寫許可權業務,這個後面慢慢說明。

3.認證

現在先看第一步認證。

開啟登入頁面,輸入使用者名稱/密碼點選提交,就會提交到後臺的登入actIon的方法:

	@RequestMapping(params = "main")
	public ModelAndView login(User user,HttpSession session, HttpServletRequest request) {

		ModelAndView modelView = new ModelAndView();
		Subject currentUser = SecurityUtils.getSubject();
		UsernamePasswordToken token = new UsernamePasswordToken(
				user.getUsercode(), EncryptUtils.encryptMD5(user.getPassword()));
		token.setRememberMe(true);
		try {
			currentUser.login(token);
		} catch (AuthenticationException e) {
			modelView.addObject("message", "login errors");
			modelView.setViewName("/login");
			e.printStackTrace();
			
		}
		if(currentUser.isAuthenticated()){
			user.setUserName("張三");
			session.setAttribute("userinfo", user);
			modelView.setViewName("/main");
		}else{
			modelView.addObject("message", "login errors");
			modelView.setViewName("/login");
		}
		return modelView;
	}

這裡有兩條語句需要說明:

Subject currentUser = SecurityUtils.getSubject() ;

currentUser就是代表當前的使用者。 

UsernamePasswordToken token = new UsernamePasswordToken(user.getUsercode(),EncryptUtils.encryptMD5(user.getPassword())); 

token大家叫他令牌,也就相當於一張表格,你要去驗證,你就得填個表,裡面寫好使用者名稱密碼,交給公安局的同志給。

currentUser.login(token); 

 這個就是校驗token,校驗過程會回撥realm裡面的認證方法:

protected AuthenticationInfo doGetAuthenticationInfo() 

這裡就是自己寫認證邏輯的地方,看下這裡的業務

	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(
			AuthenticationToken authcToken) throws AuthenticationException {
		/* 這裡編寫認證程式碼 */
		UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
//		User user = securityApplication.findby(upToken.getUsername());
		User user = new User();
		user.setUsercode(token.getUsername());
		user.setUserName("admin");
		user.setPassword(EncryptUtils.encryptMD5("admin"));
//		if (user != null) {
		return new SimpleAuthenticationInfo(user.getUserName(),
				user.getPassword(), getName());


	}

是否呼叫這裡是在前面的配置檔案中配置的,我們前面spring裡配了這個 

/*.jsp* = authc 
/*.do* = authc 


你配了authc過濾器,shiro會自動調currentUser.isAuthenticated()這個方法,沒有登入的將被返回 

<property name="unauthorizedUrl" value="/error/noperms.jsp" /> 

配置的頁面。

呼叫這個認證方法後,會對使用者的使用者名稱密碼進行校驗,校驗失敗跳轉到登入頁面,校驗成功處理登入方法後的邏輯。這裡是跳轉到main主頁。

4.授權

再看第二步,授權

再main頁面上點選myjsp,會提交到user.do?myjsp,看下後臺的這個方法:

	@RequestMapping(params = "myjsp")
	public String home() {
		Subject currentUser = SecurityUtils.getSubject();
		if(currentUser.isPermitted("user.do?myjsp")){
			return "my";
		}else{
			return "error/noperms";
		}
	}

這裡

Subject currentUser = SecurityUtils.getSubject(); 
currentUser.isPermitted("user.do?myjsp"); 

這兩行語句的意思是判斷當前使用者是否有訪問user.do?myjsp的許可權

呼叫currentUser.isPermitted("user.do?myjsp");此方法後會回撥realm中的授權驗證方法

protected AuthorizationInfo doGetAuthorizationInfo( PrincipalCollection principals)

看下這裡是如何授權的:

	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(
			PrincipalCollection principals) {
		/* 這裡編寫授權程式碼 */
		Set<String> roleNames = new HashSet<String>();
	    Set<String> permissions = new HashSet<String>();
	    roleNames.add("admin");
	    permissions.add("user.do?myjsp");
	    permissions.add("login.do?main");
	    permissions.add("login.do?logout");
		SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roleNames);
	    info.setStringPermissions(permissions);
		return info;

	}

這裡沒有用到資料庫,直接將許可權硬編碼在程式碼裡面,新增admin角色,然後給角色新增許可權,當然正常肯定是預先存放在資料庫裡面,直接從資料庫裡面查詢出來,構造成一個許可權物件返回,再與currentUser.isPermitted()中傳入的許可權引數進行對比,看看使用者是否有訪問此地址引數的許可權,沒有許可權就返回到沒有許可權的頁面。

5.總結

通過前面的demo流程,可以分析出如下幾點:

1)認證的配置一般都是配置再shiro的配置檔案,而授權設定比較靈活,一般不直接寫在shiro配置檔案裡面。

2)認證是授權的前提,通過通過了認證,才會用相應的許可權管理。

3)認證過程會呼叫reanlm裡面的認證方法doGetAuthenticationInfo(),授權會呼叫realm裡面的授權方法doGetAuthenticationInfo()

一般認證方法只會在登入的時候呼叫一次,而授權則在需要授權的地方都會呼叫,會被多次呼叫。