Shiro入門這篇就夠了【Shiro的基礎知識、回顧URL攔截】

Java3y發表於2018-03-21

前言

本文主要講解的知識點有以下:

  • 許可權管理的基礎知識
    • 模型
    • 粗粒度和細粒度的概念
  • 回顧URL攔截的實現
  • Shiro的介紹與簡單入門

一、Shiro基礎知識

在學習Shiro這個框架之前,首先我們要先了解Shiro需要的基礎知識:許可權管理

1.1什麼是許可權管理?

只要有使用者參與的系統一般都要有許可權管理,許可權管理實現對使用者訪問系統的控制,按照安全規則或者安全策略控制使用者可以訪問而且只能訪問自己被授權的資源

對許可權的管理又分為兩大類別:

  • 使用者認證
  • 使用者授權

1.1.1使用者認證

使用者認證,使用者去訪問系統,系統要驗證使用者身份的合法性

最常用的使用者身份驗證的方法:1、使用者名稱密碼方式、2、指紋打卡機、3、基於證照驗證方法。。系統驗證使用者身份合法,使用者方可訪問系統的資源。

舉個例子:

  • 當我們輸入了自己的淘寶的賬戶和密碼,才能開啟購物車

使用者認證的流程:

  • 判斷該資源能否不認證就能訪問【登陸頁面、首頁】
  • 如果該資源需要認證後才能訪問,那麼判斷該訪問者是否認證了
  • 如果還沒有認證,那麼需要返回到【登陸頁面】進行認證
  • 認證通過後才能訪問資源

這裡寫圖片描述

從使用者認證我們可以抽取出這麼幾個概念

  • subject主體:理解為使用者,可能是程式,都要去訪問系統的資源,系統需要對subject進行身份認證
  • principal身份資訊:通常是唯一的,一個主體還有多個身份資訊,但是都有一個主身份資訊(primary principal)【我們可以選擇身份證認證、學生證認證等等都是我們的身份資訊】
  • credential憑證資訊:可以是密碼 、證照、指紋。

總結:主體在進行身份認證時需要提供身份資訊和憑證資訊。

1.1.2使用者授權

使用者授權,簡單理解為訪問控制,在使用者認證通過後,系統對使用者訪問資源進行控制,使用者具有資源的訪問許可權方可訪問

使用者授權的流程

  • 到達了使用者授權環節,當然是需要使用者認證之後了
  • 使用者訪問資源,系統判斷該使用者是否有許可權去操作該資源
  • 如果該使用者有許可權才能夠訪問,如果沒有許可權就不能訪問了

這裡寫圖片描述

授權的過程可以簡單理解為:主體認證之後,系統進行訪問控制

subject必須具備資源的訪問許可權才可訪問該資源..

許可權/許可(permission) :針對資源的許可權或許可,subject具有permission訪問資源,如何訪問/操作需要定義permission,許可權比如:使用者新增、使用者修改、商品刪除

資源可以分為兩種

  • 資源型別:系統的使用者資訊就是資源型別,相當於java類。
  • 資源例項:系統中id為001的使用者就是資源例項,相當於new的java物件。

1.2許可權管理模型

一般地,我們可以抽取出這麼幾個模型:

  • 主體(賬號、密碼)
  • 資源(資源名稱、訪問地址)
  • 許可權(許可權名稱、資源id)
  • 角色(角色名稱)
  • 角色和許可權關係(角色id、許可權id)
  • 主體和角色關係(主體id、角色id)

這裡寫圖片描述

通常企業開發中將資源和許可權表合併為一張許可權表,如下:

  • 資源(資源名稱、訪問地址)
  • 許可權(許可權名稱、資源id)

合併為:

  • 許可權(許可權名稱、資源名稱、資源訪問地址)

這裡寫圖片描述

1.3分配許可權

使用者需要分配相應的許可權才可訪問相應的資源。許可權是對於資源的操作許可。

