golang jwt 解析过期信息

golang 下解析 jwt 信息,基本上都用的这个包. https://github.com/dgrijalva/jwt-go

官方的 GitHub 案例,也提供了简单的解析方法:

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

func ParseToken(token string) (*Claims, error) {
  tokenClaims, err := jwt.ParseWithClaims(token, &Claims{}, func(token *jwt.Token) (interface{}, error) {
    return jwtSecret, nil
  })

  if tokenClaims != nil {
    if claims, ok := tokenClaims.Claims.(*Claims); ok && tokenClaims.Valid {
      return claims, nil
    }
  }

  return nil, err
}

ParseWithClaims方 法里会对token做验证,只有验证通过的才会进行后续的逻辑。

但对于已经过期的信息,验证显然是不能通过的,使用这个方法会抛出 get claim is nil 的错误。这时候应该使用 ParseUnverified 方法 跳过验证.

func GetUsernameFromToken(cookie string) (string, error) {
  token, _, err := new(jwt.Parser).ParseUnverified(cookie, jwt.MapClaims{})
  if err != nil {
    return "", err
  }

  if claims, ok := token.Claims.(jwt.MapClaims); ok {
    jwtToken := KeluJwtToken{}
    tmp, err := json.Marshal(claims)
    if err != nil {
      return "", err
    }

    err = json.Unmarshal(tmp, &jwtToken)
    if err != nil {
      return "", err
    }
    return jwtToken.Username, nil
  }

  return "", errors.New("cannot parase cookie")
}

参考资料


golang 解析 map[string]interface{} 和 json 到 struct

从 json 转成 strcut 可以算是go开发中的常事了。这里记录两个窍门。

通过 json 自动生成 struct 声明

一般来说如果只有三四个字段,还是比较轻松的,如果有50个以上的字段,光是定义结构体,都要累死一拨人了~

这时候这个网站 https://app.quicktype.io/ 真是个神器一般的存在,可以根据 json 定义go struct。

image-20201215165155521

猜测json的内部数据类型

进行开发,解析json数据时,我先写了以下的测试代码,用于判断数据的格式。不得不说,强类型的语言就是有这种不方便。

同时要注意下面几个事项:

  • 解析出来的 int 类型会变成 float64 类型
  • 要注意和前端的交互,某些情形下前端解析 int64 会有精度问题,如果涉及前端的交互,尽量避免使用int64,或先转成string再与前端交互。
func GuessType(k string, v interface{})  {
  switch value := v.(type) {
  case nil:
    fmt.Println(k, "is nil", "null")
  case string:
    fmt.Println(k, "is string", value)
  case int:
    fmt.Println(k, "is int", value)
  case float64:
    fmt.Println(k, "is float64", value)
  case []interface{}:
    fmt.Println(k, "is an array:")
    for i, u := range value {
      fmt.Println(i, u)
    }
  case map[string]interface{}:
    fmt.Println(k, "is an map:")
  default:
    fmt.Println(k, "is unknown type", fmt.Sprintf("%T", v))
  }

}

func GuessTypeFromMapStringInterface(m map[string]interface{}){
  for k, v := range m {
    GuessType(k,v)
  }
}

将 map[string]interface{} 和 json 转成struct

由上一个步骤接着往下走,使用 encoding/json 库中的格式化命令,先将 json 格式化,再反格式化成 struct:

import "encoding/json"

  tmpJson, err := json.Marshal(data)
  if err != nil {
    return nil, err
  }
  create := KeluTest{}
  json.Unmarshal(tmpJson, &create)

参考资料


golang mgo 库的简单使用备忘

最近 golang 开发用到 mongodb,由于一些原因,我使用了 mgo 包进行开发。这篇文章记录一下 mgo 包的用法,一个比较浅的记录,做个备忘。

目前 mgo 已经不维护了,类似的包还有 官方七牛 (七牛的包基于mongo官方)的,都不错,可以试试。

mgo 的使用例子推荐这个网站——learningprogramming.net,写的挺好。

安装

go get gopkg.in/mgo.v2

使用

连接服务器

var mgoSession *mgo.Session
var mgoDatabase *mgo.Database

func GetMgo() *mgo.Session {
	return mgoSession
}

func GetDataBase() *mgo.Database {
	sessionCopy := mgoSession.Copy()
	mgoDatabase = sessionCopy.DB(setting.Config.Mongo.Database)
	return mgoDatabase
}

type Config struct {
	URL        string
	Database   string
	Collection string
	ReplicaSet string
}

