SHA(secure hashing algorithm)表示安全哈希算法.SHA是MD5的修正版本,用于数据摘要和认证。哈希和加密类似,唯一区别是哈希是单项的,即哈希后的数据无法解密。SHA有不同的算法,主要包括SHA-1, SHA-2, SHA-256, SHA-512, SHA-224, and SHA-384等,其中SHA-256是SHA-2家族的一个成员,它把原数据转为256字节固定长度摘要信息,SHA256的内部块大小为32位。本文介绍Golang如何使用sha256算法实现对配置文件的监控。
Golang hash256实现包
hash256包实现了FIPS 180-4规范中定义的SHA224、SHA256哈希算法。主要包括下面几个函数:
func New() hash.Hash
func Sum256(data []byte) [Size]byte
使用sha256.New()
下面示例使用New()函数返回Hash.hash:
package main
import (
"crypto/sha256"
"fmt"
)
func main() {
h := sha256.New()
h.Write([]byte("this is a password"))
// Calculate and print the hash
fmt.Printf("%x", h.Sum(nil))
}
过程很简单,运行输出结果:
289ca48885442b5480dd76df484e1f90867a2961493b7c60e542e84addce5d1e
使用sha256.Sum256()函数
package main
import (
"crypto/sha256"
"fmt"
)
func main() {
sum := sha256.Sum256([]byte("this is a password"))
fmt.Printf("%x", sum)
}
更简洁,输出结果一样。
下面示例展示两个字符串,尽管只有一个字符微小差异,但生成的hash却完全不同:
package main
import (
"crypto/sha256"
"fmt"
)
func main() {
sum := sha256.Sum256([]byte("this is a password"))
sumCap := sha256.Sum256([]byte("This is a password"))
fmt.Printf("lowercase hash: %x", sum)
fmt.Println("")
fmt.Printf("Capital hash: %x", sumCap)
}
运行输出结果:
lowercase hash: 289ca48885442b5480dd76df484e1f90867a2961493b7c60e542e84addce5d1e
Capital hash: 9ae12b1403d242c53b0ea80137de34856b3495c3c49670aa77c7ec99eadbba6e
监控配置文件变化
我们需要观察文件是否变化,标准实现使用time.Ticker每隔几秒重新计算配置文件的哈希值,如果哈希值发生变化,则重新加载。
获取配置hash值
func getCfgHash() string {
file, err := os.Open("test.cfg")
defer file.Close()
if err != nil {
panic(err)
}
hash := sha256.New()
if _, err := io.Copy(hash, file); err != nil {
panic(err)
}
sum := fmt.Sprintf("%x", hash.Sum(nil))
return sum
}
上面方法步骤:
- 打开配置文件
- 从crypto/sha256创建hash.Hash对象
- 解析文件内容到hash对象
- 调用Sum方法获得hash值
上面示例中test测试文件,可以随便输入一些内容,生成文件hash值。
下面实现比较hash值方法:
func compare(new string) bool {
var oldStr = ""
oldFile, _ := os.Open("tmp.hash")
oldBytes, _ := io.ReadAll(oldFile)
oldStr = string(oldBytes)
oldFile.Close()
if oldStr != new {
newFile, _ := os.Create("tmp.hash")
fmt.Println("new hash:", new)
newFile.WriteString(new)
RefreshCfg()
return false
}
return true
}
先加载上一次保存的配置文件hash值,与本次传入最新hash值进行比较,如不同则保存最新hash值用于下一次比较,同时调用RefreshCfg()方法,该方法是具体业务实现,这里仅给出空实现:
func RefreshCfg() {
fmt.Println(" refresh config information.")
}
最后是main函数部分:
func main() {
// 先记录配置文件hash值
cfgHash := getCfgHash()
fmt.Println("cfg hash:", cfgHash)
file, _ := os.Create("tmp.hash")
file.WriteString(cfgHash)
file.Close()
// define an interval and the ticker for this interval
interval := time.Duration(2) * time.Second
// create a new Ticker
tk := time.NewTicker(interval)
// start the ticker by constructing a loop
for range tk.C {
fmt.Println("time running...")
loadStr := getCfgHash()
if !compare(loadStr) {
fmt.Println("config file has changed...")
}
}
}
首先保存当前配置文件的Hash值。然后利用Ticker每2秒比较一次比较。运行程序修改配置文件,可以立刻看到程序监控到变化并调用RefreshCfg方法。
下面给出完整代码实现:
package main
import (
"crypto/sha256"
"time"
"fmt"
"io"
"os"
)
func main() {
// 先记录配置文件hash值
cfgHash := getCfgHash()
fmt.Println("cfg hash:", cfgHash)
file, _ := os.Create("tmp.hash")
file.WriteString(cfgHash)
file.Close()
// define an interval and the ticker for this interval
interval := time.Duration(2) * time.Second
// create a new Ticker
tk := time.NewTicker(interval)
// start the ticker by constructing a loop
for range tk.C {
fmt.Println("time running...")
loadStr := getCfgHash()
if !compare(loadStr) {
fmt.Println("config file has changed...")
}
}
}
func RefreshCfg() {
fmt.Println(" refresh config information.")
}
func getCfgHash() string {
file, err := os.Open("test.cfg")
defer file.Close()
if err != nil {
panic(err)
}
hash := sha256.New()
if _, err := io.Copy(hash, file); err != nil {
panic(err)
}
sum := fmt.Sprintf("%x", hash.Sum(nil))
return sum
}
func compare(new string) bool {
var oldStr = ""
oldFile, _ := os.Open("tmp.hash")
oldBytes, _ := io.ReadAll(oldFile)
oldStr = string(oldBytes)
oldFile.Close()
if oldStr != new {
newFile, _ := os.Create("tmp.hash")
fmt.Println("new hash:", new)
newFile.WriteString(new)
RefreshCfg()
return false
}
return true
}
总结
crypto/sha256