通常給使用者分配資源許可權需要將許可權資訊持久化,比如儲存在關聯式資料庫中。把使用者資訊、許可權管理、使用者分配的許可權資訊寫到資料庫(許可權資料模型)

1.3.1基於角色訪問控制

RBAC(role based access control),基於角色的訪問控制。


//如果該user是部門經理則可以訪問if中的程式碼
if(user.hasRole('部門經理')){
	//系統資源內容
	//使用者報表檢視
}
複製程式碼

角色針對人劃分的,人作為使用者在系統中屬於活動內容,如果該 角色可以訪問的資源出現變更,需要修改你的程式碼了


if(user.hasRole('部門經理') || user.hasRole('總經理')  ){
	//系統資源內容
	//使用者報表檢視
}
複製程式碼

基於角色的訪問控制是不利於系統維護(可擴充套件性不強)。

1.3.2基於資源的訪問控制

RBAC(Resource based access control),基於資源的訪問控制。

資源在系統中是不變的,比如資源有:類中的方法,頁面中的按鈕。


對資源的訪問需要具有permission許可權,程式碼可以寫為:

if(user.hasPermission ('使用者報表檢視(許可權識別符號)')){
	//系統資源內容
	//使用者報表檢視
}
複製程式碼

建議使用基於資源的訪問控制實現許可權管理


二、 粗粒度和細粒度許可權

細粒度許可權管理:對資源例項的許可權管理。資源例項就資源型別的具體化,比如:使用者id為001的修改連線,1110班的使用者資訊、行政部的員工。細粒度許可權管理就是資料級別的許可權管理。

粗粒度許可權管理比如:超級管理員可以訪問戶新增頁面、使用者資訊等全部頁面。部門管理員可以訪問使用者資訊頁面包括 頁面中所有按鈕。

粗粒度和細粒度例子


系統有一個使用者列表查詢頁面,對使用者列表查詢分許可權,

如果粗顆粒管理,張三和李四都有使用者列表查詢的許可權,張三和李四都可以訪問使用者列表查詢。

進一步進行細顆粒管理,張三(行政部)和李四(開發部)只可以查詢自己本部門的使用者資訊。

張三隻能檢視行政部 的使用者資訊,李四隻能檢視開發部門的使用者資訊。

細粒度許可權管理就是資料級別的許可權管理。

複製程式碼

2.1如何實現粗粒度許可權管理?

粗粒度許可權管理比較容易將許可權管理的程式碼抽取出來在系統架構級別統一處理。比如:通過springmvc的攔截器實現授權

對細粒度許可權管理在資料級別是沒有共性可言,針對細粒度許可權管理就是系統業務邏輯的一部分在業務層去處理相對比較簡單

比如:部門經理只查詢本部門員工資訊,在service介面提供一個部門id的引數,controller中根據當前使用者的資訊得到該 使用者屬於哪個部門,呼叫service時將部門id傳入service,實現該使用者只查詢本部門的員工。

2.1.1基於URL攔截

基於url攔截的方式實現在實際開發中比較常用的一種方式。

對於web系統,通過filter過慮器實現url攔截,也可以springmvc的攔截器實現基於url的攔截。

2.2.2使用許可權管理框架實現

對於粗粒度許可權管理,建議使用優秀許可權管理框架來實現,節省開發成功,提高開發效率。

shiro就是一個優秀許可權管理框架。

三、回顧URL攔截

我們在學習的路途上也是使用過幾次URL對許可權進行攔截的

當時我們做了許可權的增刪該查的管理系統,但是在許可權表中是沒有把資源新增進去,我們使用的是Map集合來進行替代的blog.csdn.net/hon_3y/arti…

隨後,我們學習了動態代理和註解,我們也做了一個基於註解的攔截

  • 在Controller得到service物件的時候,service工廠返回的是一個動態代理物件回去
  • Controller拿著代理物件去呼叫方法,代理物件就會去解析該方法上是否有註解
  • 如果有註解,那麼就需要我們進行判斷該主體是否認證了,如果認證了就判斷該主體是否有許可權
  • 當我們解析出該主體的許可權和我們註解的許可權是一致的時候,才放行!

