shiro 瞅完就會用(ssm+shiro)

一 shiro 是什么

shiro 是一個功能強大和易于使用的Java安全框架,為開發人員提供一個直觀而全面的解決方案的認證,授權,加密,會話管理。

二 shiro 能干什么


先上圖:


所有功能

shiro 四個主要的功能

  • Authentication:身份認證/登錄,驗證用戶是不是擁有相應的身份;
  • Authorization:授權,即權限驗證,判斷某個已經認證過的用戶是否擁有某些權限訪問某些資源,一般授權會有角色授權和權限授權;
  • SessionManager:會話管理,即用戶登錄后就是一次會話,在沒有退出之前,它的所有信息都在會話中;會話可以是普通JavaSE環境的,也可以是如Web環境的,web 環境中作用是和 HttpSession 是一樣的;
  • Cryptography:加密,保護數據的安全性,如密碼加密存儲到數據庫,而不是明文存儲;

shiro 的其它幾個特點

  • Web Support:Web支持,可以非常容易的集成到Web環境;
  • Caching:緩存,比如用戶登錄后,其用戶信息、擁有的角色/權限不必每次去查,這樣可以提高效率;
  • Concurrency:shiro支持多線程應用的并發驗證,即如在一個線程中開啟另一個線程,能把權限自動傳播過去;
  • Testing:提供測試支持;
  • Run As:允許一個用戶假裝為另一個用戶(如果他們允許)的身份進行訪問;
  • Remember Me:記住我,這個是非常常見的功能,即一次登錄后,下次再來的話不用登錄了。
三 shiro 架構

先上圖


架構

從圖中我們可以看到不管是任何請求都會經過 SecurityManager 攔截并進行相應的處理,shiro 幾乎所有的功能都是由 SecurityManager 來管理。
其中:

  • Subject:主體,相當與是請求過來的"用戶"
  • SecurityManager: 是 Shiro 的心臟;所有具體的交互都通過 SecurityManager 進行攔截并控制;它管理著所有 Subject、且負責進行認證和授權、及會話、緩存的管理
  • Authenticator:認證器,負責主體認證的,即確定用戶是否登錄成功,我們可以使用  Shiro 提供的方法來認證,有可以自定義去實現,自己判斷什么時候算是用戶登錄成功
  • Authrizer:授權器,即權限授權,給 Subject 分配權限,以此很好的控制用戶可訪問的資源
  • Realm:一般我們都需要去實現自己的 Realm ,可以有1個或多個 Realm,即當我們進行登錄認證時所獲取的安全數據來源(帳號/密碼)
  • SessionManager:為了可以在不同的環境下使用 session 功能,shiro 實現了自己的 sessionManager ,可以用在非 web 環境下和分布式環境下使用
  • SessionDAO:對 session 的 CURD 操作
  • CacheManager:緩存控制器,來管理如用戶、角色、權限等的緩存的;
  • Cryptography:密碼模塊,Shiro提高了一些常見的加密組件用于如密碼加密/解密的。
四 shiro 的主要功能 - 身份認證

1 Subject 認證

身份認證就是在應用中誰能證明他就是他本人,一般會使用用戶名和密碼作為認證信息。

2 Subject 認證主體

Subject 認證主體包含兩個信息:

  • Principals:身份,即用戶名
  • Credentials:憑證,即密碼

** 3 認證流程**

認證流程
  1. 用戶發送請求進行 Subject 認證(調用 subject.login(token))
  2. SecurityManager 會去 Authenticator(認證器)中查找相應的 Realms(可能不止一個)源
  3. Realms 可以根據不同類型的 Realm 中去查找用戶信息,并進行判斷是否認證成功

4 快速搭建 helloWorld

  1. 導包
<dependencies>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-core</artifactId>
            <version>1.2.4</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.12</version>
        </dependency>
    </dependencies>
  1. 創建 Realm /resources/shiro.ini
[users]
acey=123456
jack=111
  1. 進行身份驗證
public class HelloWorld {

    public static void main(String[] args) {
//        加載配置文件,初始化 SecurityManager 工廠
        Factory<SecurityManager> factory = new IniSecurityManagerFactory
          ("classpath:shiro.shiro.ini");
//        獲取 SecurityManager 實例
        SecurityManager securityManager = factory.getInstance();
//        把 SecurityManager 綁定到 SecurityUtils 中
        SecurityUtils.setSecurityManager(securityManager);
//        得到當前執行的用戶
        Subject currentUser = SecurityUtils.getSubject();
//        創建 token 令牌,用戶名/密碼
        UsernamePasswordToken token = new UsernamePasswordToken("acey", "123456");
        try {
//            身份驗證
            currentUser.login(token);
            System.out.println("登錄成功");
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("登錄失敗");
        }
    }
}

四 shiro 的主要功能 - 授權

權限授權就是訪問控制,在應用中控制誰能訪問哪些資源

