1. 程式人生 > >淺析shiro框架的認證和授權

淺析shiro框架的認證和授權

shiro安全框架

1.1.Shiro 概述

Shiro是apache旗下一個開源安全框架,它將軟體系統的安全認證相關的功能抽取出來,實現使用者身份認證,許可權授權、加密、會話管理等功能,組成了一個通用的安全認證框架,使用shiro就可以非常快速的完成認證、授權等功能的開發,降低系統成本。
課後瞭解:Spring security 安全框架

1.2.Shiro 概要架構

在概念層,Shiro 架構包含三個主要的理念:Subject,SecurityManager和 Realm。

1.3shiro的構件

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和你的應用程式安全資料之間的橋樑。

2.1Shiro 認證

簡述:
1)系統呼叫subject的login方法將使用者資訊提交給SecurityManager
2)SecurityManager將認證操作委託給認證器物件Authenticator
3)Authenticator將身份資訊傳遞給Realm。
4)Realm訪問資料庫獲取使用者資訊然後對資訊進行封裝並返回。
5)Authenticator 對realm返回的資訊進行身份認證。

詳述:
使用者名稱密碼驗證為例,當瀏覽器客戶端向伺服器傳送AJAX非同步請求,請求伺服器處理驗證使用者輸入的使用者名稱和密碼,
當請求傳入Tomcat,通過Tomcat的過濾器****到達前端控制器

的時候,前端控制器擷取請求的url,在HandlerMapping裡找對應的map集合中的entry元素,找到後穿過springMVC的攔截器到達後端控制器Controller,在Controller裡使用SecorityUtils獲取一個Subject,subject裡面有一個login方法,但是呼叫login方法必須傳入一個token型別的引數,這時候我們想到AJAX裡面傳過來的username和password正好可以封裝在一個UsernamePasswordtoken裡面,所以我們這樣寫

UsernamePasswordToken tokensubjec = new UsernamePasswordToken(username,password);
Subject subject = SecurityUtils.Subject();
subject.login(token);

當我們寫完這句subject.login(token);
系統底層會幫我們從配置檔案讀取我們配置的SecurityManager以及相關配置,並將token傳過去,會找到anthenticator(認證管理器),anthenticator根據SecurityManager裡面配置的realm,找到realm。我們要做的就是在這之前一定要確保該realm已經存在,否則會找不到,在realm中我們將傳過來token進行向下造型回usernamepasswordtoken,
然後將裡面的username取出來傳遞到資料層,讓資料層根據username從使用者名稱中查詢具體使用者所有資訊,查詢完畢我們使用一個SimpleAuthenticationInfo進行資訊封裝,new一個該物件的時候我們要傳入四個引數第一個是身份資訊,我們一般傳入從dao資料層查到的使用者資訊,但是要注意這裡寫入的型別和授權的時候取資料的型別要對應,如果只寫使用者名稱則取資料的時候用字串接收,第二個引數是加密後的密碼,我們已經查到,第三個引數是指定型別的salt,其實和我們通過資料層查到的salt是一樣的只不過需要的型別不一致,需要做一步轉型即可,第四個引數是realm的名字,表示本類直接用this.getName();然後將我們new出來的SimpleAuthenticationInfo物件的例項返回給認證管理器,認證管理器會自動幫我們認證。
如下圖所示在這裡插入圖片描述

2.2Shiro的授權

概述:
1)系統呼叫subject相關方法將使用者資訊(例如isPermitted)遞交給SecurityManager
2)SecurityManager將許可權檢測操作委託給Authorizer物件
3)Authorizer將使用者資訊委託給realm.
4)Realm訪問資料庫獲取使用者許可權資訊並封裝。
5) Authorizer對使用者授權資訊進行判定。

3.基於註解的方式配置Shiro元件

@Bean("securityManager")
public DefaultWebSecurityManager  newDefaultWebSecurityManager(
			AuthorizingRealm userRealm){
		DefaultWebSecurityManager sManager=
		new DefaultWebSecurityManager();
		//此時必須保證realm物件已經存在了
		sManager.setRealm(userRealm);
		return sManager;
}


@Bean("shiroFilterFactoryBean")
public ShiroFilterFactoryBean newShiroFilterFactoryBean(
			SecurityManager securityManager){//shiro 包
		ShiroFilterFactoryBean bean=new ShiroFilterFactoryBean();
		bean.setSecurityManager(securityManager);
	    //當此使用者是一個非認證使用者,需要先登陸進行認證
		bean.setLoginUrl("/doLoginUI.do");
		LinkedHashMap<String,String> fcMap=
		new LinkedHashMap<>();
		fcMap.put("/bower_components/**","anon");//anon表示允許匿名訪問
		fcMap.put("/build/**", "anon");
		fcMap.put("/dist/**","anon");
		fcMap.put("/plugins/**","anon");
		fcMap.put("/doLogin.do","anon");
		fcMap.put("/doLogout.do","logout");
		fcMap.put("/**", "authc");//必須授權才能訪問
		bean.setFilterChainDefinitionMap(fcMap);
		return bean;
}


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