func Setup(){
  mgoSession, err := mgo.Dial("username:password@10.19.0.57:7018,10.19.0.58:7018/dbname?replicaSet=replsetname")
  if err != nil {
    logrus.Error(err)
  }

  mgoSession.SetMode(mgo.Monotonic, true)
  mgo.SetDebug(true)
}

// 创建索引
func EnsureIndex(col string,keyArray []string) {
	mongoCol := GetDataBase().C(col)

	for _, key := range keyArray {
		err := mongoCol.EnsureIndex(
			mgo.Index{
				Key: []string{key},
				Background: true,
			})

		if err != nil {
			logrus.Errorln("create index error: ", err)
		}
	}
}

切换db

db := session.DB("test")

切换collection

c := db.C("users")

这样就能对mongo进行操作啦。

查找

查询可以用find,也可以用聚合管道(Aggregation Pipeline)。

find 就是普通的查询,聚合管道可以对集合中的文档进行变换和组合。

聚合管道以一个集合中的所有文档作为开始,然后这些文档从一个操作节点流向下一个节点 ,每个操作节点对文档做相应的操作。这些操作可能会创建新的文档或者过滤掉一些不符合条件的文档,在管道中可以对文档进行重复操作。

find

m := bson.M{
        "CurTimestamp": bson.M{
            "$gte": start,
            "$lte": end,
        },
        "Account":    account,
        "ToNodeType": "cloud",
    }

c.Find(m).Count()

pipeline

import "gopkg.in/mgo.v2/bson"

  match := bson.M{}

  if len(receivedData.Method) > 0 {
    matchIn := []interface{}{}
    for _, v := range receivedData.Method {
      matchIn = append(matchIn, v)
    }
    match["RequestMethod"] = bson.M{"$in":matchIn}
  }
  
  match["time"] = bson.M{"$lt": endedAt,"$gt": startedAt}
  

  pipeline := []bson.M{
    {"$match": match},
    {"$sort": bson.M{"time": -1}},
    {"$limit": limit},
    {"$skip": skip},
  }
  
  var xxxList []xxxItem
  c.Pipe(pipeline).All(&xxxList)

mapreduce

目前还没有使用mapreduce,以下例子来自《谈谈一些关于mgo的用法》

    m := new(mgo.MapReduce)
    m.Map = `function() { var date = new Date();
    date.setTime(this.CurTimestamp / 1000);
    var hour = date.getHours();
    if((hour >= 6) && (hour <= 11)) {
        result.morning++;
    }else if((hour >= 12) && (hour <= 18)){
        result.afternoon ++;
    }else if((hour >= 19) && (hour <= 23)) {
        result.night ++;
    }else{
        result.am ++;
    }
    emit(this.Account, {});}`
    m.Reduce = `function() {return result;}`
    m.Scope = bson.M{
        "result": bson.M{
            "morning":   0,
            "afternoon": 0,
            "night":     0,
            "am":        0,
        },
    }
    var value []timeResult
    c.Find().MapReduce(m, &value)

update

import "gopkg.in/mgo.v2/bson"

err = c.UpdateId(bson.ObjectIdHex(xxx.ID.Hex()), bson.M{"$set": bson.M{"ClientUsername": username,}})

err := c.Update(bson.M{"wsid": wsid}, bson.M{"$set": bson.M{"endtime": time.Now(),}})

修改字段的值($set)

c.Update(bson.M{"_id": bson.ObjectIdHex("5204af979955496907000001")}, bson.M{"$set": bson.M{ "name": "Jimmy Gu", "age": 34, }})

字段增加值inc($inc)

c.Update(bson.M{"_id": bson.ObjectIdHex("5204af979955496907000001")}, bson.M{"$inc": bson.M{ "age": -1, }})

从数组中增加一个元素push($push)

c.Update(bson.M{"_id": bson.ObjectIdHex("5204af979955496907000001")}, bson.M{"$push": bson.M{ "interests": "Golang", }})

从数组中删除一个元素pull($pull)

c.Update(bson.M{"_id": bson.ObjectIdHex("5204af979955496907000001")}, bson.M{"$pull": bson.M{ "interests": "Golang", }})

create

  create := XxxStruct{}
  create.Time = time.Now()
  create.Wsid = wsid
  c.Insert(create)

remove

c.Remove(bson.M{"name": "Jimmy Kuu"})

条件匹配

=($eq)

c.Find(bson.M{"name": "Jimmy Kuu"}).All(&users)

!=($ne)

