Shiro第二篇【介紹Shiro、認證流程、自定義realm、自定義realm支持md5】

什麼是Shiro

shiro是apache的一個開源框架,是一個權限管理的框架,實現 用戶認證、用戶受權java

spring中有spring security (原名Acegi),是一個權限框架,它和spring依賴過於緊密,沒有shiro使用簡單。
shiro不依賴於spring,shiro不只能夠實現 web應用的權限管理,還能夠實現c/s系統,分佈式系統權限管理,shiro屬於輕量框架,愈來愈多企業項目開始使用shiro。web

Shiro架構:算法

這裏寫圖片描述

  • subject:主體,能夠是用戶也能夠是程序,主體要訪問系統,系統須要對主體進行認證、受權。
  • securityManager:安全管理器,主體進行認證和受權都 是經過securityManager進行。
  • authenticator:認證器,主體進行認證最終經過authenticator進行的。
  • authorizer:受權器,主體進行受權最終經過authorizer進行的。
  • sessionManager:web應用中通常是用web容器對session進行管理,shiro也提供一套session管理的方式。
  • SessionDao: 經過SessionDao管理session數據,針對個性化的session數據存儲須要使用sessionDao。
  • cache Manager:緩存管理器,主要對session和受權數據進行緩存,好比將受權數據經過cacheManager進行緩存管理,和ehcache整合對緩存數據進行管理。
  • realm:域,領域,至關於數據源,經過realm存取認證、受權相關數據。

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

  • 好比md5散列算法。

爲何使用Shiro

咱們在使用URL攔截的時候,要將全部的URL都配置起來,繁瑣、不易維護數據庫

而咱們的Shiro實現系統的權限管理,有效提升開發效率,從而下降開發成本。apache

Shiro認證

導入jar包

咱們使用的是Maven的座標就好了緩存

<dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-core</artifactId>
            <version>1.2.3</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-web</artifactId>
            <version>1.2.3</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.2.3</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-ehcache</artifactId>
            <version>1.2.3</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-quartz</artifactId>
            <version>1.2.3</version>
        </dependency>

固然了,咱們也能夠把Shiro相關的jar包所有導入進去安全

<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-all</artifactId>
    <version>1.2.3</version>
</dependency>

Shiro認證流程

這裏寫圖片描述

經過配置文件建立工廠

這裏寫圖片描述

