【原】在Golang程序中支持RS256验证的JWT

前言:

什么是JWT就不做介绍了,本文只介绍如何基于JWT做支持RS256验证的Go程序。

几个有用的URL:

  1. https://godoc.org/github.com/dgrijalva/jwt-go#example-Parse–Hmac
  2. https://jwt.io/
  3. https://github.com/dgrijalva/jwt-go

其实官网提供了很多各种语言的SDK,直接拿来用就好(本文选用的是github.com/dgrijalva/jwt-go),但查询资料途中,大部分都是HMAC验证(作为对称加密的一种,总有种不安的感觉),于是根据自己的实践,编写本文。

准备工作:

此处基于Linux环境下,需要openssl以及Go环境的支持

生成一个私钥证书(做签发用)

# openssl genrsa -out private.key 2048

基于私钥生成一个公钥证书(做验证用)

# openssl rsa -in private.key -pubout > public.key

开始写代码了:(以下代码经过删减与修改,主要是调用github.com/dgrijalva/jwt-go项目)

import (
        ...
        jwt "github.com/dgrijalva/jwt-go"
        ...
)

var (
  publicKey  *rsa.PublicKey
  privateKey *rsa.PrivateKey
)


func init() {
  publicKeyByte, err := ioutil.ReadFile("公钥的路径/public.key")
  if err != nil {
    log.Println(err.Error())
  }
  publicKey, err = jwt.ParseRSAPublicKeyFromPEM(publicKeyByte)

  privateKeyByte, err := ioutil.ReadFile("私钥的路径/private.key")
  if err != nil {
    log.Println(err.Error())
  }
  privateKey, _ = jwt.ParseRSAPrivateKeyFromPEM(privateKeyByte)
}

// createToken 生成一个RS256验证的Token
// Token里面包括的值,可以自己根据情况添加,
func createToken() (tokenStr string, err error) {
  token := jwt.NewWithClaims(jwt.SigningMethodRS256, jwt.MapClaims{
    "iat":      time.Now().Unix(), // Token颁发时间
    "nbf":      time.Now().Unix(), // Token生效时间
    "exp":      time.Now().Add(time.Hour * 24).Unix(), // Token过期时间,目前是24小时
    "iss":      "liwenbin.com", // 颁发者
    "sub":      "AuthToken", // 主题
//  "role":     "guest", // 角色(附加)
  })
  return token.SignedString(privateKey)
}

// getSubFromToken 获取Token的主题(也可以更改获取其他值)
// 参数tokenStr指的是 从客户端传来的待验证Token
// 验证Token过程中,如果Token生成过程中,指定了iat与exp参数值,将会自动根据时间戳进行时间验证
func getSubFromToken(tokenStr string) (sub string, err error) {
  // 基于公钥验证Token合法性
  token, err := jwt.Parse(tokenStr, func(token *jwt.Token) (interface{}, error) {
    // 基于JWT的第一部分中的alg字段值进行一次验证
    if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok {
      return nil, errors.New("验证Token的加密类型错误")
    }
    return publicKey, nil
  })
  if err != nil {
    return
  }

  if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
    return claims["sub"].(string), nil
  }

  return "", errors.New("Token无效或者无对应值")
}

 

这样,就搞定了,注意要把公私钥的路径确定好哈。

生成的Token分为3个部分(均基于BASE64编码,以“.”区分),第一个部分是定义,第二个部分是你生成Token时设置的各项值,第三个是签名。

最后再次感谢项目:github.com/dgrijalva/jwt-go

【原】开发微信小程序中获取unionId失败——附Golang与PHP示例

前言:话说这个在微信小程序获取union_id真是一个大坑,前来记录一下,避免更多人的入坑;同时提供一段可运行的Golang语言的解密代码(官方提供的代码只有C++、NodeJS、PHP、Python – -)。

前期准备:去腾讯开放平台(open.weixin.qq.com)把相关应用绑定在一起,同时需要进行微信认证(300人民币/年)。

坑一:

小程序开发文档误导,如下图:

通过这种途径是无法获取到unionid的。(我的环境是:一个网页应用,一个小程序,已认证通过,已绑定在同一个主体下。通过调试是无法获取unionid。)

坑二:

如图,通过wx.login是无法获取到union_id的。


那么“正确”的做法是什么呢?请按照如下操作:

1、当通过wx.login获取到code后,发送到后台服务器,此时后台服务器通过请求地址“https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code”得到openid以及session_key。

2、在小程序端调用如下接口:

wx.getUserInfo({
    withCredentials: true,
    success: function (res){
    console.log(res)
}})
在变量res中,会包括“encryptedData”,“iv”变量的值(当然也会包括“userInfo”,“signature”等值,那些不在本文讨论范围之内)。
3、将“encryptedData”,“iv”的值发送到后台服务器,此时后台服务器相当于得到这个用户的“session_key”,“encryptedData”,“iv”这三个值(Base64 编码)。
4、进行对称解密,得到unionId等值。参照如图:

继续阅读【原】开发微信小程序中获取unionId失败——附Golang与PHP示例

【原】关于Go-Mirco如何使用Kafka作为消息队列

前言:其实一开始是打算用RabbitMQ的,不过出现点问题(随机性的),由于赶进度没有进一步搞,于是切换到Kafka。之前的那篇文章我隐藏了,有机会再看看。

本文重点不在Kafka等消息队列的对比与介绍,只关心于Go-Micro框架如何集成Kafka;阅读本文前,请确保您的Go-Micro框架可正常使用,其文档可参考:https://micro.mu/docs/install-guide.html


步骤:

一、引用包,你懂得。

import (

    ...

    "github.com/micro/go-micro/broker"
    "github.com/micro/go-plugins/broker/kafka"

    ....

)

以下以建立Service为例:(Function同理)

二、生产者

1.在main函数中:

service := micro.NewService(
    micro.Name("服务名"),
    micro.Version("0.0.2"),
    micro.Metadata(map[string]string{
        "type": "XXXXX",
    }),
    micro.Broker(kafka.NewBroker(func(o *broker.Options) {
        o.Addrs = config.BrokerURLs
    })),
)

if err := broker.Connect(); err != nil {
    log.Fatal(err.Error())
}

注意:config.BrokerURLs 是[]string 类型的,存放Kafka的IP和端口,例如:

var BrokerURLs = []string{
    0: "192.168.0.33:9092",
}

2.发布事件:

broker.Publish("Topic主题", &broker.Message{
    Header: map[string]string{
        "AAA": "BBBBB",
        "CCCCC": "DDDDDD",
    },
    Body: []byte("消息内容"),
})

三、消费者:

与发布者相似,唯独是订阅事件,如下代码:

broker.Subscribe("Topic主题", func(p broker.Publication) error {
    brokerHeader := p.Message().Header
    aaa := brokerHeader["AAA"]
    bbb := string(p.Message().Body)
})
if err != nil {
    log.Fatal(err.Error())
}

即可。