1. 程式人生 > >shiro的簡單實用

shiro的簡單實用

shiro:     Apache Shiro是一個強大且易用的Java安全框架,執行身份驗證、授權、密碼和會話管理。     使用Shiro的易於理解的API,您可以快速、輕鬆地獲得任何應用程式,從最小的移動應用程式到最大的網路和企業應用程式。     單點登入:如果兩個網站上通用一個賬號來登入,那麼就會把這認證操作去儲存到一箇中央伺服器中去操作,這個中央伺服器就是為了實現認證許可權設計的,當你訪問到一個網站時,這個網站回去中央伺服器中去核對資料是否正確如果正確就會發送一個cookie 令牌給客戶端,客戶端儲存了cookie 然後再去訪問天貓網時,這時你就有了cookie天貓會用這個cookie去對比中央服務其中的如果匹配就可以進行登入,然後就實現了單點登入。

    三個核心元件:Subject, SecurityManager 和 Realms.

  •             Subject:即“當前操作使用者”。但是,在Shiro中,Subject這一概念並不僅僅指人,也可以是第三方程序、後臺帳戶(Daemon Account)或其他類似事物。 它僅僅意味著“當前跟軟體互動的東西”。但考慮到大多數目的和用途,你可以把它認為是Shiro的“使用者”概念。
  •           Subject代表了當前使用者的安全操作,SecurityManager則管理所有使用者的安全操作。
  •           SecurityManager:它是Shiro框架的核心,典型的Facade模式,Shiro通過SecurityManager來管理內部元件例項,並通過它來提供安全管理的各種服務。
  •           Realm: Realm充當了Shiro與應用安全資料間的“橋樑”或者“聯結器”。也就是說,當對使用者執行認證(登入)和授權(訪問控制)驗證時,Shiro會從應用配置的Realm中查詢使用者及其許可權資訊。

          從這個意義上講,Realm實質上是一個安全相關的DAO:它封裝了資料來源的連線細節,並在需要時將相關資料提供給Shiro。當配置Shiro時,你必須至少指定一個Realm,用於認證和(或)授權。配置多個Realm是可以的,但是至少需要一個。           Shiro內建了可以連線大量安全資料來源(又名目錄)的Realm,如LDAP、關係資料庫(JDBC)、類似INI的文字配置資源以及屬性檔案等。                     如果預設的Realm不能滿足需求,你還可以插入代表自定義資料來源的自己的Realm實現。              shiro認證流程:

            allipcation --->subject(當前使用者物件)--------->SecurityManager(安全管理器)--->Realm(其實就是一個dao層,可以直接編寫,也可以用框架提供的形式)

  •     SecutiyManager:包括Authenticator:認證器,負責主體認證的,這是一個擴充套件點,如果使用者覺得Shiro預設的不好,      可以自定義實現;其需要認證策略(Authentication Strategy),即什麼情況下算使用者認證通過了;
  •      Authorizer:授權器,或者訪問控制器,用來決定主體是否有許可權進行相應的操作;即控制著使用者能訪問應用中的哪些功能
  •      sessionManager:會話管理,會話管理,即使用者登入後就是一次會話,   在沒有退出之前,它的所有資訊都在會話中;會話可以是普通JavaSE環境的,也可以是如Web環境的
  •       catche manager :快取控制器,來管理如使用者、角色、許可權等的快取的;因為這些資料基本上很少去改變,放到快取中後可以提高訪問的效能
  •        sessionDao:比如我們想把Session儲存到資料庫,那麼可以實現自己的SessionDAO,通過如JDBC寫到資料庫;比如想把Session放到Memcached中,
  •      可以實現自己的Memcached SessionDAO;另外SessionDAO中可以使用Cache進行快取,以提高效能;

                        Pluggable Realms(1...more):就是提供連線各種資料庫的操作    自定義Realm:     public class ShiroRealm extends AuthorizingRealm{}

  •                 1、ShiroRealm父類AuthorizingRealm將獲取Subject相關資訊分成兩步:獲取身份驗證資訊(doGetAuthenticationInfo)及授權資訊(doGetAuthorizationInfo);
  •                 2、doGetAuthenticationInfo獲取身份驗證相關資訊:首先根據傳入的使用者名稱獲取User資訊;然後如果user為空,那麼丟擲沒找到帳號異常UnknownAccountException;如果user找到但鎖定了丟擲鎖定異常LockedAccountException;最後生成AuthenticationInfo資訊,交給間接父類AuthenticatingRealm使用CredentialsMatcher進行判斷密碼是否匹配,如果不匹配將丟擲密碼錯誤異常IncorrectCredentialsException;另外如果密碼重試此處太多將丟擲超出重試次數異常ExcessiveAttemptsException;在組裝SimpleAuthenticationInfo資訊時,需要傳入:身份資訊(使用者名稱)、憑據(密文密碼)、鹽(username+salt),CredentialsMatcher使用鹽加密傳入的明文密碼和此處的密文密碼進行匹配。
  •                 3、doGetAuthorizationInfo獲取授權資訊:PrincipalCollection是一個身份集合,因為我們現在就一個Realm,所以直接呼叫getPrimaryPrincipal得到之前傳入的使用者名稱即可;然後根據使用者名稱呼叫UserService介面獲取角色及許可權資訊。

    過濾器簡稱及對應的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 shiro整合spring:         web.xml中:           

  <!-- 配置spring框架提供的整合shiro框架的過濾器 
                    這裡命名要和spring中配置的bean 中提供的相同
              -->
              <filter>
                    <filter-name>shiroFilter</filter-name>
                    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>    
              </filter>
              <filter-mapping>
                        <filter-name>shiroFilter</filter-name>
                        <url-pattern>/*</url-pattern>
                    </filter-mapping>

        applicationContext.xml:                  

   <!-- 註冊realm -->
                        <bean id="bosRealm" class="com.leo.bos.realm.BOSRealm"></bean>
                        <!-- 注入安全管理器 -->
                        <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
                            <property name="realm" ref="bosRealm"></property>
                            <!--  <property name="cacheManager" ref="cacheManager"></property>-->
                        </bean>
                        <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
                            <property name="cacheManagerConfigFile" value="classpath:ehcache.xml"></property>
                        </bean>
                    <!-- 引入filter提供的工廠然後交給spring -->
                    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
                        <!-- 注入安全管理物件 -->
                        <!-- 
                        securityManager:這個屬性是必須的。
                        loginUrl :沒有登入的使用者請求需要登入的頁面時自動跳轉到登入頁面,不是必須的屬性,
                        不輸入地址的話會自動尋找專案web專案的根目錄下的”/login.jsp”頁面。
                        successUrl :登入成功預設跳轉頁面,不配置則跳轉至”/”。如果登陸前點選的一個需要登入的頁面,
                        則在登入自動跳轉到那個需要登入的頁面
                        unauthorizedUrl :沒有許可權預設跳轉的頁面
                         -->
                        <property name="securityManager" ref="securityManager"></property>
                        <property name="loginUrl" value="/login.jsp"></property>
                        <property name="successUrl" value="/index.jsp"></property>
                        <property name="unauthorizedUrl" value="unauthrized.jsp"></property>
                        <!-- 注入url攔截規則 
                            /css/**:css/.../...=anon就是匿名可以訪問
                            =perms["staff"]:檢查是否有這個許可權,
                            /admins/user/**=authc表示需要認證(登入)才能使用,沒有引數:但是現在
                            用shiro來判斷時struts中的配置就不行了
                        -->
                        <property name="filterChainDefinitions">
                            <value>
                                /css/**=anon
                                /js/**=anon
                                /images/**=anon
                                /validatecode.jsp*=anon
                                /login.jsp*=anon
                                /userAction_login.action=anon
                                /page_base_staff.action=perms["staff"]
                                /*=authc
                            </value>
                        </property>
                    </bean>    

        自定義Realm:             主要提供了授權和認證兩個方法:                      

package com.leo.bos.realm;

import java.util.List;

import org.apache.shiro.SecurityUtils;
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.subject.PrincipalCollection;
import org.hibernate.criterion.DetachedCriteria;
import org.springframework.beans.factory.annotation.Autowired;

import com.leo.bos.dao.IFunctionDao;
import com.leo.bos.dao.IUserDao;
import com.leo.bos.domain.AuthFunction;
import com.leo.bos.domain.User;
/**
 * 
 * @author leoi555
 *
 */