// 用戶登錄和退出
    @Test
    public void testLoginAndLogout() {

        // 建立securityManager工廠,經過ini配置文件建立securityManager工廠
        Factory<SecurityManager> factory = new IniSecurityManagerFactory(
                "classpath:shiro-first.ini");

        // 建立SecurityManager
        SecurityManager securityManager = factory.getInstance();

        // 將securityManager設置當前的運行環境中
        SecurityUtils.setSecurityManager(securityManager);

        // 從SecurityUtils裏邊建立一個subject
        Subject subject = SecurityUtils.getSubject();

        // 在認證提交前準備token(令牌)
        // 這裏的帳號和密碼 未來是由用戶輸入進去
        UsernamePasswordToken token = new UsernamePasswordToken("zhangsan",
                "111111");
        try {
            // 執行認證提交
            subject.login(token);
        } catch (AuthenticationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        // 是否定證經過
        boolean isAuthenticated = subject.isAuthenticated();

        System.out.println("是否定證經過:" + isAuthenticated);

        // 退出操做
        subject.logout();

        // 是否定證經過
        isAuthenticated = subject.isAuthenticated();

        System.out.println("是否定證經過:" + isAuthenticated);

    }

這裏寫圖片描述

小結

ModularRealmAuthenticator做用進行認證,須要調用realm查詢用戶信息(在數據庫中存在用戶信息)
ModularRealmAuthenticator進行密碼對比(認證過程)。
realm:須要根據token中的身份信息去查詢數據庫(入門程序使用ini配置文件),若是查到用戶返回認證信息,若是查詢不到返回nullmarkdown

自定義realm

從第一個認證程序咱們能夠看見,咱們所說的流程,是認證器去找realm去查詢咱們相對應的數據。而默認的realm是直接去與配置文件來比對的,通常地,咱們在開發中都是讓realm去數據庫中比對。
所以,咱們須要自定義realmsession

這裏寫圖片描述

public class CustomRealm extends AuthorizingRealm {

    // 設置realm的名稱
    @Override
    public void setName(String name) {
        super.setName("customRealm");
    }

    // 用於認證
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(
            AuthenticationToken token) throws AuthenticationException {

        // token是用戶輸入的
        // 第一步從token中取出身份信息
        String userCode = (String) token.getPrincipal();

        // 第二步:根據用戶輸入的userCode從數據庫查詢
        // ....


        // 若是查詢不到返回null
        //數據庫中用戶帳號是zhangsansan
        /*if(!userCode.equals("zhangsansan")){// return null; }*/


        // 模擬從數據庫查詢到密碼
        String password = "111112";

        // 若是查詢到返回認證信息AuthenticationInfo

        SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(
                userCode, password, this.getName());

        return simpleAuthenticationInfo;
    }

    // 用於受權
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(
            PrincipalCollection principals) {
        // TODO Auto-generated method stub
        return null;
    }

}

配置realm

須要在shiro-realm.ini配置realm注入到securityManager中。

這裏寫圖片描述

測試自定義realm

同上邊的入門程序,須要更改ini配置文件路徑:

同上邊的入門程序,須要更改ini配置文件路徑:
Factory<SecurityManager> factory = new IniSecurityManagerFactory(
                "classpath:shiro-realm.ini");

散列算法

咱們若是知道md5,咱們就會知道md5是不可逆的,可是若是設置了一些安全性比較低的密碼:111111…即時是不可逆的,但仍是能夠經過暴力算法來獲得md5對應的明文…

建議對md5進行散列時加salt(鹽),進行加密至關 於對原始密碼+鹽進行散列。\

正常使用時散列方法:

  • 在程序中對原始密碼+鹽進行散列,將散列值存儲到數據庫中,而且還要將鹽也要存儲在數據庫中。

測試:

public class MD5Test {

    public static void main(String[] args) {

        //原始 密碼 
        String source = "111111";
        //鹽
        String salt = "qwerty";
        //散列次數
        int hashIterations = 2;
        //上邊散列1次:f3694f162729b7d0254c6e40260bf15c
        //上邊散列2次:36f2dfa24d0a9fa97276abbe13e596fc


        //構造方法中:
        //第一個參數:明文,原始密碼 
        //第二個參數:鹽,經過使用隨機數
        //第三個參數:散列的次數,好比散列兩次,至關 於md5(md5(''))
        Md5Hash md5Hash = new Md5Hash(source, salt, hashIterations);

        String password_md5 =  md5Hash.toString();
        System.out.println(password_md5);
        //第一個參數:散列算法 
        SimpleHash simpleHash = new SimpleHash("md5", source, salt, hashIterations);
        System.out.println(simpleHash.toString());
    }

}

自定義realm支持md5

自定義realm

public class CustomRealmMd5 extends AuthorizingRealm {

    // 設置realm的名稱
    @Override
    public void setName(String name) {
        super.setName("customRealmMd5");
    }

    // 用於認證
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(
            AuthenticationToken token) throws AuthenticationException {

        // token是用戶輸入的
        // 第一步從token中取出身份信息
        String userCode = (String) token.getPrincipal();

        // 第二步:根據用戶輸入的userCode從數據庫查詢
        // ....

        // 若是查詢不到返回null
        // 數據庫中用戶帳號是zhangsansan
        /* * if(!userCode.equals("zhangsansan")){// return null; } */

        // 模擬從數據庫查詢到密碼,散列值
        String password = "f3694f162729b7d0254c6e40260bf15c";
        // 從數據庫獲取salt
        String salt = "qwerty";
        //上邊散列值和鹽對應的明文:111111

        // 若是查詢到返回認證信息AuthenticationInfo
        SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(
                userCode, password, ByteSource.Util.bytes(salt), this.getName());

        return simpleAuthenticationInfo;
    }

    // 用於受權
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(
            PrincipalCollection principals) {
        // TODO Auto-generated method stub
        return null;
    }

}

配置文件:

這裏寫圖片描述

測試:

// 自定義realm實現散列值匹配
    @Test
    public void testCustomRealmMd5() {

        // 建立securityManager工廠,經過ini配置文件建立securityManager工廠
        Factory<SecurityManager> factory = new IniSecurityManagerFactory(
                "classpath:shiro-realm-md5.ini");

        // 建立SecurityManager
        SecurityManager securityManager = factory.getInstance();

        // 將securityManager設置當前的運行環境中
        SecurityUtils.setSecurityManager(securityManager);

        // 從SecurityUtils裏邊建立一個subject
        Subject subject = SecurityUtils.getSubject();

        // 在認證提交前準備token(令牌)
        // 這裏的帳號和密碼 未來是由用戶輸入進去
        UsernamePasswordToken token = new UsernamePasswordToken("zhangsan",
                "222222");

        try {
            // 執行認證提交
            subject.login(token);
        } catch (AuthenticationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        // 是否定證經過
        boolean isAuthenticated = subject.isAuthenticated();

        System.out.println("是否定證經過:" + isAuthenticated);

    }
相關文章
相關標籤/搜索