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

寫在前面

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

那麼,請你們思考一個問題,咱們的應用程序真的安全了嗎?java

我把咱麼上一篇文章當中的認證方法代碼摘抄在下面給你們看看算法

/**認證
 * @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行當中,咱們模擬從數據庫當中查詢出了用戶的註冊信息,包括帳戶和密碼,而且這裏的密碼是明文的。這意味着若是咱們的用戶密碼被泄露了(這裏用戶緣由致使的泄露除外),那麼一些不友好的朋友將能夠隨意的進出咱們的系統。這不但讓咱們的應用程序變得不安全,並且還會讓咱們面臨法律風險。mongodb

如下內容摘自《網絡安全法》數據庫

第三十四條 網絡運營者應當創建健全用戶信息保護制 度,增強對用戶我的信息、隱私和商業祕密的保護apache

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

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

因此,咱們須要對用戶信息進行加密保護。對於帳戶密碼信息,咱們應該採起不可逆的加密方式。也就是說,咱們對密碼進行加密存儲後,哪怕其獲取了咱們的密文,他也不能獲得咱們的密碼明文。這樣就對咱們的用戶信息起到了一個很好的保護做用。dom

MD5加密算法和salt鹽值加密

MD5加密算法

什麼是MD5加密

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

特色

  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的相關內容,文章可能有點長,會考慮分兩次寫。請你們多多關注。

歡迎你們點贊、轉發、分享。轉載註明出處時要帶有原文連接。

相關文章
相關標籤/搜索