03-11-動吧旅遊生態系統-Shiro

Absoor發表於2020-09-25

1Shiro安全框架簡介

1.1Shiro概述

Shiro是apache旗下一個開源安全框架(http://shiro.apache.org/),它將軟體系統的安全認證相關的功能抽取出來,實現使用者身份認證,許可權授權、加密、會話管理等功能,組成了一個通用的安全認證框架。使用shiro就可以非常快速的完成認證、授權等功能的開發,降低系統成本。
使用者在進行資源訪問時,要求系統要對使用者進行許可權控制,其具體流程如圖-1所示:
在這裡插入圖片描述

圖-1

1.2Shiro概要架構

在概念層面,Shiro 架構包含三個主要的理念,如圖-2所示:
在這裡插入圖片描述

圖-2
其中:
1)Subject :主體物件,負責提交使用者認證和授權資訊。
2)SecurityManager:安全管理器,負責認證,授權等業務實現。
3)Realm:領域物件,負責從資料層獲取業務資料。

1.3Shiro詳細架構

Shiro框架進行許可權管理時,要涉及到的一些核心物件,主要包括:認證管理物件,授權管理物件,會話管理物件,快取管理物件,加密管理物件以及Realm管理物件(領域物件:負責處理認證和授權領域的資料訪問題)等,其具體架構如圖-3所示:
在這裡插入圖片描述

圖-3
其中:
1)Subject(主體):與軟體互動的一個特定的實體(使用者、第三方服務等)。
2)SecurityManager(安全管理器) :Shiro 的核心,用來協調管理元件工作。
3)Authenticator(認證管理器):負責執行認證操作。
4)Authorizer(授權管理器):負責授權檢測。
5)SessionManager(會話管理):負責建立並管理使用者 Session 生命週期,提供一個強有力的 Session 體驗。
6)SessionDAO:代表 SessionManager 執行 Session 持久(CRUD)動作,它允許任何儲存的資料掛接到 session 管理基礎上。
7)CacheManager(快取管理器):提供建立快取例項和管理快取生命週期的功能。
8)Cryptography(加密管理器):提供了加密方式的設計及管理。
9)Realms(領域物件):是shiro和你的應用程式安全資料之間的橋樑。

2Shiro框架認證攔截實現(filter)

2.1Shiro基本環境配置

2.1.1新增shiro依賴

實用spring整合shiro時,需要在pom.xml中新增如下依賴:

<dependency>
   <groupId>org.apache.shiro</groupId>
   <artifactId>shiro-spring</artifactId>
   <version>1.5.3</version>
</dependency>

2.1.2Shiro核心物件配置

基於SpringBoot 實現的專案中,沒有提供shiro的自動化配置,需要我們自己配置。

第一步:建立SpringShiroConfig類。關鍵程式碼如下:

package com.cy.pj.common.config;
/**@Configuration 註解描述的類為一個配置物件,
 * 此物件也會交給spring管理
 */
@Configuration
public class SpringShiroConfig {

}

第二步:在Shiro配置類中新增SecurityManager配置(這裡一定要使用org.apache.shiro.mgt.SecurityManager這個介面物件),關鍵程式碼如下:

@Bean
public SecurityManager securityManager() {
		 DefaultWebSecurityManager sManager=
		 new DefaultWebSecurityManager();
		 return sManager;
}

第三步: 在Shiro配置類中新增ShiroFilterFactoryBean物件的配置。通過此物件設定資源匿名訪問、認證訪問。關鍵程式碼如下:

@Bean
public ShiroFilterFactoryBean shiroFilterFactory (
			 SecurityManager securityManager) {
		 ShiroFilterFactoryBean sfBean=
		 new ShiroFilterFactoryBean();
		 sfBean.setSecurityManager(securityManager);
		 //定義map指定請求過濾規則(哪些資源允許匿名訪問,哪些必須認證訪問)
		 LinkedHashMap<String,String> map= new LinkedHashMap<>();
		 //靜態資源允許匿名訪問:"anon"
		 map.put("/bower_components/**","anon");
		 map.put("/build/**","anon");
		 map.put("/dist/**","anon");
		 map.put("/plugins/**","anon");
		 //除了匿名訪問的資源,其它都要認證("authc")後訪問
		 map.put("/**","authc");
		 sfBean.setFilterChainDefinitionMap(map);
		 return sfBean;
	 }

其配置過程中,物件關係如下圖-4所示:
在這裡插入圖片描述

圖-4

2.2Shiro登陸頁面呈現

2.2.1服務端Controller實現

▪業務描述及設計實現
當服務端攔截到使用者請求以後,判定此請求是否已經被認證,假如沒有認證應該先跳轉到登入頁面。
▪關鍵程式碼分析及實現.
第一步:在PageController中新增一個呈現登入頁面的方法,關鍵程式碼如下:

@RequestMapping("doLoginUI")
public String doLoginUI(){
		return "login";
}

第二步:修改SpringShiroConfig類中shiroFilterFactorybean的配置,新增登陸url的設定。關鍵程式碼見sfBean.setLoginUrl("/doLoginUI")部分。

@Bean
public ShiroFilterFactoryBean shiroFilterFactory (
			 SecurityManager securityManager) {
		 ShiroFilterFactoryBean sfBean=
		 new ShiroFilterFactoryBean();
		 sfBean.setSecurityManager(securityManager);
 sfBean.setLoginUrl("/doLoginUI");
//定義map指定請求過濾規則(哪些資源允許匿名訪問,
哪些必須認證訪問)
		 LinkedHashMap<String,String> map=
				 new LinkedHashMap<>();
		 //靜態資源允許匿名訪問:"anon"
		 map.put("/bower_components/**","anon");
		 map.put("/modules/**","anon");
		 map.put("/dist/**","anon");
		 map.put("/plugins/**","anon");
		 //除了匿名訪問的資源,其它都要認證("authc")後訪問
		 map.put("/**","authc");
		 sfBean.setFilterChainDefinitionMap(map);
		 return sfBean;
}

2.2.2客戶端頁面實現

▪業務描述及設計實現。
在/templates/pages/新增一個login.html頁面,然後將專案部署到web伺服器,並啟動測試執行.
▪關鍵程式碼分析及實現。
具體程式碼見專案中login.html。

3Shiro框架認證業務實現

3.1認證流程分析

身份認證即判定使用者是否是系統的合法使用者,使用者訪問系統資源時的認證(對使用者身份資訊的認證)流程圖-5所示:
在這裡插入圖片描述

圖-5
其中認證流程分析如下:
1)系統呼叫subject的login方法將使用者資訊提交給SecurityManager
2)SecurityManager將認證操作委託給認證器物件Authenticator
3)Authenticator將使用者輸入的身份資訊傳遞給Realm。
4)Realm訪問資料庫獲取使用者資訊然後對資訊進行封裝並返回。
5)Authenticator 對realm返回的資訊進行身份認證。
思考:不使用shiro框架如何完成認證操作?filter,intercetor。

3.2認證服務端實現

3.2.1核心業務分析

認證業務API處理流程分析,如圖-6所示:
在這裡插入圖片描述

圖-6

3.2.2DAO介面定義

▪業務描述及設計實現。
在使用者資料層物件SysUserDao中,按特定條件查詢使用者資訊,並對其進行封裝。
▪關鍵程式碼分析及實現。
在SysUserDao介面中,新增根據使用者名稱獲取使用者物件的方法,關鍵程式碼如下:

SysUser findUserByUserName(String username)

3.2.3Mapper元素定義