c.Find(bson.M{"name": bson.M{"$ne": "Jimmy Kuu"}}).All(&users)

>($gt)

c.Find(bson.M{"age": bson.M{"$gt": 32}}).All(&users)

<($lt)

c.Find(bson.M{"age": bson.M{"$lt": 32}}).All(&users)

>=($gte)

c.Find(bson.M{"age": bson.M{"$gte": 33}}).All(&users)

<=($lte)

c.Find(bson.M{"age": bson.M{"$lte": 31}}).All(&users)

in($in)

c.Find(bson.M{"name": bson.M{"$in": []string{"Jimmy Kuu", "Tracy Yu"}}}).All(&users)

$nin 不在集合内

iter = c.Find(bson.M{"city": bson.M{"$nin": []string{"Shanghai", "Hangzhou"}}}).Iter()

$exists 是否包含键

iter = c.Find(bson.M{"city": bson.M{"$exists": true}}).Iter()

键值为null(键存在,键值为null)

iter = c.Find(bson.M{"city": bson.M{"$in": []interface{}{nil}, "$exists": true}}).Iter()

and($and)

c.Find(bson.M{"name": "Jimmy Kuu", "age": 33}).All(&users)

or($or)

c.Find(bson.M{"$or": []bson.M{bson.M{"name": "Jimmy Kuu"}, bson.M{"age": 31}}}).All(&users)

结果操作

排序 Sort

//按age升序,如果要降序Sort("-age")
iter = c.Find(bson.M{"age": bson.M{"$gte": 33}}).Sort("age").Iter()

限定结果数量 Limit

//使用Limit限定只去5条记录
iter = c.Find(bson.M{"age": bson.M{"$gte": 20}}).Sort("age").Limit(5).Iter()

跳过指定数量的记录 Skip

//跳过两条记录,取接下来的5条记录
iter = c.Find(bson.M{"age": bson.M{"$gte": 20}}).Sort("age").Skip(2).Limit(5).Iter()

计算记录的条数 Count

recordsCount, err := c.Find(bson.M{"age": bson.M{"$gte": 20}}).Count()

参考资料


windows 通过命令行设置应用程序的窗口位置

目前还没有使用,我总觉得以后会用到的,先转过来了。

AutoHotKey非常适合窗口定位任务。

这是一个示例脚本。将其命名为 notepad.ahk ,然后从命令行运行它或双击它。

Run, notepad.exe
WinWait, ahk_class Notepad
WinActivate
WinMove A,, 10, 10, A_ScreenWidth-20, A_ScreenHeight-20

它将启动一个应用程序(记事本),然后调整窗口大小,使其在窗口中居,所有边都有10像素边框。

参考资料


excel vlookup index match 等常用表达式备忘

计算当月天数 DAY

在任意单元格输入公式:

=DAY(DATE(YEAR(TODAY()),MONTH(TODAY())+1,1)-1)

可以计算计算机系统时间为准的当月天数。

计算每月天数 EOMONTH

A1单元格是一个任意日期,B1单元格输入公式:

=DAY(EOMONTH(A1,0))

即可求出2月份天数。

excel每月天数

计算天数差 DATEDIF

在A1单元格输入前面的日期,比如2019-10-10

在A2单元格输入后面的日期,如2020-6-7

在A3单元格,输入公式

=DATEDIF(A1,A2,"d")

即可得出两者的天数差 “241”。

vlookup

Excel 要掌握 “4+1”:

  • 4个核心函数:vlookup、if、sum、sumif
  • 1个核心功能:数据透视表

抱歉,我还不会用= =,占个坑,等我以后需要了再研究研究。

preview

index

index 和 vlookup函数很像,但它不需要源数据的严格对齐,也不需要查找值必须在首列,更为灵活一些。

留个空,最近都没用到,先不看了,囧。

参考链接

SUMIF

参考链接

简单求和

=SUMIF(C3:D7,"女",D3:D7)

img

统计前3名成绩之和

=SUMIF(D3:D7,">"&LARGE(D3:D7,4))

函数LARGE(RANGE,K)的作用是返回数组中地K个最大值。如LARGE(D3:D7,4)返回的值为90,现在就不难理解公式:=SUMIF(D3:D7,”>”&LARGE(D3:D7,4))。

img

模糊求和

=SUMIF(B3:B7,"李*",D3:D7)

img

参考资料