blog.csdn.net/hon_3y/arti…

流程:

這裡寫圖片描述

3.1認證的JavaBean

我們之前認證都是放在預設的Javabean物件上的,現在既然我們準備學Shiro了,我們就得專業一點,弄一個專門儲存認證資訊的JavaBean



/**
 * 使用者身份資訊,存入session 由於tomcat將session會序列化在本地硬碟上,所以使用Serializable介面
 * 
 * @author Thinkpad
 * 
 */
public class ActiveUser implements java.io.Serializable {
	private String userid;//使用者id(主鍵)
	private String usercode;// 使用者賬號
	private String username;// 使用者名稱稱

	private List<SysPermission> menus;// 選單
	private List<SysPermission> permissions;// 許可權

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}


	public String getUsercode() {
		return usercode;
	}

	public void setUsercode(String usercode) {
		this.usercode = usercode;
	}

	public String getUserid() {
		return userid;
	}

	public void setUserid(String userid) {
		this.userid = userid;
	}

	public List<SysPermission> getMenus() {
		return menus;
	}

	public void setMenus(List<SysPermission> menus) {
		this.menus = menus;
	}

	public List<SysPermission> getPermissions() {
		return permissions;
	}

	public void setPermissions(List<SysPermission> permissions) {
		this.permissions = permissions;
	}

	
}

複製程式碼

認證的服務


	@Override
	public ActiveUser authenticat(String userCode, String password)
			throws Exception {
		/**
	認證過程:
	根據使用者身份(賬號)查詢資料庫,如果查詢不到使用者不存在
	對輸入的密碼 和資料庫密碼 進行比對,如果一致,認證通過
		 */
		//根據使用者賬號查詢資料庫
		SysUser sysUser = this.findSysUserByUserCode(userCode);
		
		if(sysUser == null){
			//丟擲異常
			throw new CustomException("使用者賬號不存在");
		}
		
		//資料庫密碼 (md5密碼 )
		String password_db = sysUser.getPassword();
		
		//對輸入的密碼 和資料庫密碼 進行比對,如果一致,認證通過
		//對頁面輸入的密碼 進行md5加密 
		String password_input_md5 = new MD5().getMD5ofStr(password);
		if(!password_input_md5.equalsIgnoreCase(password_db)){
			//丟擲異常
			throw new CustomException("使用者名稱或密碼 錯誤");
		}
		//得到使用者id
		String userid = sysUser.getId();
		//根據使用者id查詢選單 
		List<SysPermission> menus =this.findMenuListByUserId(userid);
		
		//根據使用者id查詢許可權url
		List<SysPermission> permissions = this.findPermissionListByUserId(userid);
		
		//認證通過,返回使用者身份資訊
		ActiveUser activeUser = new ActiveUser();
		activeUser.setUserid(sysUser.getId());
		activeUser.setUsercode(userCode);
		activeUser.setUsername(sysUser.getUsername());//使用者名稱稱
		
		//放入許可權範圍的選單和url
		activeUser.setMenus(menus);
		activeUser.setPermissions(permissions);
		
		return activeUser;
	}
複製程式碼

Controller處理認證,如果身份認證成功,那麼把認證資訊儲存在Session中

	@RequestMapping("/login")
	public String login(HttpSession session, String randomcode,String usercode,String password)throws Exception{
		//校驗驗證碼,防止惡性攻擊
		//從session獲取正確驗證碼
		String validateCode = (String) session.getAttribute("validateCode");
		
		//輸入的驗證和session中的驗證進行對比 
		if(!randomcode.equals(validateCode)){
			//丟擲異常
			throw new CustomException("驗證碼輸入錯誤");
		}
		
		//呼叫service校驗使用者賬號和密碼的正確性
		ActiveUser activeUser = sysService.authenticat(usercode, password);
		
		//如果service校驗通過,將使用者身份記錄到session
		session.setAttribute("activeUser", activeUser);
		//重定向到商品查詢頁面
		return "redirect:/first.action";
	}
	
