關於shiro安全框架和shiro的認證流程

莎士比亞鼠發表於2020-11-17

Shiro

談談關於shiro的認識和理解

shiro是什麼

Apache Shiro 是Java 的一個安全框架。Shiro 可以非常容易的開發出足夠好的應用,其不僅可以用在JavaSE 環境,也可以用在JavaEE 環境。Shiro 可以幫助我們完成:認證、授權、加密、會話管理、與Web 整合、快取等。
Shiro 官方網站 :http://shiro.apache.org/

為什麼要學習shiro

shiro將安全認證,許可權分配等功能抽離出一個框架,我們可以使用shiro快速的完成認證,授權等。可以應用在web應用和非web應用,叢集分散式應用。

shiro的基本功能

在這裡插入圖片描述
1 Authentication
認證功能,驗證使用者身份
2 Authorization
授權功能,授權給使用者哪些角色比如授權給A使用者管理員角色
3 SessionManagement
會話管理功能 即使用者的一次會話在退出之前,使用者的所有資訊都在session中,類似於sevlet中的session,但是區別在於shiro中的sessionManagement可以在普通的javaSE環境中也可以是Web環境中。
4 Cryptography
加密功能,如將密碼用md5加密之後儲存到資料庫中
5 Web Support
支援Web 容易整合到web環境
6 Caching
快取 使用者登陸後他的資訊以及被賦予的角色和許可權被查詢到之後不必再次或多次查詢。
7 Concurrency
shiro支援多執行緒應用的併發驗證。
8 Testing
支援測試
9 Remember Me
記住我功能,登陸後,下次不用登陸。底層實現是cookie技術。
shiro不會去維護使用者和許可權,如果想給使用者賦予角色和許可權需要我們呼叫相應的介面。

shiro架構

Altshiro的基本架構
上述我們瞭解到shiro能做到哪些事情,我們接下來看shiro怎麼做這些事情的。
1 subject
subject即主體,可以理解為當前的使用者。比如在瀏覽器中登陸的當前使用者,這個主體可以來自很多地方。sbuject在shiro中是一個介面,介面中定義了很多認證相關的方法,外部程式的使用者通過subject來跟shiro產生聯絡。即相當於你要找shiro做認證等相關事情,shiro給你開了一個視窗。
2 SecurityManager
安全管理器,shiro的核心負責對sbbject進行管理,在安全管理器中有認證器,授權器。也就是說它負責告訴subject應該幹嘛,你想認證就把當前的subejct給認證器。
3 Authentiactor
認證器
4 Authorizer
授權器
5 Realms
領域,在認證使用者(subject)時需要從相應的資料來源中讀取資料。因為有很多不同的資料來源比如datasource資料來源,ini檔案,資料庫等。所以realms有很對realms兒子 ,你需要從哪裡讀取資料來源就用那個兒子。
6 SessionManager 和Sessiondao
sessionManage負責儲存和管理我們的登陸後的使用者資訊。當我們需要將session 中的資料儲存到資料庫中,就使用到了Sessiondao,當然它不僅能儲存資料但資料庫,也能讀取資料。
7 CacheManager
CacheManager即快取管理,將使用者許可權資料儲存在快取。
以上7個都屬於SecurityManager安全管理器
8 Cryptography
密碼管理,可以將使用者的密碼進行加密和解密,如雜湊加密/解密。

shiro的認證

shiro的主要功能之一。
在認證前我們需要明白幾個概念
subject
|----------|
ID -----token
----------- |
-------- |-------|
-----name -----password
當一個subject進入安全管理器時 首先通過ID判斷是否登陸, 這個ID只是我理解的一個東西。 如果登陸的話進進行下一步操作,如果沒有登陸,就將使用者的使用者名稱和密碼封裝到token,使用login(token)方法進行登陸。

認證前準備

org.apache.shiro
shiro-all
1.3.2

使用maven引入jar包
認證流程的HelloWord
這個僅僅是我們認識認證,真正開發中不會這樣

我們使用shiro.ini檔案當作資料來源

#配置使用者
[users]
#賬號=密碼,角色1,角色2
lxh=123
ntysh=456,addUser,deletUser

#配置角色能幹的事
addUser = add:*

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.junit.Test;
//shiro認證流程
public class shiroTest {

