淺析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的過濾器****到達前端控制器
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>