複製程式碼

身份認證攔截器



	//在執行handler之前來執行的
	//用於使用者認證校驗、使用者許可權校驗
	@Override
	public boolean preHandle(HttpServletRequest request,
			HttpServletResponse response, Object handler) throws Exception {
		
		//得到請求的url
		String url = request.getRequestURI();
		//判斷是否是公開 地址
		//實際開發中需要公開 地址配置在配置檔案中
		//從配置中取逆名訪問url
		List<String> open_urls = ResourcesUtil.gekeyList("anonymousURL");
		//遍歷公開 地址,如果是公開 地址則放行
		for(String open_url:open_urls){
			if(url.indexOf(open_url)>=0){
				//如果是公開 地址則放行
				return true;
			}
		}
		//判斷使用者身份在session中是否存在
		HttpSession session = request.getSession();
		ActiveUser activeUser = (ActiveUser) session.getAttribute("activeUser");
		//如果使用者身份在session中存在放行
		if(activeUser!=null){
			return true;
		}
		//執行到這裡攔截,跳轉到登陸頁面,使用者進行身份認證
		request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response);
		
		//如果返回false表示攔截不繼續執行handler,如果返回true表示放行
		return false;
	}
複製程式碼

授權攔截器


	//在執行handler之前來執行的
	//用於使用者認證校驗、使用者許可權校驗
	@Override
	public boolean preHandle(HttpServletRequest request,
			HttpServletResponse response, Object handler) throws Exception {
		
		//得到請求的url
		String url = request.getRequestURI();
		//判斷是否是公開 地址
		//實際開發中需要公開 地址配置在配置檔案中
		//從配置中取逆名訪問url
		
		List<String> open_urls = ResourcesUtil.gekeyList("anonymousURL");
		//遍歷公開 地址,如果是公開 地址則放行
		for(String open_url:open_urls){
			if(url.indexOf(open_url)>=0){
				//如果是公開 地址則放行
				return true;
			}
		}
		//從配置檔案中獲取公共訪問地址
		List<String> common_urls = ResourcesUtil.gekeyList("commonURL");
		//遍歷公用 地址,如果是公用 地址則放行
		for(String common_url:common_urls){
			if(url.indexOf(common_url)>=0){
				//如果是公開 地址則放行
				return true;
			}
		}
		//獲取session
		HttpSession session = request.getSession();
		ActiveUser activeUser = (ActiveUser) session.getAttribute("activeUser");
		//從session中取許可權範圍的url
		List<SysPermission> permissions = activeUser.getPermissions();
		for(SysPermission sysPermission:permissions){
			//許可權的url
			String permission_url = sysPermission.getUrl();
			if(url.indexOf(permission_url)>=0){
				//如果是許可權的url 地址則放行
				return true;
			}
		}
		
		//執行到這裡攔截,跳轉到無權訪問的提示頁面
		request.getRequestDispatcher("/WEB-INF/jsp/refuse.jsp").forward(request, response);
		
		//如果返回false表示攔截不繼續執行handler,如果返回true表示放行
		return false;
	}
複製程式碼

攔截器配置:



	<!--攔截器 -->
	<mvc:interceptors>

		<mvc:interceptor>
			<!-- 使用者認證攔截 -->
			<mvc:mapping path="/**" />
			<bean class="cn.itcast.ssm.controller.interceptor.LoginInterceptor"></bean>
		</mvc:interceptor>
		<mvc:interceptor>
			<!-- 授權攔截 -->
			<mvc:mapping path="/**" />
			<bean class="cn.itcast.ssm.controller.interceptor.PermissionInterceptor"></bean>
		</mvc:interceptor>
	</mvc:interceptors>

複製程式碼

四、什麼是Shiro

shiro是apache的一個開源框架,是一個許可權管理的框架,實現 使用者認證、使用者授權