安装桌面版的 Linux 备忘

  1. driver for wifi, 如果是笔记本的话,一般都要装。

  2. git 配置代理,国内经常拉不了github, 影响后续软件安装。

  3. 升级内核,免得后面出什么幺蛾子。

  4. 安装zsh、docker等重型工具。

  5. 安装synergy,从笔记本直接连接过去,不需要额外的键盘和鼠标。

    1. 系统启动自动运行
    2. 设置默认用户自动登录。settings->Users->automatic login
  6. 增加sudoer,免密。

  7. 安装wireguard,组成集群。

  8. 修改terminal界面

    1. terminal->edit->preferences
    2. 修改theme为dark
    3. 修改profile的默认command,打开窗口后运行 sudo su -
    4. 修改系统快捷键,我习惯用 Win + 空格 快速启动命令行。
    5. 取消关闭窗口的警告。
    6. 透明化
  9. 取消锁屏,settings->Power,

  10. 设置桌面壁纸,settings->Background

  11. 安装拼音

  12. 安装安装代理, chrome, typora, telegram, smartgit(有时需要单独源码安装git) ,wps, 等工具。

  13. 修改 Chrome 默认的标准字体为 liberation Mono

    image-20210804170138210

  14. 始终显示dock

  15. 安装idea (我现在不用idea了,用vscode online)

  16. gnome-tweak-tool 桌面配置

    1. alt + F2,运行命令gnome-tweak-tool,如果没有这个命令则安装。apt-get install gnome-tweak-tool.
    2. 恢复最大化最小化图标,或者记住快捷键 Win + ↑ Win + ↓

    3. alternatetab/
  17. 制作一部分桌面图标,参考文章——《debian 安装 IntelliJ Idea ,并增加应用图标》

    1. 参考系统已有的图标,gnome的图标路径一般在下面三个地方:
      1. /usr/share/applications
      2. /usr/local/share/applications
      3. ~/.local/share/applications

linux 配置 privoxy 实现系统全局/自动代理

用 Privoxy 可以将 socks5 代理转成 http 代理,并根据要访问的地址使用不同的代理,在桌面环境下是个非常有用的软件。对于开发人员来说更为重要,比如根据不同的域名指向不同的开发环境和测试环境。这篇记录使用的过程。

1. 安装并启动

apt-get install privoxy
systemctl enable privoxy
systemctl restart privoxy

2. 配置 privoxy

privoxy 的配置目录在文件夹 /etc/privoxy下。

进入目录 /etc/privoxy,可以看到目录结构大致为:

  • config 配置文件,这个文件很长。。
  • *.action 代理规则文件
  • *.filter 过滤规则文件

privoxy 的 filter (过滤)功能,可以用来实现广告拦截。不过这里只希望实现自动代理,在配置文件中把下面的 actionsfile 和 filterfile 内容注释掉:

actionsfile match-all.action # Actions that are applied to all sites and maybe overruled later on.
actionsfile default.action   # Main actions file
actionsfile user.action      # User customizations

filterfile default.filter
filterfile user.filter      # User customizations

然后加上自己的配置文件

actionsfile kelu.action

image-20201130180636552

当然,如果我们想全部代理也可以:

{+forward-override{forward-socks5 127.0.0.1:31080 .}}
.
/

可以指定转换后的 HTTP 代理地址,也可以使用默认端口 8118

listen-address  127.0.0.1:8118
listen-address  [::1]:8118

另一种写法的配置文件示例:


default	= +forward-override{forward .} 
pac 	= +forward-override{forward 127.0.0.1:8080}
 
{default}
/
 
{pac}
.baidu.com
.qq.com

3. 自动生成配置文件

使用 gfwlist2privoxy 自动生成配置文件。

apt-get intall python-pip
pip install gfwlist2privoxy

wget https://raw.githubusercontent.com/gfwlist/gfwlist/master/gfwlist.txt
gfwlist2privoxy -i gfwlist.txt -f /etc/privoxy/gfwlist.action -p 127.0.0.1:31080 -t socks5

运行后便生成一个 gfwlist.action,在 privoxy 里将其配置进去即可。

actionsfile gfwlist.action

说明:

-i/--input 输入,本地 gfwlist 文件或文件 URL。这里使用上面的 gfwlist
-f/ --file 输出,即生成的 action 文件的目录。这里输出到 /etc/privoxy/gfwlist.action
-p/ --proxy 代理地址,生成后可以修改。这里是 127.0.0.1:1081
-t/ --type 代理类型,生成后也可以修改。这里是 socks5
--user-rule 用户自定义规则文件,这个文件中的规则会被追加到 gfwlist 生成的规则后面