今天處理了個登錄問題,有個PHP生成的用戶數據,要用JAVA項目去登錄,JAVA用的是shiro做的權限。
看了下PHP是用password_hash()
做的密碼hash和驗證,這個java里沒有找到合適的組件集成。
BCrypt類庫
這里找了個替代組件BCrypt類庫:
1 | <dependency> |
hash方式:
1 | String pwd="ceshi123"; |
這里注意一下,每次的hash結果不一樣。
因為結果里包含了version和cost等,驗證時要用BCrypt指定的驗證方法,不能直接比hash結果。
驗證方式:
1 | String hash="$2y$10$ozLf.I8c6WnqJ.3hSPhn7OGYALCRi9pWv0cFgQeLPlbk08OZn.DfO"; |
shiro登錄驗證
首先要把用戶信息查出來:
1 | protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { |
驗證密碼:
1 | protected void assertCredentialsMatch(AuthenticationToken authcToken, |
這里驗證通過直接返回,沒有驗證通過要拋出異常。
password_hash
1 | string password_hash ( string $password , integer $algo [, array $options ]) |
它有三個參數:密碼、哈希算法、選項。前兩項為必須的。 讓我們使用password_hash()簡單的創建一個哈希密碼: 復制代碼 代碼如下:
1 | $pwd = "123456"; |
上例輸出結果類似:
1 | $2y$10$4kAu4FNGuolmRmSSHgKEMe3DbG5pm3diikFkiAKNh.Sf1tPbB4uo2 |
并且刷新頁面該哈希值也會不斷的變化。
這里便有了一個疑問,同樣的值同意的算法不同的值如何驗證如何實現。 上述的方法支持三種算法
PASSWORD_DEFAULT - 使用 bcrypt 算法 (PHP 5.5.0 默認)。 注意,該常量會隨著 PHP 加入更新更高強度的算法而改變。所以,使用此常量生成結果的長度將在未來有變化。 因此,數據庫里儲存結果的列可超過60個字符(最好是255個字符)。
PASSWORD_BCRYPT - 使用 CRYPT_BLOWFISH 算法創建散列。 這會產生兼容使用 “$2y$” 的 crypt()。 結果將會是 60 個字符的字符串, 或者在失敗時返回 FALSE。
PASSWORD_ARGON2I - 使用 Argon2 散列算法創建散列。
PASSWORD_BCRYPT 支持的選項:
salt(string) - 手動提供散列密碼的鹽值(salt)。這將避免自動生成鹽值(salt)。 省略此值后,password_hash() 為每個密碼散列自動生成隨機的鹽值。
鹽值(salt)選項從 PHP 7.0.0 開始被廢棄(deprecated)了。 現在最好選擇簡單的使用默認產生的鹽值。
cost (integer) - 代表算法使用的 cost。crypt() 頁面上有 cost 值的例子。 省略時,默認值是 10。這個 cost個不錯的底線,但也許可以根據自己硬件的情況,加大這個值。
我要處理的密碼是 因為 password_hash 使用的是 crypt 算法, 因此參與計算 hash值的:
算法(就像身份證開頭能知道省份一樣, 由鹽值的格式決定), cost(默認10) 和 鹽值 是在$hash中可以直接看出來的!
所以說, Laravel 中bcrypt的鹽值是PHP自動隨機生成的字符, 雖然同一個密碼每次計算的hash不一樣.
但是通過 $hash 和 密碼, 卻可以驗證密碼的正確性!
具體來說, 比如這個
1 | $hash = password_hash('password',PASSWORD_BCRYPT,['cost' => 10]); |
那么我們從這個 crypt的hash值中可以看到, 因為以$2y$開頭, 所以它的算法是 CRYPT_BLOWFISH .
同時 CRYPT_BLOWFISH 算法鹽值格式規定是 :
1 | 以$2y$開頭 + 一個兩位cost參數 + $ + 22位隨機字符("./0-9A-Za-z") |