spring中有spring security (原名Acegi),是一個許可權框架,它和spring依賴過於緊密,沒有shiro使用簡單。 shiro不依賴於spring,shiro不僅可以實現 web應用的許可權管理,還可以實現c/s系統,分散式系統許可權管理,shiro屬於輕量框架,越來越多企業專案開始使用shiro。

Shiro架構:

這裡寫圖片描述

  • subject:主體,可以是使用者也可以是程式,主體要訪問系統,系統需要對主體進行認證、授權。
  • securityManager:安全管理器,主體進行認證和授權都 是通過securityManager進行。
  • authenticator:認證器,主體進行認證最終通過authenticator進行的。
  • authorizer:授權器,主體進行授權最終通過authorizer進行的。
  • sessionManager:web應用中一般是用web容器對session進行管理,shiro也提供一套session管理的方式。
  • SessionDao: 通過SessionDao管理session資料,針對個性化的session資料儲存需要使用sessionDao。
  • cache Manager:快取管理器,主要對session和授權資料進行快取,比如將授權資料通過cacheManager進行快取管理,和ehcache整合對快取資料進行管理。
  • realm:域,領域,相當於資料來源,通過realm存取認證、授權相關資料。

cryptography:密碼管理,提供了一套加密/解密的元件,方便開發。比如提供常用的雜湊、加/解密等功能。

  • 比如md5雜湊演算法。

五、為什麼使用Shiro

我們在使用URL攔截的時候,要將所有的URL都配置起來,繁瑣、不易維護

而我們的Shiro實現系統的許可權管理,有效提高開發效率,從而降低開發成本。

六、Shiro認證

6.1匯入jar包

我們使用的是Maven的座標就行了


	<dependency>
			<groupId>org.apache.shiro</groupId>
			<artifactId>shiro-core</artifactId>
			<version>1.2.3</version>
		</dependency>
		<dependency>
			<groupId>org.apache.shiro</groupId>
			<artifactId>shiro-web</artifactId>
			<version>1.2.3</version>
		</dependency>
		<dependency>
			<groupId>org.apache.shiro</groupId>
			<artifactId>shiro-spring</artifactId>
			<version>1.2.3</version>
		</dependency>
		<dependency>
			<groupId>org.apache.shiro</groupId>
			<artifactId>shiro-ehcache</artifactId>
			<version>1.2.3</version>
		</dependency>
		<dependency>
			<groupId>org.apache.shiro</groupId>
			<artifactId>shiro-quartz</artifactId>
			<version>1.2.3</version>
		</dependency>
複製程式碼

當然了,我們也可以把Shiro相關的jar包全部匯入進去


<dependency>
	<groupId>org.apache.shiro</groupId>
	<artifactId>shiro-all</artifactId>
	<version>1.2.3</version>
</dependency>


複製程式碼

6.2Shiro認證流程

這裡寫圖片描述

6.2.1通過配置檔案建立工廠