public class BOSRealm extends AuthorizingRealm{
	
	@Autowired
	private IUserDao userDao;
	@Autowired
	private IFunctionDao functionDao;
	//授權方法
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) {
		// TODO Auto-generated method stub
		SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();
		//獲取當前登陸物件
		User  user=(User) SecurityUtils.getSubject().getPrincipal();
		//User user=arg0.getPrimaryPrincipal();
		//根據當前登陸使用者查詢資料庫獲取實際對應的許可權
		List<AuthFunction> list;
		if (user.getUsername().equals("admin")) {
			DetachedCriteria detachedCriteria=DetachedCriteria.forClass(AuthFunction.class);
			//超級管理員內建使用者查詢所有許可權
			list=functionDao.findByCriteria(detachedCriteria);
		}else {
			list=functionDao.findFunctionbyUserId(user.getId());
		}
		for(AuthFunction function:list) {
			//關鍵子許可權的標識
			info.addStringPermission(function.getCode());;
		}
		return null;
	}
	//認證方法
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken arg0) throws AuthenticationException {
		// TODO Auto-generated method stub
		System.out.println("方法認證中。。。。");
		UsernamePasswordToken passwordToken=(UsernamePasswordToken) arg0;
		//獲取頁面輸入的使用者名稱
		String username=passwordToken.getUsername();
		//根據使用者名稱查詢資料庫中的密碼
		User user=userDao.findUserByUsername(username);
		if (user==null) {
			return null;
		}
		//框架負者比對資料庫中的密碼和頁面密碼是否輸入的一致
		//簡單認證的物件
		AuthenticationInfo info=new SimpleAuthenticationInfo(user, user.getPassword(), this.getName());
		
		return info;
	}

}