▪業務描述及設計實現。
根據SysUserDao中定義的方法,在SysUserMapper檔案中新增元素定義。
▪關鍵程式碼分析及實現。
基於使用者名稱獲取使用者物件的方法,關鍵程式碼如下:

  <select id="findUserByUserName"
           resultType="com.cy.pj.sys.entity.SysUser">
      select *
      from sys_users  
      where username=#{username}
   </select>

3.2.4Service介面及實現

▪業務描述及設計實現。
本模組的業務在Realm型別的物件中進行實現,我們編寫realm時,要繼承
AuthorizingRealm並重寫相關方法,完成認證及授權業務資料的獲取及封裝。
▪關鍵程式碼分析及實現。
第一步:定義ShiroUserRealm類,關鍵程式碼如下:

package com.cy.pj.sys.service.realm;
@Service
public class ShiroUserRealm extends AuthorizingRealm {

	@Autowired
	private SysUserDao sysUserDao;
		
	/**
	 * 設定憑證匹配器(與使用者新增操作使用相同的加密演算法)
	 */
	@Override
	public void setCredentialsMatcher(
	      CredentialsMatcher credentialsMatcher) {
		//構建憑證匹配物件 
		HashedCredentialsMatcher cMatcher=
		new HashedCredentialsMatcher();
		//設定加密演算法
		cMatcher.setHashAlgorithmName("MD5");
		//設定加密次數
		cMatcher.setHashIterations(1);
		super.setCredentialsMatcher(cMatcher);
	}
	/**
	 * 通過此方法完成認證資料的獲取及封裝,系統
	 * 底層會將認證資料傳遞認證管理器,由認證
	 * 管理器完成認證操作。
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(
			AuthenticationToken token) 
			throws AuthenticationException {
		//1.獲取使用者名稱(使用者頁面輸入)
		UsernamePasswordToken upToken=
		(UsernamePasswordToken)token;
		String username=upToken.getUsername();
		//2.基於使用者名稱查詢使用者資訊
		SysUser user=
		sysUserDao.findUserByUserName(username);
		//3.判定使用者是否存在
		if(user==null)
		throw new UnknownAccountException();
		//4.判定使用者是否已被禁用。
		if(user.getValid()==0)
		throw new LockedAccountException();
		
		//5.封裝使用者資訊
		ByteSource credentialsSalt=
		ByteSource.Util.bytes(user.getSalt());
		//記住:構建什麼物件要看方法的返回值
		SimpleAuthenticationInfo info=
		new SimpleAuthenticationInfo(
				user,//principal (身份)
				user.getPassword(),//hashedCredentials
				credentialsSalt, //credentialsSalt
				getName());//realName
		//6.返回封裝結果
		return info;//返回值會傳遞給認證管理器(後續
		//認證管理器會通過此資訊完成認證操作)
	}
    ....
}

第二步:對此realm,需要在SpringShiroConfig配置類中,注入給SecurityManager物件,修改securityManager方法,見黃色背景部分,例如:

@Bean
public SecurityManager securityManager(Realm realm) {
		 DefaultWebSecurityManager sManager=
		 new DefaultWebSecurityManager();
		 sManager.setRealm(realm);
		 return sManager;
}

3.2.5Controller 類實現

▪業務描述及設計實現。
在此物件中定義相關方法,處理客戶端的登陸請求,例如獲取使用者名稱,密碼等然後提交該shiro框架進行認證。
▪關鍵程式碼分析及實現。
第一步:在SysUserController中新增處理登陸的方法。關鍵程式碼如下:

       @RequestMapping("doLogin")
	   public JsonResult doLogin(String username,String password){
		   //1.獲取Subject物件
		   Subject subject=SecurityUtils.getSubject();
		   //2.通過Subject提交使用者資訊,交給shiro框架進行認證操作
		   //2.1對使用者進行封裝
		   UsernamePasswordToken token=
		   new UsernamePasswordToken(
				   username,//身份資訊
				   password);//憑證資訊
		   //2.2對使用者資訊進行身份認證
		   subject.login(token);
		   //分析:
		   //1)token會傳給shiro的SecurityManager
		   //2)SecurityManager將token傳遞給認證管理器
		   //3)認證管理器會將token傳遞給realm
		   return new JsonResult("login ok");
	   }

第二步:修改shiroFilterFactory的配置,對/user/doLogin這個路徑進行匿名訪問的配置,檢視如下黃色標記部分的程式碼:

@Bean
public ShiroFilterFactoryBean shiroFilterFactory (
			 SecurityManager securityManager) {
		 ShiroFilterFactoryBean sfBean=
		 new ShiroFilterFactoryBean();
		 sfBean.setSecurityManager(securityManager);
		 //假如沒有認證請求先訪問此認證的url
		 sfBean.setLoginUrl("/doLoginUI");
		 //定義map指定請求過濾規則(哪些資源允許匿名訪問,哪些必須認證訪問)
		 LinkedHashMap<String,String> map=
				 new LinkedHashMap<>();
		 //靜態資源允許匿名訪問:"anon"
		 map.put("/bower_components/**","anon");
		 map.put("/build/**","anon");
		 map.put("/dist/**","anon");
		 map.put("/plugins/**","anon");

  map.put("/user/doLogin","anon");                       //authc表示,除了匿名訪問的資源,其它都要認證("authc")後才能訪問訪問
		 map.put("/**","authc");
		 sfBean.setFilterChainDefinitionMap(map);
		 return sfBean;
	 }

第三步:當我們在執行登入操作時,為了提高使用者體驗,可對系統中的異常資訊進行處理,例如,在統一異常處理類中新增如下方法:

@ExceptionHandler(ShiroException.class) 
   @ResponseBody
	public JsonResult doHandleShiroException(
			ShiroException e) {
		JsonResult r=new JsonResult();
		r.setState(0);
		if(e instanceof UnknownAccountException) {
			r.setMessage("賬戶不存在");
		}else if(e instanceof LockedAccountException) {
			r.setMessage("賬戶已被禁用");
		}else if(e instanceof IncorrectCredentialsException) {
			r.setMessage("密碼不正確");
		}else if(e instanceof AuthorizationException) {
			r.setMessage("沒有此操作許可權");
		}else {
			r.setMessage("系統維護中");
		}
		e.printStackTrace();
		return r;
	}

3.3認證客戶端實現

3.3.1編寫使用者登陸頁面

在/templates/pages/目錄下新增登陸頁面(login.html)。

3.3.2非同步登陸操作實現

點選登入操作時,將輸入的使用者名稱,密碼非同步提交到服務端。

$(function () {
    $(".login-box-body").on("click",".btn",doLogin);
  });
  function doLogin(){
	  var params={
		 username:$("#usernameId").val(),
		 password:$("#passwordId").val()
	  }
	  var url="user/doLogin";
	  $.post(url,params,function(result){
		  if(result.state==1){
			//跳轉到indexUI對應的頁面
			location.href="doIndexUI?t="+Math.random();
		  }else{
			$(".login-box-msg").html(result.message); 
		  }
	  });
  }

3.4退出操作配置實現

在SpringShiroConfig配置類中,修改過濾規則,新增黃色標記部分程式碼的配置,請看如下程式碼:

@Bean
public ShiroFilterFactoryBean shiroFilterFactory(
			SecurityManager securityManager) {
		 ShiroFilterFactoryBean sfBean=
		 new ShiroFilterFactoryBean();
		 sfBean.setSecurityManager(securityManager);
		 //假如沒有認證請求先訪問此認證的url
		 sfBean.setLoginUrl("/doLoginUI");
		 //定義map指定請求過濾規則(哪些資源允許匿名訪問,哪些必須認證訪問)
		 LinkedHashMap<String,String> map=new LinkedHashMap<>();
		 //靜態資源允許匿名訪問:"anon"
		 map.put("/bower_components/**","anon");
		 map.put("/build/**","anon");
		 map.put("/dist/**","anon");
		 map.put("/plugins/**","anon");
  map.put("/user/doLogin","anon");
  map.put("/doLogout","logout"); 
            											      //除了匿名訪問的資源,其它都要認證("authc")後訪問
		 map.put("/**","authc");
		 sfBean.setFilterChainDefinitionMap(map);
		 return sfBean;
	 }

4Shiro框架授權過程實現

4.1授權流程分析

授權即對使用者資源訪問的授權(是否允許使用者訪問此資源),使用者訪問系統資源時的授權流程如圖-7所示:
在這裡插入圖片描述

圖-7
其中授權流程分析如下:
1)系統呼叫subject相關方法將使用者資訊(例如isPermitted)遞交給SecurityManager。
2)SecurityManager將許可權檢測操作委託給Authorizer物件。
3)Authorizer將使用者資訊委託給realm。
4)Realm訪問資料庫獲取使用者許可權資訊並封裝。
5)Authorizer對使用者授權資訊進行判定。
思考:思考不使用shiro如何完成授權操作?intercetor,aop。

4.2新增授權配置

在SpringShiroConfig配置類中,新增授權時的相關配置:
第一步:配置bean物件的生命週期管理(SpringBoot可以不配置)。

@Bean
public LifecycleBeanPostProcessor   lifecycleBeanPostProcessor() {
		 return new LifecycleBeanPostProcessor();
}

第二步: 通過如下配置要為目標業務物件建立代理物件(SpringBoot中可省略)。

@DependsOn("lifecycleBeanPostProcessor")
@Bean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
		 return new DefaultAdvisorAutoProxyCreator();
}
    

第三步:配置advisor物件,shiro框架底層會通過此物件的matchs方法返回值(類似切入點)決定是否建立代理物件,進行許可權控制。

@Bean
public AuthorizationAttributeSourceAdvisor 
authorizationAttributeSourceAdvisor (
	    		    SecurityManager securityManager) {
		        AuthorizationAttributeSourceAdvisor advisor=
				new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(securityManager);
	return advisor;
}

說明:使用框架最重要的尊重規則,框架規則指定了什麼方式就使用什麼方式。

4.3授權服務端實現

4.3.1核心業務分析

授權時,服務端核心業務以及API分析,如圖-8所示:
在這裡插入圖片描述

圖-8

4.3.2Dao實現

▪業務描述及設計實現。
基於登陸使用者ID,認證資訊獲取登陸使用者的許可權資訊,並進行封裝。
▪關鍵程式碼分析及實現。
第一步:在SysUserRoleDao中定義基於使用者id查詢角色id的方法(假如方法已經存在則無需再寫),關鍵程式碼如下:

List<Integer> findRoleIdsByUserId(Integer id);

第二步:在SysRoleMenuDao中定義基於角色id查詢選單id的方法,關鍵程式碼如下:

	List<Integer> findMenuIdsByRoleIds(
			@Param("roleIds")List<Integer> roleIds);

第三步:在SysMenuDao中基於選單id查詢許可權標識的方法,關鍵程式碼如下:

	List<String> findPermissions(
			@Param("menuIds")
			List<Integer> menuIds);

4.3.3Mapper實現

▪業務描述及設計實現。
基於Dao中方法,定義對映元素。
▪關鍵程式碼分析及實現。
第一步:在SysUserRoleMapper中定義findRoleIdsByUserId元素。關鍵程式碼如下:

 <select id="findRoleIdsByUserId"
            resultType="int">
           select role_id
           from sys_user_roles
           where user_id=#{userId}        
</select>

第二步:在SysRoleMenuMapper中定義findMenuIdsByRoleIds元素。關鍵程式碼如下:

    <select id="findMenuIdsByRoleIds"
         resultType="int">
         select menu_id
         from sys_role_menus
         where role_id in 
         <foreach collection="roleIds"
                  open="("
                  close=")"
                  separator=","
                  item="item">
               #{item}
         </foreach>
</select>

第三步:在SysMenuMapper中定義findPermissions元素,關鍵程式碼如下:

   <select id="findPermissions"
           resultType="string">
       select permission <!-- sys:user:update -->
       from sys_menus
       where id in 
       <foreach collection="menuIds"
                open="("
                close=")"
                separator=","
                item="item">
            #{item}
       </foreach>
   </select>

4.3.4Service實現

▪業務描述及設計實現。
在ShiroUserReam類中,重寫物件realm的doGetAuthorizationInfo方法,並完成使用者許可權資訊的獲取以及封裝,最後將資訊傳遞給授權管理器完成授權操作。
▪關鍵程式碼分析及實現。
修改ShiroUserRealm類中的doGetAuthorizationInfo方法,關鍵程式碼如下:

@Service
public class ShiroUserRealm extends AuthorizingRealm {
	@Autowired
	private SysUserDao sysUserDao;
	@Autowired
	private SysUserRoleDao sysUserRoleDao;
	@Autowired
	private SysRoleMenuDao sysRoleMenuDao;
	@Autowired
	private SysMenuDao sysMenuDao;
	/**通過此方法完成授權資訊的獲取及封裝*/
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(
		PrincipalCollection principals) {
		//1.獲取登入使用者資訊,例如使用者id
		SysUser user=(SysUser)principals.getPrimaryPrincipal();
		Integer userId=user.getId();
		//2.基於使用者id獲取使用者擁有的角色(sys_user_roles)
		List<Integer> roleIds=
		sysUserRoleDao.findRoleIdsByUserId(userId);
		if(roleIds==null||roleIds.size()==0)
		throw new AuthorizationException();
		//3.基於角色id獲取選單id(sys_role_menus)
		List<Integer> menuIds=
		sysRoleMenuDao.findMenuIdsByRoleIds(roleIds);
	    if(menuIds==null||menuIds.size()==0)
	    throw new AuthorizationException();
		//4.基於選單id獲取許可權標識(sys_menus)
	    List<String> permissions=
	    sysMenuDao.findPermissions(menuIds);
		//5.對許可權標識資訊進行封裝並返回
	    Set<String> set=new HashSet<>();
	    for(String per:permissions){
	    	if(!StringUtils.isEmpty(per)){
	    		set.add(per);
	    	}
	    }
	    SimpleAuthorizationInfo info=
	    new SimpleAuthorizationInfo();
	    info.setStringPermissions(set);
		return info;//返回給授權管理器
	}
   。。。。

}