@DependsOn(value="lifecycleBeanPostProcessor")
@Bean
public DefaultAdvisorAutoProxyCreator newDefaultAdvisorAutoProxyCreator(){
		return new DefaultAdvisorAutoProxyCreator();
}


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


3.3.Shiro 核心過濾器配置
在註解啟動類中,重寫onStartup方法,完成過濾器的註冊
	@Override
	public void onStartup(ServletContext servletContext) 
throws ServletException {
		System.out.println("onStartup");
		//super.onStartup(servletContext);
		registerContextLoaderListener(servletContext);
		registerFilter(servletContext);
		registerDispatcherServlet(servletContext);
	}

	private void registerFilter(ServletContext servletContext) {
		//註冊Filter物件
		//專案沒有web.xml並且此filter不是自己寫的,所以需要用這種配置方式
		FilterRegistration.Dynamic dy=
		servletContext.addFilter("filterProxy",
				DelegatingFilterProxy.class);
		dy.setInitParameter("targetBeanName","shiroFilterFactoryBean");
		dy.addMappingForUrlPatterns(
				null,//EnumSet<DispatcherType>
				false,"/*");//url-pattern
	}

4.基於xml方式配置Shiro元件

<?xml version="1.0" encoding="UTF-8"?>
<beans default-lazy-init="true"
	xmlns="http://www.springframework.org/schema/beans" xmlns:p="http://www.springframework.org/schema/p"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
	xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:util="http://www.springframework.org/schema/util"
	xmlns:jpa="http://www.springframework.org/schema/data/jpa"
	xsi:schemaLocation="  
       http://www.springframework.org/schema/beans   
       http://www.springframework.org/schema/beans/spring-beans-4.3.xsd  
       http://www.springframework.org/schema/mvc   
       http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd   
       http://www.springframework.org/schema/tx   
       http://www.springframework.org/schema/tx/spring-tx-4.3.xsd   
       http://www.springframework.org/schema/aop 
       http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
       http://www.springframework.org/schema/util 
       http://www.springframework.org/schema/util/spring-util-4.3.xsd
       http://www.springframework.org/schema/data/jpa 
       http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-4.3.xsd">

	<!-- 配置securityManager -->
	<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
		<!-- 配置Realm -->
		<property name="Realms" ref="shiroUserRealm"></property>
		
	</bean>
	<!-- 生命週期管理器 -->
	<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor">
	</bean>
	<!--設定自動代理Service實現類-->
	<bean id="defaultAdvisorAutoProxyCreator"
		class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
		depends-on="lifecycleBeanPostProcessor">
	</bean>
	<!-- 配置授權屬性 -->
	<bean  class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor" >
		<property name="SecurityManager" ref="securityManager"></property>
	</bean>

	<!-- 配置shiro的shiroFilterFactoryBean -->
	<!-- 目的:讓使用者先進行登入認證 -->
	<bean id="shiroFilterFactory" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
		<property name="SecurityManager" ref="securityManager"></property>
		<property name="LoginUrl" value="/doLoginUI.do"></property>
		<!-- 設定請求過濾規則 -->
		<property name="FilterChainDefinitionMap">
			<map>
				<entry key="/bower_components/**" value="anon"></entry>
				<entry key="/build/**" value="anon"></entry>
				<entry key="/dist/**" value="anon"></entry>
				<entry key="/plugins/**" value="anon"></entry>
				<entry key="/user/doLogin.do" value="anon"></entry>
				<entry key="/doLogout.do" value="logout"></entry>
				<!-- 除了以上的資源,其他資源都需要登入認證 -->
				<entry key="/**" value="authc"></entry>
			</map>
		</property>
	</bean>
</beans>

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
  <display-name>CGB-JT-SYS-V1.01</display-name>
  <!-- Shiro中的核心過濾器(負責攔截請求) -->
  <filter>
     <filter-name>shiroFilter</filter-name>
     <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
     <init-param>
        <param-name>targetBeanName</param-name>
        <param-value>shiroFilterFactory</param-value>
     </init-param>
  </filter>
  <filter-mapping>
     <filter-name>shiroFilter</filter-name>
     <url-pattern>/*</url-pattern>
  </filter-mapping>
  
  <!-- 配置Spring MVC 前端控制器 -->
  <!-- 註冊前端控制器 -->
  <servlet>
       <servlet-name>frontController</servlet-name>
       <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
       <init-param>
          <param-name>contextConfigLocation</param-name><!-- 這個名字不能隨意寫 -->
          <param-value>classpath:spring-configs.xml</param-value>
       </init-param> 
       <!--配置servlet在伺服器啟動時載入(數字越小優先順序越高) 
                       配置如下選項以後,tomcat啟動時就會初始化這個servlet,
                       這個servlet在初始化時會讀取contextConfigLocation
                       引數對應的配置檔案.
        -->
       <load-on-startup>1</load-on-startup>
 </servlet>
 <!-- 配置前端控制器對映 -->
 <servlet-mapping>
      <servlet-name>frontController</servlet-name>
      <url-pattern>*.do</url-pattern>
 </servlet-mapping>
 
</web-app>