shiro入門學習--使用MD5和salt進行加密|練氣后期


寫在前面

在上一篇文章《Shiro入門學習---使用自定義Realm完成認證|練氣中期》當中,我們學會了使用自定義Realm實現shiro數據源的切換,我們可以切換成從關系數據庫如MySQL中讀取用戶認證信息進行認證,亦可從非關系型數據庫例如mongodb中讀取用戶認證信息進行認證。這是一個偉大的進度,這使得我們可以使用shiro來提升我們應用程序的安全度了,

那么,請大家思考一個問題,我們的應用程序真的安全了嗎?

我把咱么上一篇文章當中的認證方法代碼摘抄在下面給大家看看

/**認證
 * @author 賴柄灃 bingfengdev@aliyun.com
 * @date 2020-10-04 11:01:50
 * @param authenticationToken
 * @return org.apache.shiro.authz.AuthorizationInfo
 * @throws AuthenticationException
 * @version 1.0
 */
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
    // 1. 從token中獲取用戶名
    String principal = (String) authenticationToken.getPrincipal();

    //2. 根據用戶名查詢數據庫並封裝成authenticationinfo對象返回(模擬)
    if (principal == "xiangbei") {
        AuthenticationInfo authInfo = new SimpleAuthenticationInfo("xiangbei","123",this.getName());
        return authInfo;
    }

    return null;
}

在16行當中,我們模擬從數據庫當中查詢出了用戶的注冊信息,包括賬戶和密碼,並且這里的密碼是明文的。這意味着如果我們的用戶密碼被泄露了(這里用戶原因導致的泄露除外),那么一些不友好的朋友將可以隨意的進出我們的系統。這不但讓我們的應用程序變得不安全,而且還會讓我們面臨法律風險。

以下內容摘自《網絡安全法》

第三十四條 網絡運營者應當建立健全用戶信息保護制 度,加強對用戶個人信息、隱私和商業秘密的保護

第三十五條 網絡運營者收集、使用公民個人信息,應當 遵循合法、正當、必要的原則,明示收集、使用信息的目 的、方式和范圍,並經被收集者同意。 網絡運營者不得收集與其提供的服務無關的公民個人 信息,不得違反法律、行政法規的規定和雙方的約定收 集、使用公民個人信息,並應當依照法律、行政法規的規 定或者與用戶的約定,處理其保存的公民個人信息。 網絡運營者收集、使用公民個人信息,應當公開其收 集、使用規則。

第三十六條 網絡運營者對其收集的公民個人信息必須嚴 格保密,不得泄露、篡改、毀損,不得出售或者非法向他 人提供。 網絡運營者應當采取技術措施和其他必要措施,確保 公民個人信息安全,防止其收集的公民個人信息泄露、毀 損、丟失。在發生或者可能發生信息泄露、毀損、丟失的 情況時,應當立即采取補救措施,告知可能受到影響的用 戶,並按照規定向有關主管部門報告。

所以,我們需要對用戶信息進行加密保護。對於賬戶密碼信息,我們應該采取不可逆的加密方式。也就是說,我們對密碼進行加密存儲后,哪怕其獲取了我們的密文,他也不能得到我們的密碼明文。這樣就對我們的用戶信息起到了一個很好的保護作用。

MD5加密算法和salt鹽值加密

MD5加密算法

什么是MD5加密

MD5信息摘要算法(英語:MD5 Message-Digest Algorithm),一種被廣泛使用的密碼散列函數,可以產生出一個128位(16字節)的散列值(hash value),用於確保信息傳輸完整一致。

特點

  1. 不可逆,也就是說其本身上不能由密文推出明文,

    但是,如果明文比較簡單常見,還是存在泄露風險,例如先生成好簡單明文的密文,然后使用窮舉法進行破解;

  2. 對於同一個明文,無論加密多少次其密文都是一樣的;

  3. 生成的結果始終是一個16進制的32位字符串。

作用

  1. 數字簽名(校驗和)

    例如對於一份文件,為了保證網絡傳輸當中不發生改變,我提前對其用md5加密算法進行加密,得到一段密文。我將這份文件和密文分別發給你。你在收到文件后也對其使用md5加密一次,得到一個密文。這時,你就可以比較兩個密文是否一致,如果一致,則文件沒有被篡改,反之,文件已經被篡改。

  2. 加密

  3. 垃圾郵件篩選

    原理和作用1一樣

salt鹽值加密策略

在上面的介紹md5加密算法時我們講到,雖然MD5算法本身不可逆,但是如果用戶采用簡單的字符串作為密碼的話,仍然有被暴力破解的風險。因此,為了解決這個問題,我們需要在對密碼加密之前使其變得復雜化。

而加鹽就是其中的一種方式。所謂的加鹽就是在原密碼的基礎上,加上一段隨機字符串。然后再加密。

當然,如果鹽值隨着密碼一起被泄露出去,也是存在着密碼被破解的風險的,我們只能做到相對安全。

為了增加破解難度,可以在加鹽時采取一定的策略,例如哈希加鹽、加密后多次哈希。

當然,這要在安全跟性能直接做個平衡。

shiro使用MD5+salt加密

分析