4.4授權訪問實描述現

在需要進行授權訪問的業務層(Service)方法上,新增執行此方法需要的許可權標識,參考程式碼

@RequiresPermissions(“sys:user:update”)

說明:此要註解一定要新增到業務層方法上。

5Shiro擴充套件功能應用

5.1Shiro快取配置

當我們進行授權操作時,每次都會從資料庫查詢使用者許可權資訊,為了提高授權效能,可以將使用者許可權資訊查詢出來以後進行快取,下次授權時從快取取資料即可。
Shiro中內建快取應用實現,其步驟如下:
第一步:在SpringShiroConfig中配置快取Bean物件(Shiro框架提供)。

@Bean
public CacheManager shiroCacheManager(){
	 return new MemoryConstrainedCacheManager();
}

說明:這個CacheManager物件的名字不能寫cacheManager,因為spring容器中已經存在一個名字為cacheManager的物件了.
第二步:修改securityManager的配置,將快取物件注入給SecurityManager物件。

@Bean
public SecurityManager securityManager(
			Realm realm,
			CacheManager cacheManager) {
		 DefaultWebSecurityManager sManager=
		 new DefaultWebSecurityManager();
		 sManager.setRealm(realm);
		 sManager.setCacheManager(cacheManager);
		 return sManager;
}

說明:對於shiro框架而言,還可以藉助第三方的快取產品(例如redis)對使用者的許可權資訊進行cache操作.

5.2Shiro記住我

記住我功能是要在使用者登入成功以後,假如關閉瀏覽器,下次再訪問系統資源(例如首頁doIndexUI)時,無需再執行登入操作。

5.2.1客戶端業務實現

在頁面上選中記住我,然後執行提交操作,將使用者名稱,密碼,記住我對應的值提交到控制層,如圖-9所示:
在這裡插入圖片描述

圖-9
其客戶端login.html中關鍵JS實現:

 function doLogin(){
	  var params={
		 username:$("#usernameId").val(),
		 password:$("#passwordId").val(),
		 isRememberMe:$("#rememberId").prop("checked"),
	  }
	  var url="user/doLogin";
	  console.log("params",params);
	  $.post(url,params,function(result){
		  if(result.state==1){
			//跳轉到indexUI對應的頁面
			location.href="doIndexUI?t="+Math.random();
		  }else{
			$(".login-box-msg").html(result.message); 
		  }
		  return false;//防止重新整理時重複提交
	  });
  }

5.2.2服務端業務實現

服務端業務實現的具體步驟如下:
第一步:在SysUserController中的doLogin方法中基於是否選中記住我,設定token的setRememberMe方法。

@RequestMapping("doLogin")
	 @ResponseBody
	 public JsonResult doLogin(
			 boolean isRememberMe,
			 String username,
			 String password) {
		 //1.封裝使用者資訊
		 UsernamePasswordToken token=
		 new UsernamePasswordToken(username, password);
		 if(isRememberMe) {
			token.setRememberMe(true); 
		 }
		 //2.提交使用者資訊
		 Subject subject=SecurityUtils.getSubject();
		 subject.login(token);//token會提交給securityManager
		 return new JsonResult("login ok");
	 }

第二步:在SpringShiroConfig配置類中新增記住我配置,關鍵程式碼如下:

     @Bean
	 public RememberMeManager rememberMeManager() {
		 CookieRememberMeManager cManager=
		 new CookieRememberMeManager();
  SimpleCookie cookie=new SimpleCookie("rememberMe");
		 cookie.setMaxAge(7*24*60*60);
		 cManager.setCookie(cookie);
		 return cManager;
	 }

第三步:在SpringShiroConfig中修改securityManager的配置,為
securityManager注入rememberManager物件。參考黃色部分程式碼。

	 @Bean
	 public SecurityManager securityManager(
			Realm realm,CacheManager cacheManager
RememberMeManager rememberManager) {
		 DefaultWebSecurityManager sManager=
		 new DefaultWebSecurityManager();
		 sManager.setRealm(realm);
		 sManager.setCacheManager(cacheManager);
		 sManager.setRememberMeManager(rememberManager);
		 return sManager;
	 }