1 權限認證中的幾個元素

  • 權限:即操作某個資源的權限,這些資源可以是某個鏈接,也可以是某個圖片,也可以是對某個模塊的數據的 CURL
  • 角色:即權限的集合,一個角色可以有多個權限
  • 用戶:代表訪問的用戶,即  Subject

2 授權的流程

授權流程
  1. 當用戶訪問應用中的某個資源時,會被 SecurityManager 攔截.
  2. SecurityManager 會去調用 Authorizer(授權器)
  3. Authorizer 會根據 Subject 的身份去相應的 Realm 中去查找該 Subject 是否有權限去訪問該資源

3 授權實現

  1. 導包
  2. 配置 permission(權限) resources/shiro_permission.ini
[main] 
authc.loginUrl=/login  //表示用戶登錄失敗跳轉到 /login
roles.unauthorrizedUrl=/unauthorrized.jsp //表示用戶沒有對應的訪問角色跳轉到/unauthorrized.jsp
perms.unauthorrizedUrl=/unauthorrized.jsp  //表示用戶沒有對應的訪問權限跳轉到/unauthorrized.jsp

[users]
acey=123456,role1,role2
jack=123,role1
[roles]
role1=user:select // role1 角色有訪問 user:select 的權限
role2=user:add,/delete //role2 角色有訪問 user:add 和 /delete 的權限

[urls]
/login=anon  //表示任何用戶都可以訪問 /login
/index=authc //表示只有身份認證通過的用戶才可以訪問 /index
/index=roles[role1,role2...] //表示只有用戶含有 role1 role2 ... 角色才可以訪問 /index
/index=perms["user:create","/update"]  //表示只有用戶含有 "user:create" 
                      和"/update"權限才可以訪問 /index 
/index?=authc //`?`通配符,表示一個字符,如/index1 /indexa /index- (不能匹配/index) ,
                      將符合這種規則的請求進行`authc`攔截
/index*=authc  `*`通配符,表示零個或一個或多個字符,如/index1213asd /index /index2 ,
                      將符合這種規則的請求進行`authc`攔截
/index/**=authc  `**`表示匹配零個或一個或多個路徑,如/index/create /index/create/update/...  ,
                      將符合這種規則的請求進行`authc`攔截
/index*/**authc  可以匹配 /index12/create/update/...

3)配置 roles (角色) resources/shiro_role.ini

[users]
acey=123456,role1,role2 //表示有一個用戶,用戶名是acey,密碼為123456,有role1和role2角色
jack=123,role1
  1. 驗證用戶角色是否足夠
public class RoleTest {

//  使用 checkRole 來檢驗角色時,若權限不足會返回 false
    @Test
    public void testHasRole() {
        Subject currentUser= ShiroUtil.login("classpath:shiro_role.ini", "acey", "123456");
        // Subject currentUser=ShiroUtil.login("classpath:shiro_role.ini", "jack", "123");
        System.out.println(currentUser.hasRole("role1")?"has role1":"has not role1");
        currentUser.logout();
    }

    //  使用 checkRole 來檢驗角色時,若權限不足會拋出異常
    @Test
    public void testCheckRole() {
        Subject currentUser=ShiroUtil.login("classpath:shiro_role.ini", "acey", "123456");
        // Subject currentUser=ShiroUtil.login("classpath:shiro_role.ini", "jack", "123");
        currentUser.checkRole("role1");

        currentUser.logout();
    }
}
  1. 驗證用戶權限是否足夠
