Go-Excelize API源码阅读三十一——ProtectSheet(sheet string, settings *SheetProtectionOptions)

Go-Excelize API源码阅读(三十一)—— ProtectSheet(sheet string, settings *SheetProtectionOptions)

开源摘星计划(WeOpen Star) 是由腾源会 2022 年推出的全新项目,旨在为开源人提供成长激励,为开源项目提供成长支持,助力开发者更好地了解开源,更快地跨越鸿沟,参与到开源的具体贡献与实践中。

不管你是开源萌新,还是希望更深度参与开源贡献的老兵,跟随“开源摘星计划”开启你的开源之旅,从一篇学习笔记、到一段代码的提交,不断挖掘自己的潜能,最终成长为开源社区的“闪亮之星”。

我们将同你一起,探索更多的可能性!

项目地址: WeOpen-Star:https://github.com/weopenprojects/WeOpen-Star

一、Go-Excelize简介

Excelize 是 Go 语言编写的用于操作 Office Excel 文档基础库,基于 ECMA-376,ISO/IEC 29500 国际标准。可以使用它来读取、写入由 Microsoft Excel™ 2007 及以上版本创建的电子表格文档。支持 XLAM / XLSM / XLSX / XLTM / XLTX 等多种文档格式,高度兼容带有样式、图片(表)、透视表、切片器等复杂组件的文档,并提供流式读写 API,用于处理包含大规模数据的工作簿。可应用于各类报表平台、云计算、边缘计算等系统。使用本类库要求使用的 Go 语言为 1.15 或更高版本。

二、ProtectSheet(sheet string, settings *SheetProtectionOptions)

func (f *File) ProtectSheet(sheet string, settings *SheetProtectionOptions) error

防止其他用户意外或有意更改、移动或删除工作表中的数据。可选字段 AlgorithmName 支持指定哈希算法 XOR、MD4、MD5、SHA-1、SHA-256、SHA-384 或 SHA-512,如果未指定哈希算法,默认使用 XOR 算法。例如,将名为 Sheet1 的工作表设置密码保护,但是允许选择锁定的单元格、选择未锁定的单元格、编辑方案:

err := f.ProtectSheet("Sheet1", &excelize.SheetProtectionOptions{
    AlgorithmName: "SHA-512",
    Password:      "password",
    EditScenarios: false,
})

SheetProtectionOptions 定义了保护工作表的设置选项。

type SheetProtectionOptions struct {
    AlgorithmName       string
    AutoFilter          bool
    DeleteColumns       bool
    DeleteRows          bool
    EditObjects         bool
    EditScenarios       bool
    FormatCells         bool
    FormatColumns       bool
    FormatRows          bool
    InsertColumns       bool
    InsertHyperlinks    bool
    InsertRows          bool
    Password            string
    PivotTables         bool
    SelectLockedCells   bool
    SelectUnlockedCells bool
    Sort                bool
}

我们直接来看一看源码:

func (f *File) ProtectSheet(sheet string, settings *FormatSheetProtection) error {
	ws, err := f.workSheetReader(sheet)
	if err != nil {
		return err
	}
	if settings == nil {
		settings = &FormatSheetProtection{
			EditObjects:       true,
			EditScenarios:     true,
			SelectLockedCells: true,
		}
	}
	ws.SheetProtection = &xlsxSheetProtection{
		AutoFilter:          settings.AutoFilter,
		DeleteColumns:       settings.DeleteColumns,
		DeleteRows:          settings.DeleteRows,
		FormatCells:         settings.FormatCells,
		FormatColumns:       settings.FormatColumns,
		FormatRows:          settings.FormatRows,
		InsertColumns:       settings.InsertColumns,
		InsertHyperlinks:    settings.InsertHyperlinks,
		InsertRows:          settings.InsertRows,
		Objects:             settings.EditObjects,
		PivotTables:         settings.PivotTables,
		Scenarios:           settings.EditScenarios,
		SelectLockedCells:   settings.SelectLockedCells,
		SelectUnlockedCells: settings.SelectUnlockedCells,
		Sheet:               true,
		Sort:                settings.Sort,
	}
	if settings.Password != "" {
		if settings.AlgorithmName == "" {
			ws.SheetProtection.Password = genSheetPasswd(settings.Password)
			return err
		}
		hashValue, saltValue, err := genISOPasswdHash(settings.Password, settings.AlgorithmName, "", int(sheetProtectionSpinCount))
		if err != nil {
			return err
		}
		ws.SheetProtection.Password = ""
		ws.SheetProtection.AlgorithmName = settings.AlgorithmName
		ws.SheetProtection.SaltValue = saltValue
		ws.SheetProtection.HashValue = hashValue
		ws.SheetProtection.SpinCount = int(sheetProtectionSpinCount)
	}
	return err
}

