【原創】基於Spring-SpringMVC-Mybatis 的 Shiro 安全框架使用教程--轉載請註明出處

Shiro使用說明文檔

聲明:

我們所使用的框架爲SSM框架+Shiro權限控制框架,在以下部分中,將會描述如何使用一個Shiro框架。

框架使用概述:

Shiro安全框架爲我們提供了一個較爲完善的權限管理系統。我們將使用該框架提供的用戶登錄,登出,用戶權限控制,用戶信息保存。

首先我們要明確一個對象Subject,這個對象可以是一個用戶,一個爬蟲,也可是一個人工智能等任意訪問該網站的對象。我們對其權限進行管理的一切信息,都是基於Subject對象得來的。而Subject對象可以通過SecurityUtils.getSubject()來獲得。

Shiro中還提供給了我們一個獨特的SessionShiro中的SessionHttpSession不同,他們的不同之處在於HttpSession當服務器重啓後就會失效,而Session則不會。這裏的Session可以通過Subject對象調用getSession()方法來獲取Session,而失效時間可以通過setTimeout(long time)方法來設置,其中的參數爲一個long型數字,當long<1000時,則會使Session永遠有效。

後端的權限控制:

其最常使用的註解是Shiro@RequiresRoles註解和@RequiresPermissions註解。@RequiresRoles註解限定了一個類或者方法需要哪一些角色類型才能夠進行訪問,@RequiresPermissions註解限定了類或方法需要哪一些權限類型才能夠被訪問。如果沒有沒有該權限,則會被攔截,之後跳轉到指定的登錄頁面上。

如下圖中代碼所示,下圖中的代碼沒有對角色進行限定,僅對權限進行了限定:

	/**
	 * 跳轉到用戶的界面
	 * @RequiresPermissions 該註解表示只有存在該權限的情況下才能訪問該頁面
	 * @param request
	 * @return 跳轉至user.jsp
	 */
	@RequiresPermissions(value={"view:aaaa"}, logical=Logical.OR)  
	@RequestMapping("user.do")
	public String toUser(HttpServletRequest request){
		Subject user = SecurityUtils.getSubject(); 
		user.getSession().setTimeout(1000000);
		//該方法可以用於獲取用戶的獨特的識別信息,一般爲用戶名
		String userName = (String) user.getPrincipal();
		request.setAttribute("userName", userName);
		return "user.jsp";
	}

@RequiresPermissions(value={"view:aaaa","aaaa"}, logical=Logical.OR)

該註解限定了用戶是否具有某個權限,logical=Logical.OR表示該註解標註的權限之間的關係爲|關係,當logical=Logical.AND時表示權限之間的關係爲& 關係。而viewaaaa,表示用戶只要具有view權限下的aaaa權限即可訪問該方法或者類,而如果用戶具有viewaaaa權限,而不具有view權限,那麼當value={"view"}時是無法訪問的。當用戶具有view權限時,則可以訪問所有的view權限下的方法或者類。

”分號,設定權限爲子權限與父權限之間的關係,此時的權限view即是父權限,而viewaaaa爲子權限。

自動的頁面攔截:

Shiro提供了自動的頁面攔截,在ShiroConfig.xml中配置了哪些是需要進行權限攔截的頁面,哪些是不需要進行權限攔截的頁面。我們的項目爲內網項目,所以以後我們將只暴露極少的登錄接口允許訪問,其他接口全部都將劃分在Shiro的保護之下。

