商城增加了实时性较强的竞价模块,需要通过 websocket 或类似技术来实现双向通信。后端是 spring boot 开发的,因为 react native 对 stomp 的支持存在些问题,就选择用 golang ( 顺便温习一下久违的golang ) 来写一个基于 websocket 的竞价服务。

暴君坑:jwt 复用

由于登录端点在 spring boot 侧,jwt ( 基于 jsonwebtoken) 也是该侧生成。所以 golang 使用 dgrijalva/jwt-go 实现 jwt 的解析。
spring boot 侧生成和解析 jwt 代码:

因为 go 侧未使用 authorities,所以请无视。
go 侧解析 jwt 的代码:

习惯性的用了跟 spring boot 侧一致的SECRET。但是一跑,就报 "signature is invalid"。在 jwt-go 的 issue 272 中找到一些线索:用 base64.URLEncoding.DecodeString(key) 代替 []byte(key)。上面的代码改写后:

跑起来后就报 "illegal base64 data at input byte 2"。显然不接受“空格”字符。
但是受该 issue 启发,去查看 java 源码,signWith 的实现:

TextCodec.BASE64.decode 的实现:

theConverter.parseBase64Binary 的实现:

_parseBase64Binary 实现:

由decodeMap 的定义:

结合起来看,是把 "A-Z", "a-z", "0-9", "+/=" 之外的其它字符都过滤掉了。问题也就迎刃而解了,把 SECRET 中的空格去掉:

如果 jwt 不跨语言和库的话,是不存在这个坑的。但是从安全角度看,spring boot 侧的方案摒弃了一些特殊字符如"!", "@"等,降低了安全性和暴力破解的成本。

主宰坑:redis 复用

spring boot 侧登录成功后,会在 redis 中保存 用户信息:

go 基于 gomodule/redigo 访问 redis,主要是 token 换取用户信息:

redigo 侧一直无法读取到 token 对应的值。通过 redis 图形客户端,显示 key 前面有几个乱码符号。通过 redis-cli 的 KEYS 命令则显示 key 之前存在 "\\xac\\xed\\x00\\x05t" 等值。这是因为 spring boot 侧的 RedisTemplate 默认使用 JdkSerializationRedisSerializer 序列化 Key 和 Value。为了修正该问题,需要自定义 RedisTemplate 的序列化类:

把 key,value,HashKey 和 HashValue 的序列化类都指定为 StringRedisSerializer。在使用该 RedisTemplate 处用 @Autowired 注解代替 @Resource 即可。
Java是一门开放性的语言,但 RedisTemplate 的默认序列化行为有失妥当,忽略了其它语言工具的适用性和便利性,差评。