整个逻辑比较简单:

	ws, err := f.workSheetReader(sheet)
	if err != nil {
		return err
	}

先读取工作表。

	if settings == nil {
		settings = &FormatSheetProtection{
			EditObjects:       true,
			EditScenarios:     true,
			SelectLockedCells: true,
		}
	}
	ws.SheetProtection = &xlsxSheetProtection{
		AutoFilter:          settings.AutoFilter,
		DeleteColumns:       settings.DeleteColumns,
		DeleteRows:          settings.DeleteRows,
		FormatCells:         settings.FormatCells,
		FormatColumns:       settings.FormatColumns,
		FormatRows:          settings.FormatRows,
		InsertColumns:       settings.InsertColumns,
		InsertHyperlinks:    settings.InsertHyperlinks,
		InsertRows:          settings.InsertRows,
		Objects:             settings.EditObjects,
		PivotTables:         settings.PivotTables,
		Scenarios:           settings.EditScenarios,
		SelectLockedCells:   settings.SelectLockedCells,
		SelectUnlockedCells: settings.SelectUnlockedCells,
		Sheet:               true,
		Sort:                settings.Sort,
	}

如果参数settings为空,我们就新建一个 FormatSheetProtection结构体,给EditObjects、EditScenarios、SelectLockedCells这三个参数分配为true。

	ws.SheetProtection = &xlsxSheetProtection{
		AutoFilter:          settings.AutoFilter,
		DeleteColumns:       settings.DeleteColumns,
		DeleteRows:          settings.DeleteRows,
		FormatCells:         settings.FormatCells,
		FormatColumns:       settings.FormatColumns,
		FormatRows:          settings.FormatRows,
		InsertColumns:       settings.InsertColumns,
		InsertHyperlinks:    settings.InsertHyperlinks,
		InsertRows:          settings.InsertRows,
		Objects:             settings.EditObjects,
		PivotTables:         settings.PivotTables,
		Scenarios:           settings.EditScenarios,
		SelectLockedCells:   settings.SelectLockedCells,
		SelectUnlockedCells: settings.SelectUnlockedCells,
		Sheet:               true,
		Sort:                settings.Sort,
	}

然后再新建一个xlsxSheetProtection结构体,用settings内的成员给其赋值。

	if settings.Password != "" {
		if settings.AlgorithmName == "" {
			ws.SheetProtection.Password = genSheetPasswd(settings.Password)
			return err
		}
		hashValue, saltValue, err := genISOPasswdHash(settings.Password, settings.AlgorithmName, "", int(sheetProtectionSpinCount))
		if err != nil {
			return err
		}
		ws.SheetProtection.Password = ""
		ws.SheetProtection.AlgorithmName = settings.AlgorithmName
		ws.SheetProtection.SaltValue = saltValue
		ws.SheetProtection.HashValue = hashValue
		ws.SheetProtection.SpinCount = int(sheetProtectionSpinCount)
	}
	return err

然后再看最后一部分,如果settings的Password不为空,就进入这部分逻辑。 先判断settings.AlgorithmName是否为空,如果为空,就调用genSheetPasswd函数,将Password字段转换成另一种密码形式:

func genSheetPasswd(plaintext string) string {
	var password int64 = 0x0000
	var charPos uint = 1
	for _, v := range plaintext {
		value := int64(v) << charPos
		charPos++
		rotatedBits := value >> 15 // rotated bits beyond bit 15
		value &= 0x7fff            // first 15 bits
		password ^= value | rotatedBits
	}
	password ^= int64(len(plaintext))
	password ^= 0xCE4B
	return strings.ToUpper(strconv.FormatInt(password, 16))
}
		hashValue, saltValue, err := genISOPasswdHash(settings.Password, settings.AlgorithmName, "", int(sheetProtectionSpinCount))
		if err != nil {
			return err
		}

然后使用genISOPasswdHash,通过给定的明文密码、加密散列算法的名称、盐值和自旋次数来实现ISO密码散列算法。

		ws.SheetProtection.Password = ""
		ws.SheetProtection.AlgorithmName = settings.AlgorithmName
		ws.SheetProtection.SaltValue = saltValue
		ws.SheetProtection.HashValue = hashValue
		ws.SheetProtection.SpinCount = int(sheetProtectionSpinCount)

最后将生成的hash值、盐值、哈希算法名、自旋次数等放入结构体。

相关文章

学习编程是顺着互联网的发展潮流,是一件好事。新手如何学习...
IT行业是什么工作做什么?IT行业的工作有:产品策划类、页面...
女生学Java好就业吗?女生适合学Java编程吗?目前有不少女生...
Can’t connect to local MySQL server through socket \'/v...
oracle基本命令 一、登录操作 1.管理员登录 # 管理员登录 ...
一、背景 因为项目中需要通北京网络,所以需要连vpn,但是服...