在進行編碼之前,我們需要理一下流程:

  1. 用戶注冊或系統分配賬戶時,服務層在接收到賬號和憑證信息后,先對憑證信息采用md5+salt進行加密處理,然后將賬號、加密后的密碼還有鹽值存入數據庫;

  2. 用戶登錄請求接收后,先根據請求中的賬號查詢數據庫:

    2.1 如果沒有查到,直接返回“用戶名或密碼錯誤”的類似提示

    2.2 如果查到了賬戶信息,就執行步驟3;

  3. 將賬號和加鹽后憑證封裝成AuthenticationInfo對象返回給shiro,shiro執行步驟4

  4. 對請求中的憑證進行加鹽處理並執行步驟5

  5. 對加鹽后的憑證進行md5加密,並將密文跟數據庫當中的存儲的密文進行比對:

    5.1 如果匹配成功,則認證通過

    5.2 如果匹配失敗,則返回“用戶名或密碼錯誤”的類似提示

實現

編寫自定義Realm並切換掉默認的憑證匹配器

/**自定義Realm對象
 * @author 賴柄灃 bingfengdev@aliyun.com
 * @version 1.0
 * @date 2020/10/4 11:00
 */
public class MySqlRealm extends AuthorizingRealm {

    public MySqlRealm() {
        //設置憑證匹配器,修改為hash憑證匹配器
        HashedCredentialsMatcher myCredentialsMatcher = new HashedCredentialsMatcher();
        //設置算法
        myCredentialsMatcher.setHashAlgorithmName("md5");
        //散列次數
        myCredentialsMatcher.setHashIterations(1024);
        this.setCredentialsMatcher(myCredentialsMatcher);
    }

    /**授權
     * @author 賴柄灃 bingfengdev@aliyun.com
     * @date 2020-10-04 11:01:50
     * @param principalCollection
     * @return org.apache.shiro.authz.AuthorizationInfo
     * @throws AuthenticationException
     * @version 1.0
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {

        return null;
    }

    /**認證
     * @author 賴柄灃 bingfengdev@aliyun.com
     * @date 2020-10-04 11:01:50
     * @param authenticationToken
     * @return org.apache.shiro.authz.AuthorizationInfo
     * @throws AuthenticationException
     * @version 1.0
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        // 1. 從token中獲取用戶名
        String principal = (String) authenticationToken.getPrincipal();

        //2. 根據用戶名查詢數據庫並封裝成authenticationinfo對象返回(模擬)
        if (principal == "xiangbei") {
            //四個參數分別是數據庫中的賬號、加密后的密碼、鹽值、realm名字
            AuthenticationInfo authInfo = new SimpleAuthenticationInfo("xiangbei",
                    "ff595c47b51b4cf70fddce090f68879e",
                    ByteSource.Util.bytes("ee575f62-0dda-44f2-b75e-4efef795018f"),
                    this.getName());
            return authInfo;
        }

        return null;
    }
}

編寫認證器

/**認證管理器
 * @author 賴柄灃 bingfengdev@aliyun.com
 * @version 1.0
 * @date 2020/10/4 11:11
 */
public class CurrentSystemAuthenticator {
    private DefaultSecurityManager securityManager;
    public CurrentSystemAuthenticator() {
        //創建安全管理器
        securityManager = new DefaultSecurityManager();

        //設置自定義realm
        this.securityManager.setRealm(new MySqlRealm());

        //將安全管理器設置到安全工具類中
        SecurityUtils.setSecurityManager(securityManager);

    }

    public void authenticate(String username,String password) {
        //獲取當前登錄主題
        Subject subject = SecurityUtils.getSubject();

        //生成toeken
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);

        //進行認證
        try {
            subject.login(token);
        }catch (UnknownAccountException | IncorrectCredentialsException e) {
            System.out.println("用戶名或密碼不正確");
        }


        //打印認證狀態
        if (subject.isAuthenticated()){
            System.out.println(token.getPrincipal()+" 認證通過!");
        }else {
            System.out.println(token.getPrincipal()+" 認證未通過!");
        }





    }
}

測試

生成加密后的密碼
/**
 * @author 賴柄灃 bingfengdev@aliyun.com
 * @version 1.0
 * @date 2020/10/4 21:37
 */
public class Md5Test {

    @Test
    public void testMd5(){
        //三個參數分別對應密碼明文、鹽值、散列次數
        String salt = UUID.randomUUID().toString();
        Md5Hash md5Hash = new Md5Hash("123", salt,1024);
        System.out.println("密文:"+md5Hash.toHex());
        System.out.println("鹽值:"+salt);
    }
}

輸出

密文:ff595c47b51b4cf70fddce090f68879e
鹽值:ee575f62-0dda-44f2-b75e-4efef795018f
進行認證測試
/**
 * @author 賴柄灃 bingfengdev@aliyun.com
 * @version 1.0
 * @date 2020/10/4 11:20
 */
public class AuthcTest {
    private CurrentSystemAuthenticator authenticator;
    @Before
    public void init() {
        this.authenticator = new CurrentSystemAuthenticator();
    }

    @Test
    public void testAuthc(){
        this.authenticator.authenticate("xiangbei","123");
    }


}

輸出

xiangbei 認證通過!

寫在最后

在這篇文章當中,我們主要是簡單了解了shiro中的加密策略以及如何使用MD5+salt對密碼進行加密。大家可以嘗試着將MD5換成SHA-256加密算法再測一下。

在下一篇文章當中,作者將介紹SpringBoot整合Shiro的相關內容,文章可能有點長,會考慮分兩次寫。請大家多多關注。

歡迎大家點贊、轉發、分享。轉載注明出處時要帶有原文鏈接。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM