1. 程式人生 > >Spring Boot2整合Shiro(2):用 BCrypt 加密密碼

Spring Boot2整合Shiro(2):用 BCrypt 加密密碼

因為新專案沒有繼續用Shiro框架,再加上人比較懶,所以就沒有再寫新的關於 Shiro 的文章。但是今天發現已經有幾個朋友在 GitHub 上的給示例專案加星,也有留言諮詢的。我很高興這篇文章能夠幫到大家,也再次讓我感受到把在工作中學到的東西寫出來,和大家分享是這麼一件令人高興的事,用知乎上的話說這是一件長半衰期的事。我會繼續寫下去的

前言

上一篇文章,我們用 Spring Boot2 框架搭建了一個 web 專案,並且使用 Shiro 作為安全管理框架實現了使用者的身份認證,也就是登入。這篇文章首先簡要介紹了密碼儲存的演進史,然後結合程式碼介紹了在 Shiro 中怎麼使用 MD5、MD5 加鹽、 Bcrypt 等三種逐漸進步的方法加密密碼。

密碼儲存演進史

在上一篇文章中我們儲存在系統的使用者密碼還是明文,這顯然是很不合理和不專業的做法。作為一名工程師 專業自然是必須的。

常用的做法是使用加密演算法對使用者密碼加密然後儲存資料庫,當用戶登入時,對使用者密碼使用相同的演算法進行加密,然後與資料庫中的密文進行比對。作為一個合格專業的系統有兩點需要保證:

  1. 使用者賬號不易被破解
  2. 系統不能儲存使用者密碼原文

關於密碼儲存演進史這篇文章講的很好,朋友們可以看一下。從文章中我們知道密碼儲存大概分為幾個階段:

  • 明文儲存
  • 單向 hash(md5、sha256)
  • 單向 has h加鹽
  • 慢加密演算法(Bcrypt、Scrypt)

單項hash演算法

註冊使用者

因為要演示使用 Shiro 對密碼進行加密,上一節中的那種儲存密碼的方式非常不方便。在這一節我們新增一個使用者註冊的介面,使用使用者物件儲存使用者名稱和使用者密碼並且用一個集合儲存系統已註冊的使用者。

新建使用者類,並重寫toString方法。


package top.zhaodongxx.domain;

/**
 * @author zhaodong [email protected]
 * @version v1.0
 * @since 2018/7/19 10:40
 */
public class User {
    private
String username; private String password; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } @Override public String toString() { return "User{" + "username='" + username + '\'' + ", password='" + password + '\'' + '}'; } }

ShiroService 類中新增新增使用者的方法 addUser ,我們使用 SimpleHash 類使用 MD5 加密演算法對密碼進行加密,然後在提供一個通過使用者名稱查詢使用者的方法,用於後面的登入驗證。


package top.zhaodongxx.service;

import org.apache.shiro.crypto.hash.SimpleHash;
import org.springframework.stereotype.Service;
import top.zhaodongxx.domain.User;

import java.util.ArrayList;
import java.util.List;

/**
 * <P></P>
 *
 * @author zhaodong [email protected]
 * @version v1.0
 * @since 2018/3/30 22:59
 */
@Service
public class ShiroService {

    public static List<User> userList = new ArrayList<User>();

    public String getPasswordByUsername(String username) {
        switch (username) {
            case "liming":
                return "123";
            case "hanli":
                return "456";
            default:
                return null;
        }
    }

    //根據使用者名稱查詢使用者
    public User getUserByUsername(String username) {
        System.out.println("登陸的使用者 " + username);
        for (User user : userList) {
            if (username.equals(user.getUsername())) {
                return user;
            }
        }
        return null;
    }

    //新增使用者
    public User addUser(String username, String password) {
        //通過MD5的方式加密密碼
        String newpwd = new SimpleHash("MD5", password).toHex();

        User user = new User();
        user.setUsername(username);
        user.setPassword(newpwd);

        userList.add(user);

        System.out.println(user);
        return user;
    }
}

新建註冊控制器類 RegisterController ,並提供註冊介面 /register


package top.zhaodongxx.controller;

import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import top.zhaodongxx.domain.User;
import top.zhaodongxx.result.Result;
import top.zhaodongxx.result.ResultGenerator;
import top.zhaodongxx.service.ShiroService;

import javax.annotation.Resource;

/**
 * @author zhaodong [email protected]
 * @version v1.0
 * @since 2018/7/19 10:53
 */
@RestController
public class RegisterController {

    @Resource
    private ShiroService shiroService;

    @PostMapping("/register")
    public Result register(String username, String password) {

        User user = shiroService.addUser(username, password);
        return ResultGenerator.genSuccessResult(user).setMessage("註冊成功");
    }
}

如下體所示到這一步,我們就已經新建了一個使用者註冊的介面。

這裡寫圖片描述

為Shiro配置加密演算法

配置hash演算法加密主要分為三步:

  • 建立憑證匹配器 HashedCredentialsMatcher
  • 為我們自定義的 MyShiroRealm Bean 新增憑證匹配器 HashedCredentialsMatcher
  • 修改 MyShiroRealmdoGetAuthenticationInfo 方法的獲取使用者的方法

在 Shiro 配置檔案 ShiroConfig 中建立憑證匹配器 HashedCredentialsMatcher


/**
    * 憑證匹配器
    *
    * @return
    */
@Bean
public HashedCredentialsMatcher hashedCredentialsMatcher() {
    HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
    /**
        * hash演算法:這裡使用MD5演算法;
        */
    hashedCredentialsMatcher.setHashAlgorithmName("MD5");
    /**
        * 雜湊的次數,比如雜湊兩次,相當於 md5(md5(""));
        */
    hashedCredentialsMatcher.setHashIterations(1);

    return hashedCredentialsMatcher;
}

在 Shiro 配置檔案 ShiroConfig 中為我們自定義的 MyShiroRealm Bean 新增憑證匹配器 HashedCredentialsMatcher


/**
    * 自定義的Realm
    */
@Bean(name = "myShiroRealm")
public MyShiroRealm myShiroRealm(){
    MyShiroRealm myShiroRealm = new MyShiroRealm();
    myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
    return myShiroRealm;
}

修改 MyShiroRealmdoGetAuthenticationInfo 方法


@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
    //獲取使用者賬號
    String username = token.getPrincipal().toString();

    User user = shiroService.getUserByUsername(username);
    if (!ObjectUtils.isEmpty(user)) {

        String realmName = getName();
        return new SimpleAuthenticationInfo(username, user.getPassword(), realmName);
    }
    return null;
}

到目前為止,我們已經成功的為 Shiro 配置了 MD5 這種加密方式。
下面我們來測試一下,先重啟專案,註冊新的賬號,然後等新賬號登入。如下圖所示,登入成功。

這裡寫圖片描述
這裡寫圖片描述

單向hash加鹽

單向hash演算法,保證了即使是系統管理員也不能輕易的通過密文反推出密碼原文。但是單向 hash 還是有個致命的缺點:相同的密碼,hash值一樣

改進的措施是引入一個 隨機的因子 摻雜進明文中進行 hash 計算,這樣的隨機因子通常被稱之為鹽 (salt)。salt 一般是使用者相關的,且要保證在一個系統內每一個使用者的鹽都不相同,這樣就可以保證即使密碼相同,hash 值也一定不相同。一般的做法是用使用者表中的主鍵作為鹽。因為在我們這個工程中沒有資料庫,所以我們把使用者名稱作為鹽來示範一下。

為 Shiro 的單項 hash 演算法加上鹽需要兩步:

  • 修改註冊使用者的加密演算法
  • 修改 MyShiroRealm 類中 doGetAuthenticationInfo 方法的返回值 SimpleAuthenticationInfo 的新建方法引數:鹽

修改註冊使用者的加密演算法

String newpwd = new SimpleHash("MD5", password, ByteSource.Util.bytes(username)).toHex();

@Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        //獲取使用者賬號
        String username = token.getPrincipal().toString();

        User user = shiroService.getUserByUsername(username);
        if (!ObjectUtils.isEmpty(user)) {

            /**
             * 四個引數
             * principal:認證的實體資訊,可以是username,也可以是資料庫表對應的使用者的實體物件
             * credentials:資料庫中的密碼(經過加密的密碼)
             * credentialsSalt:鹽值(使用使用者名稱)
             * realmName:當前realm物件的name,呼叫父類的getName()方法即可
             */

            String realmName = getName();
            String credentials = user.getPassword();
            ByteSource credentialsSalt = ByteSource.Util.bytes(username);

            return new SimpleAuthenticationInfo(username, credentials, credentialsSalt, realmName);
        }
        return null;
    }

Bcrypt

Shiro 框架沒有內建 BCrypt 。需要我們引入新的庫 jBCrypt:


<dependency>
    <groupId>de.svenkubiak</groupId>
    <artifactId>jBCrypt</artifactId>
    <version>0.4.1</version>
</dependency>

在註冊使用者時修改加密方式,在 BCrypt 中我們不需要為每個使用者分配不同的鹽,只要使用 BCrypt.gensalt() 就可以生成鹽。


public String encodeByBCrypt(String password) {
    return BCrypt.hashpw(password, BCrypt.gensalt());
}

在 Shiro 配置檔案 ShiroConfig 中為我們自定義的 MyShiroRealm Bean 新增憑證匹配器 HashedCredentialsMatcher


/**
    * 自定義的Realm
    */
@Bean(name = "myShiroRealm")
public MyShiroRealm myShiroRealm() {
    MyShiroRealm myShiroRealm = new MyShiroRealm();
    //配置單項hash
    //myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());

    //配置 BCrypt
    myShiroRealm.setCredentialsMatcher(new CredentialsMatcher() {
        @Override
        public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
            UsernamePasswordToken userToken = (UsernamePasswordToken) token;
            //要驗證的明文密碼
            String plaintext = new String(userToken.getPassword());
            //資料庫中的加密後的密文
            String hashed = info.getCredentials().toString();

            return BCrypt.checkpw(plaintext, hashed);
        }
    });
    return myShiroRealm;
}

到此為止,我們使用 BCrypt 演算法加密了密碼。如下圖所示:

這裡寫圖片描述
這裡寫圖片描述
這裡寫圖片描述

可以發現,相同密碼加密兩次後的密文是不一樣的。

至此,我們已經完成了為 Shiro 使用 BCrypt 演算法加密密碼。

專案下載地址

參考資料

相關推薦

Spring Boot2整合Shiro2 BCrypt 加密密碼

因為新專案沒有繼續用Shiro框架,再加上人比較懶,所以就沒有再寫新的關於 Shiro 的文章。但是今天發現已經有幾個朋友在 GitHub 上的給示例專案加星,也有留言諮詢的。我很高興這篇文章能夠幫到大家,也再次讓我感受到把在工作中學到的東西寫出來,和大家分享是

Spring Boot2整合Shiro1身份認證

Spring Boot2整合Shiro(1):身份認證   前言 本文主要介紹了在Spring Boot2專案中整合Shiro實現登入認證。本文假設讀者已經對Shiro和基於RBAC的許可權控制系統有了基本的認識。  本專案沒有資料庫,也就沒有dao層,所有的使用者和

SpringMVC+Spring+Mybatis整合應用2

1. 包裝型別pojo物件中pojo屬性引數繫結    1. 對於屬性都是簡單型別的pojo類     如果需要將請求中的引數繫結到該pojo物件形參中,只需要保證請求中攜帶的key/value格式的引數中的key值與pojo類中的

Xilinx-ZYNQ7000系列-學習筆記2XADC測外部溫度值

Xilinx-ZYNQ7000系列-學習筆記(2):用XADC測外部溫度值 一、XADC簡介 Zynq器件XADC模組包括2個12位元1 MIPS的模數轉換器和相關的片上感測器,內建溫度感測器和功耗感測器,可實時監測片內結溫、各路電壓資料,並可輸出告警訊號。 XADC模擬輸入包括專用

自定義連結串列2連結串列的方式實現棧

通過學習自定義連結串列,瞭解連結串列的資料結構。 本篇以連結串列的方式實現棧。(參看以陣列的方式實現棧)   雖然自定義連結串列(1)中的連結串列的時間複雜度都為O(n),但若只對連結串列的表頭進行增、刪、查,都為O(1),利用這一點,可以用來實現棧。  

Spring Boot2企業版快速開發平臺ALBase2 使用Maven建立多模組專案

系統模組劃分 Maven多模組專案,適用於一些比較大的專案,通過合理的模組拆分,實現程式碼的複用,便於維護和管理。尤其是一些開源框架,也是採用多模組的方式,提供外掛整合,使用者可以根據需要配置指定的模組。   專案結構如下:     albase   (父

視圖框架Spring MVC 4.02

源碼 resolv pub 發出 variables 不同 圖解 rect js xml 在 《springMVC4(7)模型視圖方法源碼綜合分析》 一文中,我們介紹了ModelAndView的用法,它會在控制層方法調用完畢後作為返回值返回,裏面封裝好了我們的業務邏輯數據和

JavaEE互聯網輕量級框架整合開發書籍閱讀筆記2SSM+Redis概念理解

重復 技術 理解 size 從數據 一個 ron bat 互聯網 一、SSM+Redis的結構圖 在Java互聯網中,以Spring+SpringMVC+MyBatis(SSM)作為主流框架,SSM+Redis的結構圖如下: 二、下面介紹它們各自承擔的功能: 1.S

Spring基礎快速入門spring boot2SPRING INITIALIZR

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

Spring基礎快速入門spring cloud2服務發現之eureka

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

基於springboot2 框架整合2druid資料來源整合

前言 專案中使用了很多現成的框架,都是專案經理、架構師帶來的,從來沒有自己整合過!這次決定自己從零開始整合一次,以學習鞏固。過程中參考很多開源框架的思路,工具類等,若有侵權,請速速聯絡,一定妥善處理   一:簡介 druid是alibaba開源的資料庫連線池,號

Spring Boot學習總結2——Spring Boot整合Jsp

怎麼使用jsp上面起了疑問,查閱了多方資料,找到過其他人的部落格的描述,也找到了spring在github上的給出的例子,看完後稍微改動後成功 整合jsp,於是決定將整合過程記載下來。 無論使用

從無到有整合SpringMVC-MyBatis專案2搭建SpringMVC專案

       前言:本次搭建SpringMVC專案,建立在已完成從無到有整合SpringMVC-MyBatis專案(1):搭建JavaWeb專案 的基礎上,本篇的重點在於如何將SpringMVC框架引入到普通的JavaWeb專案中去,專案基於SpringMVC4.3.18版本

Spring Cloud系列2Spring Cloud Config

Spring Cloud Config 分散式配置管理系統我們在實現微服務架構時,每個微服務都有自己的配置檔案,配置諸如埠,服務名稱,資料庫連線等。在微服務數量比較多時,維護就會變得很困難,因此我們需要一箇中心配置服務。Spring Cloud Config 主要由兩部分組成

Spring Boot使用方法小札2執行定時任務

在Spring Boot中要定時執行一些任務可以不必要使用執行緒來實現,它為我們提供了一種方法來簡化任務的定時執行,這種方式是建立在@EnableScheduling和@Scheduled上的。 首先我們需要先建立一個需要定時執行的任務,如下: @Com

從零搭建Spring Boot腳手架2增加通用的功能

![](https://img2020.cnblogs.com/other/1739473/202008/1739473-20200806093905433-1262434332.png) ## 1. 前言 今天開始搭建我們的**kono Spring Boot**腳手架,首先會整合**Spring MV

Windows Phone開發2豎立自信,初試鋒茫

一鍵 優秀 保持 知識 sdn ant emulator 一個 動畫 上一篇文章中,我們聊了一些“大炮”話題,從這篇文章開始,我們一起來學習WP開發吧。 一、我們有哪些裝備。 安裝完VS 學習版 for WP後,也連同SDK一並安裝了,不必像安卓那樣,安裝JDK,下載

Spring 事務配置實戰過濾無需事務處理的查詢之類操作

log pla ssi pan spl tail gif aop img <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes

TCP/IP協議2網絡設備

數據包 服務器 網絡設備 風暴 二層交換機 不同的 中繼器 tcp/ip 解決 1、中繼器(Repeater) 中繼器工作在OSI的一層,我們知道,超5類線的傳輸距離最大為100米,超過這個距離信號就會衰減,中繼器就是為了防止信號變差,將網絡信號進行再生和重定時。 2、

MongoDB2 增刪改操作

db nosql mongo 增刪改 curd 附加命令:1、進入前端操作命令./mongo [ip:端口]說明:默認會自動選本地,端口270172、顯示所有的庫> show dbs; 或者 show databases;3、選擇庫> use 庫名;4、顯示庫所有的集合&g