Shiro 實戰教程(上)

1

注:該shiro教程來源於B站上的一個教程,因爲源碼是付費的,我就不分享了,下篇講解springboot搭配shiro進行使用。

個人我的博客:java

天涯志

個人公衆號:菜鳥小謝web

8

1.權限的管理

1.1 什麼是權限管理

基本上涉及到用戶參與的系統都要進行權限管理,權限管理屬於系統安全的範疇,權限管理實現對用戶訪問系統的控制,按照安全規則或者安全策略控制用戶能夠訪問並且只能訪問本身被受權的資源。spring

權限管理包括用戶身份認證受權兩部分,簡稱認證受權。對於須要訪問控制的資源用戶首先通過身份認證,認證經過後用戶具備該資源的訪問權限方可訪問。數據庫

1.2 什麼是身份認證

身份認證,就是判斷一個用戶是否爲合法用戶的處理過程。最經常使用的簡單身份認證方式是系統經過覈對用戶輸入的用戶名和口令,看其是否與系統中存儲的該用戶的用戶名和口令一致,來判斷用戶身份是否正確。對於採用指紋等系統,則出示指紋;對於硬件Key等刷卡系統,則須要刷卡。apache

1.3 什麼是受權

受權,即訪問控制,控制誰能訪問哪些資源。主體進行身份認證後須要分配權限方可訪問系統的資源,對於某些資源沒有權限是沒法訪問的編程


2.什麼是shiro

Apache Shiro™ is a powerful and easy-to-use Java security framework that performs authentication, authorization, cryptography, and session management. With Shiro’s easy-to-understand API, you can quickly and easily secure any application – from the smallest mobile applications to the largest web and enterprise applications.

Shiro 是一個功能強大且易於使用的Java安全框架,它執行身份驗證、受權、加密和會話管理。使用Shiro易於理解的API,您能夠快速輕鬆地保護任何應用程序—從最小的移動應用程序到最大的web和企業應用程序。瀏覽器

Shiro是apache旗下一個開源框架,它將軟件系統的安全認證相關的功能抽取出來,實現用戶身份認證,權限受權、加密、會話管理等功能,組成了一個通用的安全認證框架。緩存


3.shiro的核心架構

2

3.1 Subject

Subject即主體,外部應用與subject進行交互,subject記錄了當前操做用戶,將用戶的概念理解爲當前操做的主體,多是一個經過瀏覽器請求的用戶,也多是一個運行的程序。 Subject在shiro中是一個接口,接口中定義了不少認證授相關的方法,外部程序經過subject進行認證授,而subject是經過SecurityManager安全管理器進行認證受權安全

3.2 SecurityManager

SecurityManager即安全管理器,對所有的subject進行安全管理,它是shiro的核心,負責對全部的subject進行安全管理。經過SecurityManager能夠完成subject的認證、受權等,實質上SecurityManager是經過Authenticator進行認證,經過Authorizer進行受權,經過SessionManager進行會話管理等。springboot

SecurityManager是一個接口,繼承了Authenticator, Authorizer, SessionManager這三個接口。

3.3 Authenticator

Authenticator即認證器,對用戶身份進行認證,Authenticator是一個接口,shiro提供ModularRealmAuthenticator實現類,經過ModularRealmAuthenticator基本上能夠知足大多數需求,也能夠自定義認證器。

3.4 Authorizer

Authorizer即受權器,用戶經過認證器認證經過,在訪問功能時須要經過受權器判斷用戶是否有此功能的操做權限。

3.5 Realm

Realm即領域,至關於datasource數據源,securityManager進行安全認證須要經過Realm獲取用戶權限數據,好比:若是用戶身份數據在數據庫那麼realm就須要從數據庫獲取用戶身份信息。

  • ​ 注意:不要把realm理解成只是從數據源取數據,在realm中還有認證受權校驗的相關的代碼。

3.6 SessionManager

sessionManager即會話管理,shiro框架定義了一套會話管理,它不依賴web容器的session,因此shiro可使用在非web應用上,也能夠將分佈式應用的會話集中在一點管理,此特性可以使它實現單點登陸。