    @Test
    public  void  shiroTe() {

        //通過讀取ini檔案  建立一個Securitymnager工廠  僅僅限於測試用 真實中我們不會把資訊放入ini檔案中
        IniSecurityManagerFactory factory = new IniSecurityManagerFactory("classpath:shiro.ini");
        //建立安全管理器物件
        SecurityManager securityManager = factory.getInstance();
        // 把securityManager設定到當前環境Securityutils.getSubject();
        SecurityUtils.setSecurityManager(securityManager);
        //獲取 當前的subject 主體物件  因為一次只能判斷一個使用者主體物件物件
        Subject currentsubject = SecurityUtils.getSubject();

        //現在我們有一個主體物件了   首先可以玩玩securityManage中的session
        //當前使用者獲取session 物件
//        Session session = currentsubject.getSession();
//          session.setAttribute("ysh","ysh");
//        Object value = (String) session.getAttribute("ysh");
//        if(value.equals("ysh")){
//            System.out.println("從session中獲取到了資料");
//        }
//    }
        // 判斷當前sbject主體物件是否登陸了 也就是是否認證了 目前這個肯定是沒有認證的
        if(currentsubject.isAuthenticated()){
             //如果true認證了
            System.out.println(currentsubject.isAuthenticated());
            System.out.println("當前使用者認證了");
        }else {
            System.out.println(currentsubject.isAuthenticated());
            System.out.println("沒有認證");
            //沒有認證就把當前使用者的資訊封裝到token中  正常使用者資訊來自前臺傳入的
            //假設當前的使用者資訊的賬號和密碼是ysh  123
            UsernamePasswordToken token =new UsernamePasswordToken("ysh","456");
            // 因為沒有認證  現在去認證  呼叫login方法
           try {
               currentsubject.login(token);
               //進入認證器
           }catch (UnknownAccountException  uae){
               System.out.println("沒有找到叫ysh的使用者");
               return;
           }catch (IncorrectCredentialsException ie){
               System.out.println("有叫ysh的但是密碼錯了");
               return;
           }catch (LockedAccountException le){
               System.out.println("使用者被鎖定");
               return;
           }catch (AuthenticationException ae){
               System.out.println("所有上面異常的父類認證失敗");
           }
        }
         //如果認證成功了 也就是資料來源中有這個人 並且密碼也正確 我們列印出來看看對不對
        System.out.println(currentsubject.getPrincipal());



        //認證成功了 那我們來看看這個使用者有沒有某個角色
        if(currentsubject.hasRole("addUser")){
          //有addUser 角色  在ini檔案中 的寫法是 使用者名稱:密3碼,角色1,角色2
            System.out.println("ysh有addUser這個角色");

        }else {
            System.out.println("ysh沒有addUser這個角色");
        }

        //下面判斷這個使用者有的角色 有沒有一些行為
        if(currentsubject.isPermitted("add:student")){
         //addUser 有addstudent的行為
            System.out.println("當前使用者ysh的addUser角色有addstudent的行為");

        }else {
            System.out.println("當前使用者ysh的addUser角色沒有addstudent的行為");
        }

        //登出
        currentsubject.logout();
        System.out.println("看看登出後使用者ysh還有沒有認證"+currentsubject.isAuthenticated());
        //退出
         System.exit(0);
    }
}

原始碼分析:

1  獲取當前的subject  SecurityUtils.getSubject();
2  判斷是否登陸 /認證
3  在沒有認證情況下 封裝token  進行登陸 subject.login(token);
4  ---> DelegatingSubject類的login(token) 
----> 進入安全管理器DefaultSecurityManager類的login(token)
---->  AuthenticationSecurityManager類中的AuthencationInfo構造方法中傳入token 構造認證資訊
-----> AbstractAuthentication抽象類中判斷token是否為空,不為空時呼叫doAuthentication(token)方法
----->*ModularRealmAuthenticator*類中的getAuthenticationInfo(token)
在這個類中同時獲取了相應的Realm,如果是自定義了Realm 那這各地方就是自定義realm
----->AuthenticatingRealm類中的doGetAuthenticationInfo(token)
5 所以最終呼叫的是doGetAuhenticationInfo() 

自定義shiro認證思路解析:

首先我們要將shiro應用到實際開發中,可以整合到maven+ssm框架和springboot框架

1 呼叫SecurityUtils.getSubject 獲取當前的使用者
2 呼叫Suject.isAuthenticated() 判斷這個使用者是否登陸/認證
3 若沒有被認證/登陸 ,就把使用者名稱name和密碼password封裝到UsernamePasswordToken物件中
1)正常情況下name和passowrd是通過表單傳遞到springmvc我們在handle裡來獲取的
4 封裝後,執行Subject(當前使用者)的login(AuthenticationToken)方法進行認證(AuthenticationToken是UsernamPasswordToken的父類)
5 如果是自定以Reml的方法,比如我們自定義從資料庫中查詢資料我們需要定一個MyShiroRealm
1)首先繼承org.apache.shiro.reaml.AuthenticatingRealm類
2)實現doGetAuthentiactionInfo(AuthenticationToken)方法傳入一個token
6 在認證的過程中 : 首先根據token中的name去資料來源中查詢這個使用者的資訊,*比如使用者的許可權,使用者的是否存在,使用者的行為,使用者的密碼password,使用者是否被鎖定。然後根據使用者的各個狀態丟擲相應的異常。*這個過程跟我們在HelloWord的認證丟擲各種異常一樣。 然後定義
AuthenticationInfo的實現類SimpleAuthenticationInfo實現類
SimpleAuthenticationInfo info =new SimleAuthentication(pricipal,credentials,realmName);

principal: token中封裝的name 也就是前臺傳過來的name
credentials : 根據name從資料庫中查詢到的密碼
realmName:當前realm物件的名稱,呼叫getName()方法獲取,不同的資料來源對應的realm名稱不同。
SimpleAuthentication的物件會去對比token中的密碼和從資料庫中查詢的密碼。

關於密碼的對比AuthenticatingRealm的credentialsMatcher屬性來進行的密碼的對比

springboot整合shiro
加入依賴
        <!--Shiro和Spring整合的橋樑包-->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>${shiro.version}</version>
        </dependency>



相關文章