Shiro使用說明文檔
聲明:
我們所使用的框架爲SSM框架+Shiro權限控制框架,在以下部分中,將會描述如何使用一個Shiro框架。
框架使用概述:
Shiro安全框架爲我們提供了一個較爲完善的權限管理系統。我們將使用該框架提供的用戶登錄,登出,用戶權限控制,用戶信息保存。
首先我們要明確一個對象Subject,這個對象可以是一個用戶,一個爬蟲,也可是一個人工智能等任意訪問該網站的對象。我們對其權限進行管理的一切信息,都是基於Subject對象得來的。而Subject對象可以通過SecurityUtils.getSubject()來獲得。
Shiro中還提供給了我們一個獨特的Session,Shiro中的Session與HttpSession不同,他們的不同之處在於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時表示權限之間的關係爲& 關係。而view:aaaa,表示用戶只要具有view權限下的aaaa權限即可訪問該方法或者類,而如果用戶具有view:aaaa權限,而不具有view權限,那麼當value={"view"}時是無法訪問的。當用戶具有view權限時,則可以訪問所有的view權限下的方法或者類。
“:”分號,設定權限爲子權限與父權限之間的關係,此時的權限view即是父權限,而view:aaaa爲子權限。
自動的頁面攔截:
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對象,然後調用subject的login方法來進行登錄。(代碼來自類: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填寫當值爲空時的默認值
如果該博客中存在錯誤,請在評論處指出。