第四步:修改shiro的過濾認證級別,將/=author修改為/=user,檢視黃色背景部分。

@Bean
	 public ShiroFilterFactoryBean shiroFilterFactory(
			 SecurityManager securityManager) {
		 ShiroFilterFactoryBean sfBean=
		 new ShiroFilterFactoryBean();
		 sfBean.setSecurityManager(securityManager);
		 //假如沒有認證請求先訪問此認證的url
		 sfBean.setLoginUrl("/doLoginUI");
		 //定義map指定請求過濾規則(哪些資源允許匿名訪問,哪些必須認證訪問)
		 LinkedHashMap<String,String> map=
				 new LinkedHashMap<>();
		 //靜態資源允許匿名訪問:"anon"
		 map.put("/bower_components/**","anon");
		 map.put("/build/**","anon");
		 map.put("/dist/**","anon");
		 map.put("/plugins/**","anon");
		 map.put("/user/doLogin","anon");
		 map.put("/doLogout", "logout");//自動查LoginUrl
		 //除了匿名訪問的資源,其它都要認證("authc")後訪問
		 map.put("/**","user");//authc
		 sfBean.setFilterChainDefinitionMap(map);
		 return sfBean;
	 }

說明:檢視瀏覽器cookie設定,可在瀏覽器中輸入如下語句。
chrome://settings/content/cookies