許可權控制:         1.使用shiro方法註解方式許可權控制方式:             1.在spring配置檔案中開啟註解支援:               

  <aop:config proxy-target-class="true"></aop:config>
                <bean class="
                org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
                    <property name="securityManager" ref="securityManager"/>
                </bean>
                在application.xml配置的時候:
                                    <bean id="filtershior" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
                        <!-- 注入安全管理物件 -->
                        <!-- 
                        securityManager:這個屬性是必須的。
                        loginUrl :沒有登入的使用者請求需要登入的頁面時自動跳轉到登入頁面,不是必須的屬性,
                        不輸入地址的話會自動尋找專案web專案的根目錄下的”/login.jsp”頁面。
                        successUrl :登入成功預設跳轉頁面,不配置則跳轉至”/”。如果登陸前點選的一個需要登入的頁面,
                        則在登入自動跳轉到那個需要登入的頁面
                        unauthorizedUrl :沒有許可權預設跳轉的頁面
                         -->
                        <property name="securityManager" ref="securityManager"></property>
                        <property name="loginUrl" value="/login.jsp"></property>
                        <property name="successUrl" value="/index.jsp"></property>
                        <property name="unauthorizedUrl" value="unauthrized.jsp"></property>
                        <!-- 注入url攔截規則 
                            /css/**:css/.../...=anon就是匿名可以訪問
                            =perms["staff"]:檢查是否有這個許可權,
                            /admins/user/**=authc表示需要認證(登入)才能使用,沒有引數:但是現在
                            用shiro來判斷時struts中的配置就不行了
                        -->
                        <property name="filterChainDefinitions">
                            <value>
                                /css/**=anon
                                /js/**=anon
                                /images/**=anon
                                /validatecode.jsp*=anon
                                /login.jsp*=anon
                                /userAction_login.action=anon
                                /page_base_staff.action=perms["staff"]
                                /*=authc
                            </value>
                        </property>
                    </bean>    

                     許可權註解:             @RequiresAuthentication             表示當前 Subject 已經通過 login 進行了身份驗證;即 Subject.isAuthenticated() 返回 true。             @RequiresUser             表示當前 Subject 已經身份驗證或者通過記住我登入的。             @RequiresGuest             表示當前 Subject 沒有身份驗證或通過記住我登入過,即是遊客身份。             @RequiresRoles(value={“admin”, “user”}, logical= Logical.AND)             表示當前 Subject 需要角色 admin 和 user。             @RequiresPermissions (value={“user:a”, “user:b”}, logical= Logical.OR)             表示當前 Subject 需要許可權 user:a 或 user:b。          @RequiresPermissions("staff-delete")              方法名:(){              }         2.使用shrio提供的標籤控制權限在頁面中使用的:             引入標籤庫,             使用shrio提供的標籤來使用:                 <!-- 許可權認證  與web.xml有關-->                 <shiro:hasPermission name="staff-delete">                     <!-- 這裡面只要把相關的程式碼寫到這裡麵包裹下就可以直接使用了 -->                 </shiro:hasPermission>         3.在方法中直接使用:         //另一種方式:             Subject subject = SecurityUtils.getSubject();             subject.checkPermission("staff-edit");              shiro實現記住使用者功能:     RememberMe:                 Shiro 提供了記住我(RememberMe)的功能,比如訪問如淘寶等一些網站時,關閉了瀏覽器下次再開啟時還是能記住你是誰,下次訪問時無需再登入即可訪問,基本流程如下:                 首先在登入頁面選中 RememberMe 然後登入成功;如果是瀏覽器登入,一般會把 RememberMe 的 Cookie 寫到客戶端並儲存下來;                 關閉瀏覽器再重新開啟;會發現瀏覽器還是記住你的;                 訪問一般的網頁伺服器端還是知道你是誰,且能正常訪問;                 但是比如我們訪問淘寶時,如果要檢視我的訂單或進行支付時,此時還是需要再進行身份認證的,以確保當前使用者還是你。             需要配置如下:                 //關閉瀏覽器時cookie失效:                 sessionIdCookie:maxAge=-1 表示瀏覽器關閉時失效此 Cookie;           

 <bean id="sessionIdCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
                <constructor-arg value="sid"/>
                <property name="httpOnly" value="true"/>
                <property name="maxAge" value="-1"/>
            </bean>
            //配置記住個人使用者登入儲存到Cookie中放到本地:
            <bean id="rememberMeCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
                <constructor-arg value="rememberMe"/>
                <property name="httpOnly" value="true"/>
                <property name="maxAge" value="2592000"/><!-- 30天 -->
            </bean>
            `<!-- rememberMe管理器 -->`
            <bean id="rememberMeManager" 
            class="org.apache.shiro.web.mgt.CookieRememberMeManager">
                <property name="cipherKey" value="
            \#{T(org.apache.shiro.codec.Base64).decode('4AvVhmFLUs0KTA3Kprsdag==')}"/>
                 <property name="cookie" ref="rememberMeCookie"/>
            </bean>
            //需要把remeberMeCookie注入到安全管理器中:
                <!-- 注入安全管理器 -->
                <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
                 <property name="rememberMeManager" ref="rememberMeManager"/>
                </bean>

這是shiro的簡單的使用,是與spring整合使用的框架,同樣看也可以作為單一的框架了來使用