精华内容
下载资源
问答
  • JSX 元素类型“Navigator”不具有任何构造签名或调用签名ts(2604) 这个主要是ts和js的写法的区别: js:传统的方式直接在构造函数中使用 constructor(){ this.state = { num1:10 } } 使用ts开发过程中需要...

    JSX 元素类型“Navigator”不具有任何构造签名或调用签名。ts(2604)

    这个主要是ts和js的写法的区别:

    • js:传统的方式直接在构造函数中使用

    constructor(){
        this.state = {
            num1:10
        }
    }

    • 使用ts开发过程中需要先定义一个接口,规范数据类型,通过泛型传入到类中

     

    //定义一个接口规范state的类型  

    export interface State{
        num1:number
    }
    // 默认导出一个Hello类,如果Component<Props,State>里面没有就用object
    export default class Hello extends React.Component<Props,State>{
        constructor(props:Props){
            super(props);
            this.state = {
                num1:10
            }
        }
    }

     

    • 父组件传递参数到子组件

    使用ts开发方式,跟上面的state一样的,只是不管怎么样都要在构造函数中写super

     

    在看上面的问题:
    A必须是个类,或函数, 并且返回JSX Navigator对象,因为jsx需要是个JSX Element

    看看官方文档,很详细

    https://www.tslang.cn/docs/handbook/jsx.html

    直接看【类组件】部分就一目了然了。

     

     

    展开全文
  • 后来翻阅官方文档后发现,除了is是谓词签名外,还有assert断言签名。 官方文档 对于这种东西讲解,最好的方法就是先甩个官方文档。 文档:...

    前言

    • 最初发现有这玩意是在styledcomponents的声明中,很神奇的写了个is。
    • 后来翻阅官方文档后发现,除了is是谓词签名外,还有assert断言签名。

    官方文档

    function yell(str) {
      assert(typeof str === "string");
    
      return str.toUpperCase();
    }
    
    • 我们需要断言str是string ,如果str不是string,那么我们没法用大写的方法。
    • 比如我们要写这样一个函数:
    function yell(str:any) {
        if (typeof str !== "string") {
          throw new TypeError("str should have been a string.");
        }
        return str.toUpperCase();
      }
    
    • 正常来说,我们使用这种方法,ts会自动检测到后面走的str是个string,从而判断为string。
    • 如果我们想动态的判断条件呢?比如这么写:
    function yell(str:any) {
        assert(typeof str === "string");
        return str.toUpperCase();
    }
      
     function assert(condition: any) {
       if (!condition) {
         throw new Error('错误');
       }
     }
    
    • 由于不是一个函数里进行throw的,并且是进行判断,虽然ts没有报错,但是ts定的str是any而不是string。
    • 此时,就需要一种断言参数来进行断言:
    function yell(str:any) {
        assert(typeof str === "string");
        return str.toUpperCase();
    }
      
    function assert(condition: any): asserts condition {
      if (!condition) {
        throw new Error('错误');
      }
    }
    
    • 对参数进行断言签名,此时str会被ts认定为string,而不是上面那个any。
    • 当然,在另一个函数中写throw可能让你觉得是throw帮助了ts。其实帮助ts的是assert,ts不会去检测另一个函数中的判断语句。
    • 比如这么写:
    function yell(str:any) {
        console.log(str)//此时仍是any
        assert(typeof str === "string");
        return str.toUpperCase();
    }
      
      function assert(condition: any): asserts condition {
        return condition
      }
    
    • 我的assert函数并没有throw 别的类型,直接返回,但是由于asserts存在,下方的str依然变为string。
    • 除了asserts断言签名,还有种谓词签名,就是is了。
    • is 和asserts有点像,但不一样。还是刚才例子,我们将asserts换成is:
    function yell(str:any) {
        console.log(str)//此时仍是any
        assert(typeof str === "string");
        return str.toUpperCase();
    }
      
      function assert(condition: any):condition is string {
        return condition
      }
    
    • 会发现str仍然会被判断为any,而不是string。
    • 这是当然的,因为is不是asserts。
    • is是谓词签名,所谓谓词签名,就是在另一个函数里强转参数,让使用其函数的函数可以正确判断类型。
    • 还是刚才例子,我们利用is来让str变成判断为string。
    function yell(str: any) {
    	console.log(str); //此时仍是any
    	if (assert(str)) {
    		return str.toUpperCase();//这里是string
    	}
    	return str.toUpperCase();//这里是any 
    }
    
    function assert(condition: any): condition is string {
    	return condition;
    }
    
    • 可以看见,谓词签名其实就是断言签名的另一种表现形式,当我们断定谓词签名是string,另一个函数是的分支下则被检测为string。如果去了is,则判断仍是any。
    • 从中可以发现,ts不会检测另一个函数的判断语句,如果需要,分发判断,就要在另一个函数里做好参数签名,不管是谓词签名还是断言签名都可以。

    实战使用

    • 上面已经理解了asserts和is的用法,但是如果实战如何使用?
    • 对于asserts,其实上面的例子已经可以实战中使用了,动态断言类型而不用管断言函数内部使用。
    • 但是对于is,就没办法动态断言(当然结合泛型动态断言是另外用法,暂不讨论)。我们需要结合is的特点来分析。
    • is的特点就是解决了函数在另一个函数中调用的参数判断,但是自己的参数判断就是正常的。
    • 基于这个特点,如果有个函数提供给别人使用为a类型,自己使用是b类型,这样就可以做出这种神奇的函数。
    • 那么什么样的函数会有上面一条特性?
    • 这种函数其实很多,几乎80%的复杂函数都会有这种特性!但是99%的人没这么写函数。
    • 我举个例子,首先写个接收各种数字的函数,如果数字等于1,2,3,4,5,6中一个则返回true。
    function pipsAreValid(pips: number) {
    	return (
    		pips === 1 ||
    		pips === 2 ||
    		pips === 3 ||
    		pips === 4 ||
    		pips === 5 ||
    		pips === 6
    	);
    }
    
    • 可以把这个类比成写个函数,然后经过各种逻辑,生成个判断。比如验证规则什么的过滤函数。
    • 当我把这个函数交给另一个函数使用时,我希望另一个函数经过我这个函数的判断,获得正确的类型推断:
    type Dice = 1 | 2 | 3 | 4 | 5 | 6;
    
    function pipsAreValid(pips: number):pips is Dice {
    	return (
    		pips === 1 ||
    		pips === 2 ||
    		pips === 3 ||
    		pips === 4 ||
    		pips === 5 ||
    		pips === 6
    	);
    }
    
    function evalThrow(count: number) {
    	if (pipsAreValid(count)) {
    		console.log(count) //如果不加参数断言,你很可能要在这里断言
        }
    }
    
    • 这个参数断言有个特点,推断的再次判断会导致参数断言失效:
    function evalThrow(count: number) {
    	if (pipsAreValid(count)===true) {
    		console.log(count) //number类型
        }
    }
    
    • 其实这个跟参数断言不关心返回值是一个道理,拿true与它相比,那么就说明计算的是返回值,而不是拿参数进行计算。
    • 但是这样是没问题的:
    type Dice = 1 | 2 | 3 | 4 | 5 | 6;
    
    function pipsAreValid(pips: number):pips is Dice {
    	return (
    		pips === 1 ||
    		pips === 2 ||
    		pips === 3 ||
    		pips === 4 ||
    		pips === 5 ||
    		pips === 6
    	);
    }
    function demo2(pips:number):pips is 8 | 9{
        return(
            pips === 8|| pips ===9
        )
    }
    function evalThrow(count: number) {
    	if (pipsAreValid(count)||demo2(count)) {
    		console.log(count) //1-9
        }
    }
    
    • 事实上虽然参数以冒号加断言语句覆盖了原来写返回值的地方,但是返回值仍然隐藏在签名中,仍会奏效。
    • 比如你不能这么写:
    function evalThrow(count: number) {
    	switch(count){
            case pipsAreValid(count)://类型“boolean”不可与类型“number”进行比较
                return ''
        }
    }
    
    • 但是提供签名却是这样:
    function pipsAreValid(pips: number): pips is Dice
    
    • 我还探究了一番,如果我写个声明文件:

    xxx.d.ts

    declare function newfn(pips: number): pips is Dice;
    
    • 此时我进行调用:
    function evalThrow(count: number) {
    	switch(count){
            case newfn(count)://类型“boolean”不可与类型“number”进行比较
                return ''
        }
    }
    
    • 也就是说,写了谓词签名的函数返回值固定为boolean。

    • 同理,断言签名asserts固定返回值类型为void

    • 这样,一下子签名方面就明朗起来,使用带谓词签名或者断言签名,可以让我们更加优雅的替代as的强转,甚至做一些as也做不到的事情。

    展开全文
  • 概览首先同步下项目概况:上篇文章分享了,路由中间件 - Jaeger 链路追踪(实战篇),文章...这篇文章咱们分享:路由中间件 - 签名验证。为什么使用签名验证?这个就不用多说了吧,主要是为了保证接口安全和识别调用...

    概览

    首先同步下项目概况:

    bccad881ddb13bf5878a2f4823b23fb5.png

    上篇文章分享了,路由中间件 - Jaeger 链路追踪(实战篇),文章反响真是出乎意料, 「Go中国」 公众号也转发了,有很多朋友加我好友交流,直呼我大神,其实我哪是什么大神,只不过在本地实践了而已,对于 Go 语言的使用,我还是个新人,在这里感谢大家的厚爱!

    dd73e3e217090523aa7f03f704591801.png

    这篇文章咱们分享:路由中间件 - 签名验证。

    为什么使用签名验证?

    这个就不用多说了吧,主要是为了保证接口安全和识别调用方身份,基于这两点,咱们一起设计下签名。

    调用方需要申请 App Key 和 App Secret,App Key 用来识别调用方身份,App Secret 用来加密生成签名使用。

    当然生成的签名还需要满足以下几点:

    • 可变性:每次的签名必须是不一样的。
    • 时效性:每次请求的时效性,过期作废。
    • 唯一性:每次的签名是唯一的。
    • 完整性:能够对传入数据进行验证,防止篡改。

    举个例子:

    /api?param_1=xxx&param_2=xxx,其中 param_1 和 param_2 是两个参数。

    如果增加了签名验证,需要再传递几个参数:

    • ak 表示App Key,用来识别调用方身份。
    • ts 表示时间戳,用来验证接口的时效性。
    • sn 表示签名加密串,用来验证数据的完整性,防止数据篡改。

    sn 是通过 App Secret 和 传递的参数 进行加密的。

    最终传递的参数如下:

    /api?param_1=xxx&param_2=xxx&ak=xxx&ts=xxx&sn=xxx

    在这要说一个调试技巧,ts 和 sn 参数每次都手动生成太麻烦了,当传递 debug=1 的时候,会返回 ts 和 sn , 具体看下代码就清楚了。

    这篇文章分享三种实现签名的方式,分别是:MD5 组合加密、AES 对称加密、RSA 非对称加密。

    废话不多说,进入主题。

    MD5 组合

    生成签名

    首先,封装一个 Go 的 MD5 方法:

    func MD5(str string) string {
        s := md5.New()
        s.Write([]byte(str))
        return hex.EncodeToString(s.Sum(nil))
    }

    进行加密:

    appKey     = "demo"
    appSecret  = "xxx"
    encryptStr = "param_1=xxx&param_2=xxx&ak="+appKey+"&ts=xxx"
    
    // 自定义验证规则
    sn = MD5(appSecret + encryptStr + appSecret)

    验证签名

    通过传递参数,再次生成签名,如果将传递的签名与生成的签名进行对比。

    相同,表示签名验证成功。

    不同,表示签名验证失败。

    中间件 - 代码实现

    var AppSecret string
    
    // MD5 组合加密
    func SetUp() gin.HandlerFunc {
    
        return func(c *gin.Context) {
            utilGin := util.Gin{Ctx: c}
    
            sign, err := verifySign(c)
    
            if sign != nil {
                utilGin.Response(-1, "Debug Sign", sign)
                c.Abort()
                return
            }
    
            if err != nil {
                utilGin.Response(-1, err.Error(), sign)
                c.Abort()
                return
            }
    
            c.Next()
        }
    }
    
    // 验证签名
    func verifySign(c *gin.Context) (map[string]string, error) {
        _ = c.Request.ParseForm()
        req   := c.Request.Form
        debug := strings.Join(c.Request.Form["debug"], "")
        ak    := strings.Join(c.Request.Form["ak"], "")
        sn    := strings.Join(c.Request.Form["sn"], "")
        ts    := strings.Join(c.Request.Form["ts"], "")
    
        // 验证来源
        value, ok := config.ApiAuthConfig[ak]
        if ok {
            AppSecret = value["md5"]
        } else {
            return nil, errors.New("ak Error")
        }
    
        if debug == "1" {
            currentUnix := util.GetCurrentUnix()
            req.Set("ts", strconv.FormatInt(currentUnix, 10))
            res := map[string]string{
                "ts": strconv.FormatInt(currentUnix, 10),
                "sn": createSign(req),
            }
            return res, nil
        }
    
        // 验证过期时间
        timestamp := time.Now().Unix()
        exp, _    := strconv.ParseInt(config.AppSignExpiry, 10, 64)
        tsInt, _  := strconv.ParseInt(ts, 10, 64)
        if tsInt > timestamp || timestamp - tsInt >= exp {
            return nil, errors.New("ts Error")
        }
    
        // 验证签名
        if sn == "" || sn != createSign(req) {
            return nil, errors.New("sn Error")
        }
    
        return nil, nil
    }
    
    // 创建签名
    func createSign(params url.Values) string {
        // 自定义 MD5 组合
        return util.MD5(AppSecret + createEncryptStr(params) + AppSecret)
    }
    
    func createEncryptStr(params url.Values) string {
        var key []string
        var str = ""
        for k := range params {
            if k != "sn" && k != "debug" {
                key = append(key, k)
            }
        }
        sort.Strings(key)
        for i := 0; i < len(key); i++ {
            if i == 0 {
                str = fmt.Sprintf("%v=%v", key[i], params.Get(key[i]))
            } else {
                str = str + fmt.Sprintf("&%v=%v", key[i], params.Get(key[i]))
            }
        }
        return str
    }

    AES 对称加密

    在使用前,咱们先了解下什么是对称加密?

    对称加密就是使用同一个密钥即可以加密也可以解密,这种方法称为对称加密。

    常用算法:DES、AES。

    其中 AES 是 DES 的升级版,密钥长度更长,选择更多,也更灵活,安全性更高,速度更快,咱们直接上手 AES 加密。

    优点

    算法公开、计算量小、加密速度快、加密效率高。

    缺点

    发送方和接收方必须商定好密钥,然后使双方都能保存好密钥,密钥管理成为双方的负担。

    应用场景

    相对大一点的数据量或关键数据的加密。

    生成签名

    首先,封装 Go 的 AesEncrypt 加密方法 和 AesDecrypt 解密方法。

    // 加密 aes_128_cbc
    func AesEncrypt (encryptStr string, key []byte, iv string) (string, error) {
        encryptBytes := []byte(encryptStr)
        block, err   := aes.NewCipher(key)
        if err != nil {
            return "", err
        }
    
        blockSize := block.BlockSize()
        encryptBytes = pkcs5Padding(encryptBytes, blockSize)
    
        blockMode := cipher.NewCBCEncrypter(block, []byte(iv))
        encrypted := make([]byte, len(encryptBytes))
        blockMode.CryptBlocks(encrypted, encryptBytes)
        return base64.URLEncoding.EncodeToString(encrypted), nil
    }
    
    // 解密
    func AesDecrypt (decryptStr string, key []byte, iv string) (string, error) {
        decryptBytes, err := base64.URLEncoding.DecodeString(decryptStr)
        if err != nil {
            return "", err
        }
    
        block, err := aes.NewCipher(key)
        if err != nil {
            return "", err
        }
    
        blockMode := cipher.NewCBCDecrypter(block, []byte(iv))
        decrypted := make([]byte, len(decryptBytes))
    
        blockMode.CryptBlocks(decrypted, decryptBytes)
        decrypted = pkcs5UnPadding(decrypted)
        return string(decrypted), nil
    }
    
    func pkcs5Padding (cipherText []byte, blockSize int) []byte {
        padding := blockSize - len(cipherText)%blockSize
        padText := bytes.Repeat([]byte{byte(padding)}, padding)
        return append(cipherText, padText...)
    }
    
    func pkcs5UnPadding (decrypted []byte) []byte {
        length := len(decrypted)
        unPadding := int(decrypted[length-1])
        return decrypted[:(length - unPadding)]
    }

    进行加密:

    appKey     = "demo"
    appSecret  = "xxx"
    encryptStr = "param_1=xxx&param_2=xxx&ak="+appKey+"&ts=xxx"
    
    sn = AesEncrypt(encryptStr, appSecret)

    验证签名

    decryptStr = AesDecrypt(sn, app_secret)

    将加密前的字符串与解密后的字符串做个对比。

    相同,表示签名验证成功。

    不同,表示签名验证失败。

    中间件 - 代码实现

    var AppSecret string
    
    // AES 对称加密
    func SetUp() gin.HandlerFunc {
    
        return func(c *gin.Context) {
            utilGin := util.Gin{Ctx: c}
    
            sign, err := verifySign(c)
    
            if sign != nil {
                utilGin.Response(-1, "Debug Sign", sign)
                c.Abort()
                return
            }
    
            if err != nil {
                utilGin.Response(-1, err.Error(), sign)
                c.Abort()
                return
            }
    
            c.Next()
        }
    }
    
    // 验证签名
    func verifySign(c *gin.Context) (map[string]string, error) {
        _ = c.Request.ParseForm()
        req   := c.Request.Form
        debug := strings.Join(c.Request.Form["debug"], "")
        ak    := strings.Join(c.Request.Form["ak"], "")
        sn    := strings.Join(c.Request.Form["sn"], "")
        ts    := strings.Join(c.Request.Form["ts"], "")
    
        // 验证来源
        value, ok := config.ApiAuthConfig[ak]
        if ok {
            AppSecret = value["aes"]
        } else {
            return nil, errors.New("ak Error")
        }
    
        if debug == "1" {
            currentUnix := util.GetCurrentUnix()
            req.Set("ts", strconv.FormatInt(currentUnix, 10))
    
            sn, err := createSign(req)
            if err != nil {
                return nil, errors.New("sn Exception")
            }
    
            res := map[string]string{
                "ts": strconv.FormatInt(currentUnix, 10),
                "sn": sn,
            }
            return res, nil
        }
    
        // 验证过期时间
        timestamp := time.Now().Unix()
        exp, _    := strconv.ParseInt(config.AppSignExpiry, 10, 64)
        tsInt, _  := strconv.ParseInt(ts, 10, 64)
        if tsInt > timestamp || timestamp - tsInt >= exp {
            return nil, errors.New("ts Error")
        }
    
        // 验证签名
        if sn == "" {
            return nil, errors.New("sn Error")
        }
    
        decryptStr, decryptErr := util.AesDecrypt(sn, []byte(AppSecret), AppSecret)
        if decryptErr != nil {
            return nil, errors.New(decryptErr.Error())
        }
        if decryptStr != createEncryptStr(req) {
            return nil, errors.New("sn Error")
        }
        return nil, nil
    }
    
    // 创建签名
    func createSign(params url.Values) (string, error) {
        return util.AesEncrypt(createEncryptStr(params), []byte(AppSecret), AppSecret)
    }
    
    func createEncryptStr(params url.Values) string {
        var key []string
        var str = ""
        for k := range params {
            if k != "sn" && k != "debug" {
                key = append(key, k)
            }
        }
        sort.Strings(key)
        for i := 0; i < len(key); i++ {
            if i == 0 {
                str = fmt.Sprintf("%v=%v", key[i], params.Get(key[i]))
            } else {
                str = str + fmt.Sprintf("&%v=%v", key[i], params.Get(key[i]))
            }
        }
        return str
    }

    RSA 非对称加密

    和上面一样,在使用前,咱们先了解下什么是非对称加密?

    非对称加密就是需要两个密钥来进行加密和解密,这两个秘钥分别是公钥(public key)和私钥(private key),这种方法称为非对称加密。

    常用算法:RSA。

    优点

    与对称加密相比,安全性更好,加解密需要不同的密钥,公钥和私钥都可进行相互的加解密。

    缺点

    加密和解密花费时间长、速度慢,只适合对少量数据进行加密。

    应用场景

    适合于对安全性要求很高的场景,适合加密少量数据,比如支付数据、登录数据等。

    创建签名

    首先,封装 Go 的 RsaPublicEncrypt 公钥加密方法 和 RsaPrivateDecrypt 解密方法。

    // 公钥加密
    func RsaPublicEncrypt(encryptStr string, path string) (string, error) {
        // 打开文件
        file, err := os.Open(path)
        if err != nil {
            return "", err
        }
        defer file.Close()
    
        // 读取文件内容
        info, _ := file.Stat()
        buf := make([]byte,info.Size())
        file.Read(buf)
    
        // pem 解码
        block, _ := pem.Decode(buf)
    
        // x509 解码
        publicKeyInterface, err := x509.ParsePKIXPublicKey(block.Bytes)
        if err != nil {
            return "", err
        }
    
        // 类型断言
        publicKey := publicKeyInterface.(*rsa.PublicKey)
    
        //对明文进行加密
        encryptedStr, err := rsa.EncryptPKCS1v15(rand.Reader, publicKey, []byte(encryptStr))
        if err != nil {
            return "", err
        }
    
        //返回密文
        return base64.URLEncoding.EncodeToString(encryptedStr), nil
    }
    
    // 私钥解密
    func RsaPrivateDecrypt(decryptStr string, path string) (string, error) {
        // 打开文件
        file, err := os.Open(path)
        if err != nil {
            return "", err
        }
        defer file.Close()
    
        // 获取文件内容
        info, _ := file.Stat()
        buf := make([]byte,info.Size())
        file.Read(buf)
    
        // pem 解码
        block, _ := pem.Decode(buf)
    
        // X509 解码
        privateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
        if err != nil {
            return "", err
        }
        decryptBytes, err := base64.URLEncoding.DecodeString(decryptStr)
    
        //对密文进行解密
        decrypted, _ := rsa.DecryptPKCS1v15(rand.Reader,privateKey,decryptBytes)
    
        //返回明文
        return string(decrypted), nil
    }

    调用方 申请 公钥(public key),然后进行加密:

    appKey     = "demo"
    appSecret  = "公钥"
    encryptStr = "param_1=xxx&param_2=xxx&ak="+appKey+"&ts=xxx"
    
    sn = RsaPublicEncrypt(encryptStr, appSecret)

    验证签名

    decryptStr = RsaPrivateDecrypt(sn, app_secret)

    将加密前的字符串与解密后的字符串做个对比。

    相同,表示签名验证成功。

    不同,表示签名验证失败。

    中间件 - 代码实现

    var AppSecret string
    
    // RSA 非对称加密
    func SetUp() gin.HandlerFunc {
    
        return func(c *gin.Context) {
            utilGin := util.Gin{Ctx: c}
    
            sign, err := verifySign(c)
    
            if sign != nil {
                utilGin.Response(-1, "Debug Sign", sign)
                c.Abort()
                return
            }
    
            if err != nil {
                utilGin.Response(-1, err.Error(), sign)
                c.Abort()
                return
            }
    
            c.Next()
        }
    }
    
    // 验证签名
    func verifySign(c *gin.Context) (map[string]string, error) {
        _ = c.Request.ParseForm()
        req   := c.Request.Form
        debug := strings.Join(c.Request.Form["debug"], "")
        ak    := strings.Join(c.Request.Form["ak"], "")
        sn    := strings.Join(c.Request.Form["sn"], "")
        ts    := strings.Join(c.Request.Form["ts"], "")
    
        // 验证来源
        value, ok := config.ApiAuthConfig[ak]
        if ok {
            AppSecret = value["rsa"]
        } else {
            return nil, errors.New("ak Error")
        }
    
        if debug == "1" {
            currentUnix := util.GetCurrentUnix()
            req.Set("ts", strconv.FormatInt(currentUnix, 10))
    
            sn, err := createSign(req)
            if err != nil {
                return nil, errors.New("sn Exception")
            }
    
            res := map[string]string{
                "ts": strconv.FormatInt(currentUnix, 10),
                "sn": sn,
            }
            return res, nil
        }
    
        // 验证过期时间
        timestamp := time.Now().Unix()
        exp, _    := strconv.ParseInt(config.AppSignExpiry, 10, 64)
        tsInt, _  := strconv.ParseInt(ts, 10, 64)
        if tsInt > timestamp || timestamp - tsInt >= exp {
            return nil, errors.New("ts Error")
        }
    
        // 验证签名
        if sn == "" {
            return nil, errors.New("sn Error")
        }
    
        decryptStr, decryptErr := util.RsaPrivateDecrypt(sn, config.AppRsaPrivateFile)
        if decryptErr != nil {
            return nil, errors.New(decryptErr.Error())
        }
        if decryptStr != createEncryptStr(req) {
            return nil, errors.New("sn Error")
        }
        return nil, nil
    }
    
    // 创建签名
    func createSign(params url.Values) (string, error) {
        return util.RsaPublicEncrypt(createEncryptStr(params), AppSecret)
    }
    
    func createEncryptStr(params url.Values) string {
        var key []string
        var str = ""
        for k := range params {
            if k != "sn" && k != "debug" {
                key = append(key, k)
            }
        }
        sort.Strings(key)
        for i := 0; i < len(key); i++ {
            if i == 0 {
                str = fmt.Sprintf("%v=%v", key[i], params.Get(key[i]))
            } else {
                str = str + fmt.Sprintf("&%v=%v", key[i], params.Get(key[i]))
            }
        }
        return str
    }

    如何调用?

    与其他中间件调用方式一样,根据自己的需求自由选择。

    比如,使用 MD5 组合:

    .Use(sign_md5.SetUp())

    使用 AES 对称加密:

    .Use(sign_aes.SetUp())

    使用 RSA 非对称加密:

    .Use(sign_rsa.SetUp())

    性能测试

    既然 RSA 非对称加密,最安全,那么统一都使用它吧。

    NO!NO!NO!绝对不行!

    为什么我要激动,因为我以前遇到过这个坑呀,都是血泪的教训呀...

    咱们挨个测试下性能:

    MD5

    func Md5Test(c *gin.Context) {
        startTime  := time.Now()
        appSecret  := "IgkibX71IEf382PT"
        encryptStr := "param_1=xxx&param_2=xxx&ak=xxx&ts=1111111111"
        count      := 1000000
        for i := 0; i < count; i++ {
            // 生成签名
            util.MD5(appSecret + encryptStr + appSecret)
    
            // 验证签名
            util.MD5(appSecret + encryptStr + appSecret)
        }
        utilGin := util.Gin{Ctx: c}
        utilGin.Response(1, fmt.Sprintf("%v次 - %v", count, time.Since(startTime)), nil)
    }

    模拟 一百万 次请求,大概执行时长在 1.1s ~ 1.2s 左右。

    AES

    func AesTest(c *gin.Context) {
        startTime  := time.Now()
        appSecret  := "IgkibX71IEf382PT"
        encryptStr := "param_1=xxx&param_2=xxx&ak=xxx&ts=1111111111"
        count      := 1000000
        for i := 0; i < count; i++ {
            // 生成签名
            sn, _ := util.AesEncrypt(encryptStr, []byte(appSecret), appSecret)
    
            // 验证签名
            util.AesDecrypt(sn, []byte(appSecret), appSecret)
        }
        utilGin := util.Gin{Ctx: c}
        utilGin.Response(1, fmt.Sprintf("%v次 - %v", count, time.Since(startTime)), nil)
    }

    模拟 一百万 次请求,大概执行时长在 1.8s ~ 1.9s 左右。

    RSA

    func RsaTest(c *gin.Context) {
        startTime  := time.Now()
        encryptStr := "param_1=xxx&param_2=xxx&ak=xxx&ts=1111111111"
        count      := 500
        for i := 0; i < count; i++ {
            // 生成签名
            sn, _ := util.RsaPublicEncrypt(encryptStr, "rsa/public.pem")
    
            // 验证签名
            util.RsaPrivateDecrypt(sn, "rsa/private.pem")
        }
        utilGin := util.Gin{Ctx: c}
        utilGin.Response(1, fmt.Sprintf("%v次 - %v", count, time.Since(startTime)), nil)
    }

    我不敢模拟 一百万 次请求,还不知道啥时候能搞定呢,咱们模拟 500 次试试。

    模拟 500 次请求,大概执行时长在 1s 左右。

    上面就是我本地的执行效果,大家可以质疑我的电脑性能差,封装的方法有问题...

    你们也可以试试,看看性能差距是不是这么大。

    PHP 与 Go 加密方法如何互通?

    我是写 PHP 的,生成签名的方法用 PHP 能实现吗?

    肯定能呀!

    我用 PHP 也实现了上面的 3 中方法,可能会有一些小调整,总体问题不大,相关 Demo 已上传到 github:

    https://github.com/xinliangnote/Encrypt

    好了,就到这了。

    源码地址

    https://github.com/xinliangnote/go-gin-api

    go-gin-api 系列文章

    • 1. 使用 go modules 初始化项目
    • 2. 规划项目目录和参数验证
    • 3. 路由中间件 - 日志记录
    • 4. 路由中间件 - 捕获异常
    • 5. 路由中间件 - Jaeger 链路追踪(理论篇)
    • 6. 路由中间件 - Jaeger 链路追踪(实战篇)
    展开全文
  • fp-ts-std 缺少的伪标准库。 同时支持Web和Node。 文档: 对于DOM绑定,请查看 。... 不正确的功能已使用IO和Task类型进行了适当的签名 着眼于fp-ts规范,倾向于Haskell命名约定和习惯用语 此外,fp-ts-st
  • openssl ts

    2018-04-25 09:16:24
    用途:时间戳处理工具(客户端/服务器模式)。...a.TSA客户端对一个数据文件计算一个哈希值,并将它发送到TSAb.TSA将当时时间衣服到所接受到的hash值后面,并签名,发送时间戳标记到客户端。在创建这个标记是根据
    用途:
    时间戳处理工具(客户端/服务器模式)。该命令是一个基本的时间戳权威认证机构(TSA)的客户端和服务器端应用程序(RFC3161协议)。TSA是一个PKI调度工具中的不部分,它的角色是提供一个证明:证明某个数据在一段时间内存在。
    a.TSA客户端对一个数据文件计算一个哈希值,并将它发送到TSA
    b.TSA将当时时间衣服到所接受到的hash值后面,并签名,发送时间戳标记到客户端。在创建这个标记是根据原始数据来产生的。
    c.TSA客户daunting接受到的时间戳标记并验证签名。如果检测到标记包含相同的哈希值再次发送到TSA.
    语法:
    openssl ts
    [-query] [-rand files] [-config configfile] [-data file_to_hash] [-digest digest_bytes]
    [-md2/-md4/-md5/-sha/-sha1/-mdc2/-ripemd160] [-policy object_id] [-no_nonce]
    [-cert] [-in request.tsq] [-out request.tsq] [-text] [-reply] [-config configure]
    [-section tsa_section] [-queryfile request.req] [-passin password_src]
    [-signer tsa_cert.pem] [-inkey private.pem] [-chain certs_file.pem] [-policy object_id]
    [-in response.tsr] [-token_in] [-out response.tsr] [-token_out] [-text] [-engine id]
    [-verify] [-data file_to_hash] [-digest digest_bytes] [-queryfile request.tsq] [-in response.tsr]
    [-token_in][-CApath trusted_cert_path] [-CAfile trusted_certs.pem] [-untrusted cert_file.pem]
    参数说明:
    时间戳请求产生的命令:
    -query:根据下面的选项来创建和打印一个时间戳请求文件。
    -rand files:随机数种子文件。可以包含多个文件。
    -config configfile:要用的配置文件,这个选项可以不顾OPENSSL_CONF的环境变量。仅仅只有OID字节使用。
    -data file_to_hash:创建时间戳请求的数据文件。如果-data命令和-digest命令没有指定,则是标准输入。
    -digest digest_bytes:有可能的话明确的指定消息的印记。印记必须是十六进制格式的数据,两字符组成一字节,字节数可以用:号分开(例如e.g. 1A:F6:01:... 或者1AF601...)。字节数必须匹配用的摘要算法的长度值。
    -md2/-md4/-md5/-sha/-sha1/-mdc2/-ripemd160:计算摘要值的摘要算法。默认的是SHA1。
    -policy object_id:客户端希望TSA创建时间戳标记用的id值。如果没有指定,则使用默认的id值。
    -no_nonce:如果该选项被设置,则没有指定临时的值。否则一个临时的64字节的随机数将会包含到请求文件中。推荐时用临时的值以防止重放攻击。
    -cert:TSA期望包含签名证书到响应中。
    -in request.tsq:该选项预先指定了一个输出目录,以打印DER格式的时间戳请求。如果以可读的格式来检查请求文件的上下文将会非常有用。
    -out request.tsq:请求文件的输出文件。默认是标准输出。
    -text:指定输出的格式是human-readable格式来代替DER格式。
    一个时间戳的响应(TimeStampResp)如果包含了一个响应状态值和自身的时间戳标记(ContentInfo)。
    产生时间戳响应的命令如下:
    -reply:该命令根据请求文件创建一个时间戳响应或时间戳标记,并用human-readable的格式打印出时间戳响应或标记。如果token_out没有指定,则输出是一个时间戳响应(TimeStampResp)否则就是时间戳标记(ContentInfo)。
    -config configure:要用的配置文件,这个选项可以不顾OPENSSL_CONF的环境变量。仅仅只有OID字节使用。
    -section tsa_section:配置文件的字段名字,包含了响应产生器。如果没有指定,则使用默认的TSA字段。
    -queryfile request.req:DER格式的时间戳请求文件。
    -passin password_src:TSA私钥的保护口令。
    -signer tsa_cert.pem:PEM格式的TSA的签名证书。TSA的签名证书必须有正确的扩展密钥用法:时间戳。扩展的密钥用法必须是临界的,否则要遭到拒绝。
    -inkey private.pem:PEM格式的TSA的签名证书私钥。
    -chain certs_file.pem:PEM格式的证书链,它包含附加的响应的签名者的证书。
    -policy object_id:默认的policy用于响应文件,除非客户端明确的要求需要详细的TSA policy。
    -in response.tsr:预先指定的DER格式的时间戳响应或时间戳标记(-token_in指定)的保存目录。这个选项不需要一个请求文件,如果以可读的格式来检查DER格式的时间戳响应或时间戳标记将会非常有用。如果输入的是一个标记,输出是一个时间戳响应,则一个默认的‘granted’状态值将添加到标记中。
    -token_in:这个标记表示可以和-in一起使用,来表示输入的是一个时间戳标记,而默认的是一个时间戳响应。
    -out response.tsr:响应的保存位置。格式和上下文要依据其它选项(-text、-token_out)。默认的是标准输出。
    -token_out:输出是一个时间戳标记,默认输出则是时间戳响应。
    -text:如果指定输出的格式是human-readable格式来代替DER格式。
    -engine id:硬件引擎。
    验证时间戳响应文件的命令如下:
    -verify:该命令验证一个时间戳响应或时间戳标记的有效性,并和时间戳请求文件向匹配。该命令不使用配置文件。
    -data file_to_hash:请求或标记必须验证,不管file_to_hash。该文件是消息摘要算法对标记的摘要值。它不能和-digest、-queryfile两个命令一起使用。
    -digest digest_bytes:请求或标记必须验证,不管digest_bytes指定的消息摘要值。字节数必须匹配标记中的消息摘要算法的长度值。它不能和-data、-queryfile两个命令一起使用。
    -queryfile request.tsq:DER格式的时间戳请求文件。它不能和-digest、-data两个命令一起使用。
    -in response.tsr:需要验证的DER格式的时间戳响应文件。
    -token_in:这个标记表示可以和-in一起使用,来表示输入的是一个DER格式的时间戳标记,而默认的是一个时间戳响应。
    -CApath trusted_cert_path:包含客户端可信任证书的路径。这个选项和-Cafile必须被指定。
    -CAfile trusted_certs.pem:PEM格式的包含可信任的自签名的CA证书。这个选项和- CApath必须被指定。
    -untrusted cert_file.pem:PEM格式的不信任的证书集合。它有可能被用于被创建TSA签名者的证书。这个文件必须包含TSA签名证书和所有的中间CA证书。
    配置文件选项:
    query和reply命令需要一个配置文件,该配置文件定义在OPENSSL_CONF环境变量中。query命令j仅仅使用了OID名字段。reply命令需要配置文件做很多操作。配置文件的语法描述如下:
    B<tsa> section, B<default_tsa>:它是一个主section,指定了其他字段的名字,包含了reply命令的所有操作。
    B<oid_file>:看L<ca(1)|ca(1)>的描述。
    B<oid_section>:看L<ca(1)|ca(1)>的描述。
    B<RANDFILE>:看L<ca(1)|ca(1)>的描述。
    B<serial>:包含了最后时间戳响应的16进制的序列号。这个值是从1按顺序递增的。如果这个文件不存在,则时间戳响应则会创建一个新文件,使序列号的值为1。
    B<signer_cert>:PEM格式的TSA签名证书。和-signer命令的意思一样。
    B<certs>:包含了一系列的PEM格式的证书,它们要包含到响应文件中。和-chain命令的意思一样。
    B<signer_key>:PEM格式的TSA私钥文件。和-inkey命令的意思一样。
    B<default_policy>:当请求文件没有授权任何policy的时候,则使用它。和-policy命令的意思一样。
    B<other_policies>:policy的列表,用逗号分开。当请求文件明确的指定其中的一个时,才能够使用。
    B<digests>:TSA能够接受的消息摘要算法集合。至少指定一种。
    B<accuracy>:TSA时间源的精确度:秒、毫秒、微妙。例如:secs:1, millisecs:500, microsecs:100。任何组件假定这个选项为0。
    B<clock_precision_digits>:指定以秒为单位的最大值。这个选项包含时间域。后面的零必须从这个时间移除,它有可能比数字稍微小一点,或者一直都没有。仅仅在UNIX平台下支持。最大值是6,默认为0。
    B<ordering>:如果这个选项设置为yes,则由TSA产生的内容有序。
    B<tsa_name>:如果这个选项设置为yes,TSA的颁发者的名字必须包含到响应的name域中。默认为no。
    B<ess_cert_id_chain>:由TSA创建的签名数据对象包含签名证书的标记。如果这个选项被设置为yes,B<certs> 变量或 B<-chain> 选项将被指定,证书链中的标记值将会包含到SigningCertificate的签名属性中。如果这个选项设置为no,仅仅包含签名证书标记。默认为no。
    展开全文
  • 类列表签名 该规则捕获使用classList情况无法在我们支持的所有浏览器中使用的情况。 强制类型 TSLint规则,该规则验证类为在其setter内部使用强制的输入声明了相应的ngAcceptInputType_*静态字段。 还处理继承的类...
  • ts函数定义方式

    2021-03-17 20:06:20
    let fn1:{():number}=function():number{return 1}//使用带有调用签名的对象字面量来定义函数 let fn2:()=>number=function():number{return 1}
  • PDF电子签名分析

    千次阅读 2017-03-23 11:25:23
    from http://blog.csdn.net/arlaichin/article/details/40351543 PDF文档结构分析可参见:...下面仅对PDF的电子签名进行分析 ...PDF的签名标准是 PAdES,ETSI TS 102 778. 签名后的PDF文档格式:
  • 用了这么久的Typescript我都没有察觉……直到今天引用二维码组件qrcode.react时报了个元素类型“QrCode”不具有任何构造签名或调用签名的错,原来只是缺少一个ts声明文件…… 本来只是这个小问题,让我以为它不能用...
  • hindley-milner-ts-源码

    2021-03-28 04:47:36
    辛德利·米尔纳茨 使用Hindley Milner的类型签名语法生成Typescript类型声明。
  • 这段时间,一直在尝试ionic2,打重新拾起ionic的过程中发现很多流程已经和之前的流程存在很大的差异,在签名时遇到了错误,集体错误如下: 根据网上查找资料,说关闭防火墙即可,经实验不可以。 执行的命令 ...
  • Swagger的TypeScript ...作为私有服务对象成员的Request和Response (不污染接口签名) 仅重写函数,不重写整个文件 可选值支持 私人服务方式 这个怎么运作? 在命令行上运行 ts2swagger 然后该工具将 找到所有

空空如也

空空如也

1 2 3 4 5 ... 7
收藏数 126
精华内容 50
关键字:

ts签名