3.7 SessionDAO

SessionDAO即會話dao,是對session會話操做的一套接口,好比要將session存儲到數據庫,能夠經過jdbc將會話存儲到數據庫。

3.8 CacheManager

CacheManager即緩存管理,將用戶權限數據存儲在緩存,這樣能夠提升性能。

3.9 Cryptography

Cryptography即密碼管理,shiro提供了一套加密/解密的組件,方便開發。好比提供經常使用的散列、加/解密等功能。


4. shiro中的認證

4.1 認證

身份認證,就是判斷一個用戶是否爲合法用戶的處理過程。最經常使用的簡單身份認證方式是系統經過覈對用戶輸入的用戶名和口令,看其是否與系統中存儲的該用戶的用戶名和口令一致,來判斷用戶身份是否正確。

4.2 shiro中認證的關鍵對象

  • Subject:主體

訪問系統的用戶,主體能夠是用戶、程序等,進行認證的都稱爲主體;

  • Principal:身份信息

是主體(subject)進行身份認證的標識,標識必須具備惟一性,如用戶名、手機號、郵箱地址等,一個主體能夠有多個身份,可是必須有一個主身份(Primary Principal)。

  • credential:憑證信息

是隻有主體本身知道的安全信息,如密碼、證書等。

4.3 認證流程

3

4.4 認證的開發

1. 建立項目並引入依賴
<dependency>
  <groupId>org.apache.shiro</groupId>
  <artifactId>shiro-core</artifactId>
  <version>1.5.3</version>
</dependency>
2. 引入shiro配置文件並加入以下配置
[users]
xiaoxie=123
zhangsan=456

4

3.開發認證代碼
public class TestAuthenticator {
    public static void main(String[] args) {
        //建立securityManager
        DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
        defaultSecurityManager.setRealm(new IniRealm("classpath:shiro.ini"));
        //將安裝工具類中設置默認安全管理器
        SecurityUtils.setSecurityManager(defaultSecurityManager);
        //獲取主體對象
        Subject subject = SecurityUtils.getSubject();
        //建立token令牌
        UsernamePasswordToken token = new UsernamePasswordToken("xiaochen1", "123");
        try {
            subject.login(token);//用戶登陸
            System.out.println("登陸成功~~");
        } catch (UnknownAccountException e) {
            e.printStackTrace();
            System.out.println("用戶名錯誤!!");
        }catch (IncorrectCredentialsException e){
            e.printStackTrace();
            System.out.println("密碼錯誤!!!");
        }

    }
}
  • DisabledAccountException(賬號被禁用)
  • LockedAccountException(賬號被鎖定)
  • ExcessiveAttemptsException(登陸失敗次數過多)
  • ExpiredCredentialsException(憑證過時)等

4.5 自定義Realm

上邊的程序使用的是Shiro自帶的IniRealm,IniRealm從ini配置文件中讀取用戶的信息,大部分狀況下須要從系統的數據庫中讀取用戶信息,因此須要自定義realm。

1.shiro提供的Realm

5

2.根據認證源碼認證使用的是SimpleAccountRealm

6
SimpleAccountRealm的部分源碼中有兩個方法一個是 認證 一個是 受權,

public class SimpleAccountRealm extends AuthorizingRealm {
        //.......省略
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        UsernamePasswordToken upToken = (UsernamePasswordToken) token;
        SimpleAccount account = getUser(upToken.getUsername());

        if (account != null) {

            if (account.isLocked()) {
                throw new LockedAccountException("Account [" + account + "] is locked.");
            }
            if (account.isCredentialsExpired()) {
                String msg = "The credentials for account [" + account + "] are expired";
                throw new ExpiredCredentialsException(msg);
            }

        }

        return account;
    }

    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        String username = getUsername(principals);
        USERS_LOCK.readLock().lock();
        try {
            return this.users.get(username);
        } finally {
            USERS_LOCK.readLock().unlock();
        }
    }
}
3.自定義realm
/**
 * 自定義realm
 */
public class CustomerRealm extends AuthorizingRealm {
    //認證方法
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        return null;
    }

    //受權方法
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        String principal = (String) token.getPrincipal();
        if("xiaochen".equals(principal)){
            return new SimpleAuthenticationInfo(principal,"123",this.getName());
        }
        return null;
    }
}
4.使用自定義Realm認證
public class TestAuthenticatorCusttomerRealm {
    public static void main(String[] args) {
        //建立securityManager
        DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
        //IniRealm realm = new IniRealm("classpath:shiro.ini");
        //設置爲自定義realm獲取認證數據
        defaultSecurityManager.setRealm(new CustomerRealm());
        //將安裝工具類中設置默認安全管理器
        SecurityUtils.setSecurityManager(defaultSecurityManager);
        //獲取主體對象
        Subject subject = SecurityUtils.getSubject();
        //建立token令牌
        UsernamePasswordToken token = new UsernamePasswordToken("xiaochen", "123");
        try {
            subject.login(token);//用戶登陸
            System.out.println("登陸成功~~");
        } catch (UnknownAccountException e) {
            e.printStackTrace();
            System.out.println("用戶名錯誤!!");
        }catch (IncorrectCredentialsException e){
            e.printStackTrace();
            System.out.println("密碼錯誤!!!");
        }

    }
}

4.6 使用MD5和Salt

實際應用是將鹽和散列後的值存在數據庫中,自動realm從數據庫取出鹽和加密後的值由shiro完成密碼校驗。
1.自定義md5+salt的realm
/**
 * 自定義md5+salt realm
 */
public class CustomerRealm extends AuthorizingRealm {
    //認證方法
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        return null;
    }

    //受權方法
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        String principal = (String) token.getPrincipal();
        if("xiaochen".equals(principal)){
            String password = "3c88b338102c1a343bcb88cd3878758e";
            String salt = "Q4F%";
            return new SimpleAuthenticationInfo(principal,password, 
                                                ByteSource.Util.bytes(salt),this.getName());
        }
        return null;
    }
2.使用md5 + salt 認證
public class TestAuthenticatorCusttomerRealm {
    public static void main(String[] args) {
        //建立securityManager
        DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
        //IniRealm realm = new IniRealm("classpath:shiro.ini");
        //設置爲自定義realm獲取認證數據
        CustomerRealm customerRealm = new CustomerRealm();
        //設置md5加密
        HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
        credentialsMatcher.setHashAlgorithmName("MD5");
        credentialsMatcher.setHashIterations(1024);//設置散列次數
        customerRealm.setCredentialsMatcher(credentialsMatcher);
        defaultSecurityManager.setRealm(customerRealm);
        //將安裝工具類中設置默認安全管理器
        SecurityUtils.setSecurityManager(defaultSecurityManager);
        //獲取主體對象
        Subject subject = SecurityUtils.getSubject();
        //建立token令牌
        UsernamePasswordToken token = new UsernamePasswordToken("xiaochen", "123");
        try {
            subject.login(token);//用戶登陸
            System.out.println("登陸成功~~");
        } catch (UnknownAccountException e) {
            e.printStackTrace();
            System.out.println("用戶名錯誤!!");
        }catch (IncorrectCredentialsException e){
            e.printStackTrace();
            System.out.println("密碼錯誤!!!");
        }

    }
}

5. shiro中的受權

5.1 受權

受權,即訪問控制,控制誰能訪問哪些資源。主體進行身份認證後須要分配權限方可訪問系統的資源,對於某些資源沒有權限是沒法訪問的。

5.2 關鍵對象

受權可簡單理解爲who對what(which)進行How操做:

Who,即主體(Subject),主體須要訪問系統中的資源。

What,即資源(Resource),如系統菜單、頁面、按鈕、類方法、系統商品信息等。資源包括資源類型資源實例,好比商品信息爲資源類型,類型爲t01的商品爲資源實例,編號爲001的商品信息也屬於資源實例。

How,權限/許可(Permission),規定了主體對資源的操做許可,權限離開資源沒有意義,如用戶查詢權限、用戶添加權限、某個類方法的調用權限、編號爲001用戶的修改權限等,經過權限可知主體對哪些資源都有哪些操做許可。

5.3 受權流程

7

5.4 受權方式

  • 基於角色的訪問控制

    • RBAC基於角色的訪問控制(Role-Based Access Control)是以角色爲中心進行訪問控制

      if(subject.hasRole("admin")){
         //操做什麼資源
      }
  • 基於資源的訪問控制

    • RBAC基於資源的訪問控制(Resource-Based Access Control)是以資源爲中心進行訪問控制

      if(subject.isPermission("user:update:01")){ //資源實例
        //對01用戶進行修改
      }
      if(subject.isPermission("user:update:*")){  //資源類型
        //對01用戶進行修改
      }

5.5 權限字符串

​ 權限字符串的規則是:資源標識符:操做:資源實例標識符,意思是對哪一個資源的哪一個實例具備什麼操做,「:」是資源/操做/實例的分割符,權限字符串也可使用*通配符。

例子:

  • 用戶建立權限:user:create,或user:create:*
  • 用戶修改實例001的權限:user:update:001
  • 用戶實例001的全部權限:user:*:001

5.6 shiro中受權編程實現方式

  • 編程式

    Subject subject = SecurityUtils.getSubject();
    if(subject.hasRole(「admin」)) {
        //有權限
    } else {
        //無權限
    }
  • 註解式

    @RequiresRoles("admin")
    public void hello() {
        //有權限
    }
  • 標籤式

    JSP/GSP 標籤:在JSP/GSP 頁面經過相應的標籤完成:
    <shiro:hasRole name="admin">
        <!— 有權限—>
    </shiro:hasRole>
    注意: Thymeleaf 中使用shiro須要額外集成!

5.7 開發受權

1.realm的實現
public class CustomerRealm extends AuthorizingRealm {
    //認證方法
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        String primaryPrincipal = (String) principals.getPrimaryPrincipal();
        System.out.println("primaryPrincipal = " + primaryPrincipal);

        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();

        simpleAuthorizationInfo.addRole("admin");

        simpleAuthorizationInfo.addStringPermission("user:update:*");
        simpleAuthorizationInfo.addStringPermission("product:*:*");


        return simpleAuthorizationInfo;
    }

    //受權方法
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        String principal = (String) token.getPrincipal();
        if("xiaochen".equals(principal)){
            String password = "3c88b338102c1a343bcb88cd3878758e";
            String salt = "Q4F%";
            return new SimpleAuthenticationInfo(principal,password, 
                                                ByteSource.Util.bytes(salt),this.getName());
        }
        return null;
    }

}
2.受權
public class TestAuthenticatorCusttomerRealm {
    public static void main(String[] args) {
        //建立securityManager
        DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
        //IniRealm realm = new IniRealm("classpath:shiro.ini");
        //設置爲自定義realm獲取認證數據
        CustomerRealm customerRealm = new CustomerRealm();
        //設置md5加密
        HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
        credentialsMatcher.setHashAlgorithmName("MD5");
        credentialsMatcher.setHashIterations(1024);//設置散列次數
        customerRealm.setCredentialsMatcher(credentialsMatcher);
        defaultSecurityManager.setRealm(customerRealm);
        //將安裝工具類中設置默認安全管理器
        SecurityUtils.setSecurityManager(defaultSecurityManager);
        //獲取主體對象
        Subject subject = SecurityUtils.getSubject();
        //建立token令牌
        UsernamePasswordToken token = new UsernamePasswordToken("xiaochen", "123");
        try {
            subject.login(token);//用戶登陸
            System.out.println("登陸成功~~");
        } catch (UnknownAccountException e) {
            e.printStackTrace();
            System.out.println("用戶名錯誤!!");
        }catch (IncorrectCredentialsException e){
            e.printStackTrace();
            System.out.println("密碼錯誤!!!");
        }
        //認證經過
        if(subject.isAuthenticated()){
            //基於角色權限管理
            boolean admin = subject.hasRole("admin");
            System.out.println(admin);

            boolean permitted = subject.isPermitted("product:create:001");
            System.out.println(permitted);
        }

    }
}

下一篇講解shiro搭配SpringBoot進行開發,謝謝!!!

相關文章
相關標籤/搜索