在該demo項目中,只有login.*和index.*是可以被訪問的,其他頁面均是受保護的。

	<!-- 配置shiroFilter 6.1 id必須和web.xml 文件中配置的DelegatingFilterProxy的filter-name一致 -->
	<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="/index.jsp" />
		<property name="unauthorizedUrl" value="/login.jsp" />
		-->
		<!-- 配置哪些頁面需要受保護 , anon可以被匿名訪問,authc 必須認證之後才能訪問 -->
		<property name="filterChainDefinitions">
			<value>
				/login.* = anon
				/index.* = anon
				/** = authc
			</value>
		</property>
	</bean>

登錄驗證與授權:

Shiro的登錄驗證與授權中,需要new一個token對象,然後通過SecurityUtil類來獲取subject對象,然後調用subjectlogin方法來進行登錄。(代碼來自類:controller下的)

	@RequestMapping("login.do")
	public String checkLogin(String username, String password, HttpServletRequest request) {  
        try{
            UsernamePasswordToken token = new UsernamePasswordToken(username, password);  
            Subject currentUser = SecurityUtils.getSubject();  
            //判斷該用戶是否已經被驗證,如果已經驗證,則不需要再次驗證
            if (!currentUser.isAuthenticated()){
                //使用shiro來驗證  
            	//設定是否要保存賬戶信息(因爲本身該項目具有對其他人保密性,因此不會使用)
//            token.setRememberMe(true);  
            	token.setRememberMe(false); 
            	currentUser.login(token);//驗證角色和權限  
            } 
        }catch(Exception e){
        	//打印出異常信息,然後跳轉到登錄頁面(任何情況下的表單均要使用ajax,而非form提交,不可採取該方式)
        	e.printStackTrace();
        	return "login.jsp";  
        }
        return "index.jsp";  
    }  

在登錄時,所調用的登錄方法,和所調用的授權方法的代碼是需要自己來進行編寫的。

登錄以及授權代碼如下

package study;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;

import study.dao.UserDao;

public class MyShiroRealm extends AuthorizingRealm implements Realm{
	
	@Autowired
	private UserDao userDao;
	/**
	 * 對用戶進行授權處理
	 * 這個方法在用戶登錄的時候會進行執行,對用戶進行授權
	 * 該方法中可以通過從數據庫獲取角色,從數據庫獲取權限的形式來進行對角色及權限的授權
	 * 在從數據庫中獲取權限信息時,如下方法,登陸時獲取用戶信息相同
	 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
	Set<String> roleNames = new HashSet<String>();  
        Set<String> permissions = new HashSet<String>();  
        roleNames.add("admin");//添加角色
       	permissions.add("view:aaaa");  //添加權限
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roleNames);  
        info.setStringPermissions(permissions);  
        return info;  
	}
	
	/**
	 * 驗證用戶是否能夠進行登錄
	 * 此處調用了持久層的用戶表信息,在這裏進行了驗證用戶是否可以登錄,是否爲用戶表中的用戶
	 * 在實際使用中,密碼將使用MD5進行加密,此處未使用
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) 
			throws AuthenticationException {
		//該token爲controller中所傳入的token,所以可以直接強轉
		UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
		//從token中獲取到用戶名和密碼
		String userName = token.getUsername();
		String password = new String(token.getPassword());
		HashMap returnMap = null;
		try{
			//調用持久層,查詢用戶名是否存在
			HashMap<String, Object> paramsMap = new HashMap<String, Object>();
			paramsMap.put("userName", userName);
			returnMap = userDao.selectUser(paramsMap);
		}catch(Exception e){
			e.printStackTrace();
		}
		
		//存在後返回一個對象,包含用戶名和密碼,對用戶名和密碼進行驗證
        if(password.equals(returnMap.get("password"))){
        	//如果存在該用戶,則返回一個用戶信息對象,此處用戶主要信息傳入爲userName,憑證傳入爲password(可傳任意對象)
            return new SimpleAuthenticationInfo(userName, password, getName());  
        }else{
        	//如果沒有該用戶,則拋出一個異常(此處必須拋出異常)
            throw new AuthenticationException();  
        }
	}
}

要注意我們最終返回的對象 new SimpleAuthenticationInfo 的構造器中,第一個參數爲主要信息,第二個參數爲用戶的憑證,第三個參數爲RealName,調用父類的getName()方法即可。其中前兩個參數可傳Object類型,第一個參數主要信息可以在其他代碼中,通過Subject對象來進行獲取。

前端權限控制標籤:

在前端,shiro框架也提供了一套jstl標籤,將標籤庫導入後,就可以使用該標籤來實現權限控制。前端代碼如下:

	<!-- 使用shiro的jstl標籤,可以直接對權限進行控制 -->
	<!-- 根據用戶的權限信息判斷是否存在該部分內容 -->
	<shiro:hasPermission name="view2">
		<h1>因爲你沒有權限view2,所以看不到這行字</h1>
	</shiro:hasPermission>
	
	<!-- 根據用戶的權限信息判斷是否存在該部分內容 -->
	<shiro:hasPermission name="view,view:aaaa">
		<h1>hasPermission  "erty,view:aaaa"</h1>
	</shiro:hasPermission>

在使用該標籤前,需要先導入標籤庫:

<%@taglib prefix="shiro" uri="http://shiro.apache.org/tags"%>

前端jstl標籤使用問題:

在使用jstl標籤時,打出<shiro打開提示就可以看到下方有如下的shiro標籤提示:


其中的所有的標籤中的name,權限均應使用“,”逗號進行分隔,同時適用“:”冒號。使用的大致情況與註解一致。

Authenticated:當用戶已認證時,內部的信息將會顯示

Guest:來賓(未登錄者),內部信息將會顯示

hashAnyRoles:存在name中的角色中的一個時,內部信息將會顯示

hasPermission:存在name中的所有權限時,內部信息將會顯示

hasRole:存在name中的所有權限時,內部信息將會顯示

lacksPermission:不存在name中的所有權限時,內部信息將會顯示

lacksRole:不存在name中所有角色時,內部信息將會顯示

nowAuthenticated:沒有被認證時,內部信息將會顯示

User:當用戶登錄後,內部信息將會顯示

Principal:返回當前用戶的信息中的某數據,該信息會從主要信息中進行獲取。

Property 其中填寫該對象中的哪一個屬性

Type其中填寫主要信息的類型,如study.User(Model)

defaultValue填寫當值爲空時的默認值


如果該博客中存在錯誤,請在評論處指出。






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