5.3Shiro會話管理配置

使用shiro框架實現認證操作,使用者登入成功會將使用者資訊寫入到會話物件中,其預設時長為30分鐘,假如需要對此進行配置,可參考如下配置:
第一步:在SpringShiroConfig類中,新增會話管理器配置。關鍵程式碼如下:

@Bean   
public SessionManager sessionManager() {
		 DefaultWebSessionManager sManager=
				 new DefaultWebSessionManager();
		 sManager.setGlobalSessionTimeout(60*60*1000);
		 return sManager;
}

第二步:在SpringShiroConfig配置類中,對安全管理器 securityManager 增加 sessionManager值的注入,關鍵程式碼如下:

@Bean
public SecurityManager securityManager(
			Realm realm,CacheManager cacheManager,
RememberMeManager rememberManager,
SessionManager sessionManager) {
		 DefaultWebSecurityManager sManager=
		 new DefaultWebSecurityManager();
		 sManager.setRealm(realm);
		 sManager.setCacheManager(cacheManager);
		 sManager.setRememberMeManager(rememberMeManager);
		 sManager.setSessionManager(sessionManager);
		 return sManager;
}

課堂練習:
1.獲取使用者登陸資訊,並將登陸使用者名稱呈現在系統主頁(starter.html)上.
第一步:定義一個工具類(ShiroUtils),獲取使用者登陸資訊.

package com.cy.pj.common.util;
import org.apache.shiro.SecurityUtils;
import com.cy.pj.sys.entity.SysUser;
public class ShiroUtils {
	  public static String getUsername() {
		  return getUser().getUsername();
	  }
	  public static SysUser getUser() {
		  return  (SysUser)
           SecurityUtils.getSubject().getPrincipal();
	  }
}

第二步:修改PageController中的doIndexUI方法,程式碼如下:

	@RequestMapping("doIndexUI")
	public String doIndexUI(Model model) {
		SysUser user=ShiroUtils.getUser();
		model.addAttribute("user",user);
		return "starter";
	}

第三步:藉助thymeleaf中的表示式直接在頁面上(starter.html)獲取登陸使用者資訊

  <span class="hidden-xs" id="loginUserId">[[${user.username}]]</span>

2.修改登陸使用者的密碼?(參考使用者模組文件)

分析:
1)確定都要修改誰?(密碼,鹽值,修改時間)
2)服務端的設計實現?(dao,service,controller)
3)客戶端的設計實現?(非同步提交使用者密碼資訊)

6Shiro總結

6.1重點和難點分析

1.shiro 認證過程分析及實現(判定使用者身份的合法性)。
2.Shiro 授權過程分析及實現(對資源訪問進行許可權檢測和授權)。
3.Shiro 快取,會話時長,記住我等功能實現。

6.2常見FAQ

1.說說shiro的核心元件?
2.說說shiro的認證流程,你如何知道的,為什麼要認證?
3.說說shiro的授權流程,你如何知道流程是這樣的,為什麼要進行授權?
4.Shiro中內建快取應用實現?為什麼使用此快取?是否可以使用第三方快取?
5.Shiro中的記住我功能如何實現?為什麼要使用這個功能?
6.Shiro中會話session的預設時長是多少,你怎麼知道的?

6.3Bug分析

1.SecurityManager包名錯誤。
2.MD5加密演算法設定錯誤。
3.Realm物件沒有交給spring管理
4.使用者名稱和密碼接收錯誤
5.CacheManager名字與Spring中內建的CacheManager名字衝突。
6.過濾規則配置錯誤?
7…

相關文章