這裡寫圖片描述


	// 使用者登陸和退出
	@Test
	public void testLoginAndLogout() {

		// 建立securityManager工廠,通過ini配置檔案建立securityManager工廠
		Factory<SecurityManager> factory = new IniSecurityManagerFactory(
				"classpath:shiro-first.ini");

		// 建立SecurityManager
		SecurityManager securityManager = factory.getInstance();

		// 將securityManager設定當前的執行環境中
		SecurityUtils.setSecurityManager(securityManager);

		// 從SecurityUtils裡邊建立一個subject
		Subject subject = SecurityUtils.getSubject();

		// 在認證提交前準備token(令牌)
		// 這裡的賬號和密碼 將來是由使用者輸入進去
		UsernamePasswordToken token = new UsernamePasswordToken("zhangsan",
				"111111");
		try {
			// 執行認證提交
			subject.login(token);
		} catch (AuthenticationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		// 是否認證通過
		boolean isAuthenticated = subject.isAuthenticated();

		System.out.println("是否認證通過:" + isAuthenticated);

		// 退出操作
		subject.logout();

		// 是否認證通過
		isAuthenticated = subject.isAuthenticated();

		System.out.println("是否認證通過:" + isAuthenticated);

	}
複製程式碼

這裡寫圖片描述

6.3小結

ModularRealmAuthenticator作用進行認證,需要呼叫realm查詢使用者資訊(在資料庫中存在使用者資訊) ModularRealmAuthenticator進行密碼對比(認證過程)。 realm:需要根據token中的身份資訊去查詢資料庫(入門程式使用ini配置檔案),如果查到使用者返回認證資訊,如果查詢不到返回null

6.4自定義realm

從第一個認證程式我們可以看見,我們所說的流程,是認證器去找realm去查詢我們相對應的資料。而預設的realm是直接去與配置檔案來比對的,一般地,我們在開發中都是讓realm去資料庫中比對。 因此,我們需要自定義realm

這裡寫圖片描述


public class CustomRealm extends AuthorizingRealm {

	// 設定realm的名稱
	@Override
	public void setName(String name) {
		super.setName("customRealm");
	}

	// 用於認證
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(
			AuthenticationToken token) throws AuthenticationException {

		// token是使用者輸入的
		// 第一步從token中取出身份資訊
		String userCode = (String) token.getPrincipal();

		// 第二步:根據使用者輸入的userCode從資料庫查詢
		// ....
	

		// 如果查詢不到返回null
		//資料庫中使用者賬號是zhangsansan
		/*if(!userCode.equals("zhangsansan")){//
			return null;
		}*/
		
		
		// 模擬從資料庫查詢到密碼
		String password = "111112";

		// 如果查詢到返回認證資訊AuthenticationInfo

		SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(
				userCode, password, this.getName());

		return simpleAuthenticationInfo;
	}

	// 用於授權
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(
			PrincipalCollection principals) {
		// TODO Auto-generated method stub
		return null;
	}

}
複製程式碼

6.5配置realm

需要在shiro-realm.ini配置realm注入到securityManager中。

這裡寫圖片描述

6.6測試自定義realm

同上邊的入門程式,需要更改ini配置檔案路徑:


同上邊的入門程式,需要更改ini配置檔案路徑:
Factory<SecurityManager> factory = new IniSecurityManagerFactory(
				"classpath:shiro-realm.ini");
複製程式碼

6.7雜湊演算法

我們如果知道md5,我們就會知道md5是不可逆的,但是如果設定了一些安全性比較低的密碼:111111...即時是不可逆的,但還是可以通過暴力演算法來得到md5對應的明文...

建議對md5進行雜湊時加salt(鹽),進行加密相當 於對原始密碼+鹽進行雜湊。\

正常使用時雜湊方法:

  • 在程式中對原始密碼+鹽進行雜湊,將雜湊值儲存到資料庫中,並且還要將鹽也要儲存在資料庫中。

測試:


public class MD5Test {
	
	public static void main(String[] args) {
		
		//原始 密碼 
		String source = "111111";
		//鹽
		String salt = "qwerty";
		//雜湊次數
		int hashIterations = 2;
		//上邊雜湊1次:f3694f162729b7d0254c6e40260bf15c
		//上邊雜湊2次:36f2dfa24d0a9fa97276abbe13e596fc
		
		
		//構造方法中:
		//第一個引數:明文,原始密碼 
		//第二個引數:鹽,通過使用隨機數
		//第三個引數:雜湊的次數,比如雜湊兩次,相當 於md5(md5(''))
		Md5Hash md5Hash = new Md5Hash(source, salt, hashIterations);
		
		String password_md5 =  md5Hash.toString();
		System.out.println(password_md5);
		//第一個引數:雜湊演算法 
		SimpleHash simpleHash = new SimpleHash("md5", source, salt, hashIterations);
		System.out.println(simpleHash.toString());
	}

}
複製程式碼

6.8自定義realm支援md5

自定義realm


public class CustomRealmMd5 extends AuthorizingRealm {

	// 設定realm的名稱
	@Override
	public void setName(String name) {
		super.setName("customRealmMd5");
	}

	// 用於認證
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(
			AuthenticationToken token) throws AuthenticationException {

		// token是使用者輸入的
		// 第一步從token中取出身份資訊
		String userCode = (String) token.getPrincipal();

		// 第二步:根據使用者輸入的userCode從資料庫查詢
		// ....

		// 如果查詢不到返回null
		// 資料庫中使用者賬號是zhangsansan
		/*
		 * if(!userCode.equals("zhangsansan")){// return null; }
		 */

		// 模擬從資料庫查詢到密碼,雜湊值
		String password = "f3694f162729b7d0254c6e40260bf15c";
		// 從資料庫獲取salt
		String salt = "qwerty";
		//上邊雜湊值和鹽對應的明文:111111

		// 如果查詢到返回認證資訊AuthenticationInfo
		SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(
				userCode, password, ByteSource.Util.bytes(salt), this.getName());

		return simpleAuthenticationInfo;
	}

	// 用於授權
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(
			PrincipalCollection principals) {
		// TODO Auto-generated method stub
		return null;
	}

}
複製程式碼

配置檔案:

這裡寫圖片描述

測試:


// 自定義realm實現雜湊值匹配
	@Test
	public void testCustomRealmMd5() {

		// 建立securityManager工廠,通過ini配置檔案建立securityManager工廠
		Factory<SecurityManager> factory = new IniSecurityManagerFactory(
				"classpath:shiro-realm-md5.ini");

		// 建立SecurityManager
		SecurityManager securityManager = factory.getInstance();

		// 將securityManager設定當前的執行環境中
		SecurityUtils.setSecurityManager(securityManager);

		// 從SecurityUtils裡邊建立一個subject
		Subject subject = SecurityUtils.getSubject();

		// 在認證提交前準備token(令牌)
		// 這裡的賬號和密碼 將來是由使用者輸入進去
		UsernamePasswordToken token = new UsernamePasswordToken("zhangsan",
				"222222");

		try {
			// 執行認證提交
			subject.login(token);
		} catch (AuthenticationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		// 是否認證通過
		boolean isAuthenticated = subject.isAuthenticated();

		System.out.println("是否認證通過:" + isAuthenticated);

	}
複製程式碼

七、總結

  • 使用者認證和使用者授權是Shiro的基礎,使用者認證其實上就是登陸操作、使用者授權實際上就是對資源攔截的操作。
  • 許可權管理的模型一般我們都將資源放在許可權表中進行管理起來。
  • 我們可以基於角色攔截,也可以基於資源攔截。要是基於角色攔截的話,那麼如果角色的許可權發生變化了,那就需要修改程式碼了**。推薦使用基於資源進行攔截**
  • 這次URL攔截,我們使用一個JavaBean來封裝所有的認證資訊。當使用者登陸了之後,我們就把使用者對選單欄的訪問、對資源的訪問許可權都封裝到該JavaBean中
  • 當使用攔截器進行使用者認證的時候,我們只要判斷Session域有沒有JavaBen物件即可了。
  • 當時候攔截器進行使用者授權的時候,我們要判斷JavaBean中的許可權是否能夠訪問該資源。
  • 以前URL攔截的方式需要把所有的URL都在資料庫進行管理。非常麻煩,不易維護。
  • 我們希望Shiro去認證的時候是通過realm去資料庫查詢資料的。而我們reaml預設是查詢配置檔案的資料的。
  • 因此,我們需要自定義reaml,使得它是去資料庫查詢資料。只要繼承AuthorizingRealm類就行了。
  • 當然了,自定義後的reaml也需要在配置檔案中寫上我們的自定義reaml的位置的。
  • 雜湊演算法就是為了讓密碼不被別人給破解。我們可對原始的密碼加鹽再進行雜湊,這就加大了破解的難度了。
  • 自定義的reaml也是支援雜湊演算法的,相同的,還是需要我們在配置檔案中配置一下就好了。

如果文章有錯的地方歡迎指正,大家互相交流。習慣在微信看技術文章,想要獲取更多的Java資源的同學,可以關注微信公眾號:Java3y

相關文章