public class PermissionTest {

//  使用 checkPermission 來檢驗權限時,若權限不足會返回 false
    @Test
    public void testIsPermitted() {
        Subject currentUser= ShiroUtil.login("classpath:shiro_permission.ini", "acey", "123456");
        System.out.println(currentUser.isPermitted("user:select")?"has user:select":"hsa not user:select");

        currentUser.logout();
    }

//  使用 checkPermission 來檢驗權限時,若權限不足會拋出異常
    @Test
    public void testCheckPermitted() {
        Subject currentUser=ShiroUtil.login("classpath:shiro_permission.ini", "acey", "123456");
        // Subject currentUser=ShiroUtil.login("classpath:shiro_permission.ini", "jack", "123");
        currentUser.checkPermission("user:select");
        currentUser.logout();
    }
}
五 ssm 和 shiro 整合

  1. 導入依賴
    2)配置 web.xml(shiro過濾器)
 <!-- shiro過濾器定義 -->
    <filter>  
        <filter-name>shiroFilter</filter-name>  
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>  
    <init-param>  
    <!-- 該值缺省為false,表示生命周期由SpringApplicationContext管理,設置為true則表示由ServletContainer管理 -->  
    <param-name>targetFilterLifecycle</param-name>  
    <param-value>true</param-value>  
    </init-param>  
    </filter>  
    <filter-mapping>  
            <filter-name>shiroFilter</filter-name>  
            <url-pattern>/*</url-pattern>  
    </filter-mapping>
    
    

3)編寫自己的 Realm(一般權限都是從數據庫中查找,所以需要自定義)

public class MyRealm extends AuthorizingRealm{

    @Resource
    private UserService userService;
    
    /**
     * 為當前登錄的用戶授予角色和權限
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        //獲取用戶名
        String userName=(String)principals.getPrimaryPrincipal();
        SimpleAuthorizationInfo authorizationInfo=new SimpleAuthorizationInfo();
        //進行授權角色
        authorizationInfo.setRoles(userService.getRoles(userName));
        //進行授權權限
        authorizationInfo.setStringPermissions(userService.getPermissions(userName));
        return authorizationInfo;
    }

    /**
     *驗證當前登錄的用戶
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        String userName=(String)token.getPrincipal();
        //根據用戶名查找用戶信息
            User user=userService.getByUserName(userName);
            if(user!=null){
                AuthenticationInfo authcInfo=new SimpleAuthenticationInfo(user.getUserName(),user.getPassword(),getName());
                return authcInfo;
            }else{
                return null;                
            }
    }
}
  1. spring 和 shiro 配置整合
...
<!-- 自定義Realm -->
    <bean id="myRealm" class="com.acey.realm.MyRealm"/>
    
    <!-- 安全管理器 -->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">  
      <property name="realm" ref="myRealm"/>  
    </bean>  
    
    <!-- Shiro過濾器 -->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">  
        <!-- Shiro的核心安全接口,這個屬性是必須的 -->  
        <property name="securityManager" ref="securityManager"/>
        <!-- 身份認證失敗,則跳轉到登錄頁面的配置 -->  
        <property name="loginUrl" value="/index.jsp"/>
        <!-- 權限認證失敗,則跳轉到指定頁面 -->  
        <property name="unauthorizedUrl" value="/unauthor.jsp"/>  
        <!-- Shiro連接約束配置,即過濾鏈的定義 -->  
        <property name="filterChainDefinitions">  
            <value>  
                 /login=anon
                /admin*=authc
                /student=roles[teacher]
                /teacher=perms["user:create"]
            </value>  
        </property>
    </bean>  
    
    <!-- 保證實現了Shiro內部lifecycle函數的bean執行 -->  
    <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>  
    
    <!-- 開啟Shiro注解 -->
    <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor"/>  
        <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">  
      <property name="securityManager" ref="securityManager"/>  
    </bean>  
...

一般角色和權限都存在數據庫中,所以我們還可以自定義一個 filter 去自己驗證每一個請求的 Subject 是否有權限去訪問,這樣我們就可以減少對過濾鏈的維護.比如

<!-- Shiro過濾器 -->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">  
        <!-- Shiro的核心安全接口,這個屬性是必須的 -->  
        <property name="securityManager" ref="securityManager"/>
        <!-- 身份認證失敗,則跳轉到登錄頁面的配置 -->  
        <property name="loginUrl" value="/index.jsp"/>
        <!-- 權限認證失敗,則跳轉到指定頁面 -->  
        <property name="unauthorizedUrl" value="/unauthor.jsp"/>  
        <!-- Shiro連接約束配置,即過濾鏈的定義 -->  
        <property name="filterChainDefinitions">  
            <value>  
                 /login=anon
                /admin*=authc
                /student=roles[teacher]
                /teacher=perms["user:create"]
            </value>  
        </property>
    </bean>  

可以改成

<!-- Shiro過濾器 -->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">  
        <!-- Shiro的核心安全接口,這個屬性是必須的 -->  
        <property name="securityManager" ref="securityManager"/>
        <!-- 身份認證失敗,則跳轉到登錄頁面的配置 -->  
        <property name="loginUrl" value="/index.jsp"/>
        <!-- 權限認證失敗,則跳轉到指定頁面 -->  
        <property name="unauthorizedUrl" value="/unauthor.jsp"/>  
        <property name="ownFilter" class="ownFilter.class">
        <!-- Shiro連接約束配置,即過濾鏈的定義 -->  
        <property name="filterChainDefinitions">  
            <value>  
                 /login=anon
               /**=ownFilter
            </value>  
        </property>
    </bean>  

待續! 歡迎大家拍磚

源碼地址:ShirDemos

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 前言 Spring boot 是什么,網上的很多介紹,這里博客就不多介紹了。如果不明白Spring boot是什么...
    xuezhijian閱讀 17,942評論 13 39
  • 構建一個互聯網應用,權限校驗管理是很重要的安全措施,這其中主要包含: 認證 - 用戶身份識別,即登錄 授權 - 訪...
    zhuke閱讀 3,559評論 0 30
  • 一:基礎概念 什么是權限管理 權限管理包括用戶身份認證和授權兩部分,簡稱認證授權。對于需要訪問控制的資源用戶首先經...
    QGUOFENG閱讀 571評論 0 0
  • Shiro(代碼) 1.1 簡介 Apache Shiro是Java的一個安全框架。目前,使用Apache Shi...
    ZZS_簡閱讀 493評論 0 0
  • 1.簡介 Apache Shiro是Java的一個安全框架。功能強大,使用簡單的Java安全框架,它為開發人員提供...
    H_Man閱讀 3,181評論 4 47