1
jedrek 2019-06-07 00:07:45 +08:00 1
将一次 request - response 当成一次 session 不可以解决吗?想不通为何一定要整个长 session
|
4
jedrek 2019-06-07 14:25:49 +08:00 1
你要做的其实是 http 的身份鉴定和令牌有效期。
大致流程是在登录或注册成功后,服务端签发一个令牌返回给客户端,令牌常见存放在 cookie 中。客户端在下次请求时携带令牌,服务端验证令牌来判别此次请求的有效性,若令牌有效,说明此次请求的用户就是其声明的身份。反之不能证明身份的就不能通过。 其中的核心就是令牌,令牌中包含两部分信息,数据和签名,由服务端负责签发和验证。 举一个最小化的例子. ID 为 10000 的用户登录成功了,服务端对 ID 进行数字签名。服务端将 ID + 签名结果 拼接起来组成字符串令牌放到 cookie 中返回给客户端。客户端下次请求时将令牌带上,服务端校验令牌格式和数字签名的有效性。签名的目的是防止伪造和篡改,比如数据部分被改成 10001,这个在服务端就不能通过验证。目前比较推荐的签名算法是 ed25519。 如果你需要限制令牌的有效期,原理也是一样的,将用户 ID 和截止时间一起签名返回给客户端。下次验证是否已过截止时间就可以了。 为了美观和统一, 可以将数据部分 base64 编码后再和签名拼接,用 . 号分隔。 不嫌烦的话 jwt 可以做到这个事,虽然它定义的标准很糟糕。 若要考虑修改密码后吊销所有令牌,在设计上又稍微复杂一点。 |
5
dt2vba OP @jedrek
非常感谢你的详细的回复,尤其是关于令牌解释的部分,清晰地简述用户认证流程,令人印象深刻。 可能我对我的需求解释地不是很清楚。比如,在同一时刻,用户 A,在浏览器 1、2、3、4 登录在线。此时,用户 A 无法在浏览器 5 成功登录(因为最多允许 4 个)。如果用户 A 在浏览器 3 注销登录,那么用户 A 则可以在浏览器 5 成功登录。 但是对于用户注销登录状态,我不太确定令牌的方式是否可以实现。 |
6
dt2vba OP @jedrek 非常感谢你的帮助,借助 dgrijalva/jwt-go,基本实现了需求。以下是我的解决方案。如果你有空的话,并且愿意提出一些意见,那将是非常期待的。
首先定义一个结构体,存储用户的 token 信息 type TokenManager struct { Lock sync.Mutex //map[user] map[token]user Token map[string]map[string]string } 然后定义一个该结构体的方法,删除已经过期的 token func (manager *TokenManager) DeleteTokenNotValid() { //删除过期的 Token ... return } 定义一个全局的 TokenManager 结构体变量 var TokenStore=TokenManager{} 主函数 func main() { //开启协程,删除已经过期的 token go func() { for true { TokenStore.DeleteTokenNotValid() } }() //业务处理函数 ... } 登录处理函数 func login(c echo.Context) error { //如果请求携带有效的 token,则跳转至首页 ... //如果用户的 token 数量大于等于 4,则禁止登录 ... //创建用户的 token,并保存至 TokenStore.Token[user] ... } 注销处理函数 func logout(c echo.Context) error { //删除请求携带的有效 token ... } |
7
jedrek 2019-06-07 21:51:54 +08:00 1
在上面的身份鉴定的基础上,另外使用一次性口令。
登录或注册成功后,服务端签发身份令牌和生成并保存新口令,然后将身份令牌和口令同时返回客户端,客户端将身份令牌和口令保存。下一次请求时,客户端携带口令和身份令牌,服务端做两个验证,一是请求的口令是否与已存在的匹配,二是身份令牌是否有效。两者都有效才通过。都验证通过后,返回时,生成新的口令返回给客户端(不需要更新身份令牌),服务端和客户端都需要更新保存新口令,下次请求同理。登出时删掉服务端对应的口令和客户端信息即可。 当第五个客户端登录时,服务端检测当前用户已有四个令牌了,拒绝登录。 当用户拷贝 http headers 试图突破限制时,第五个客户端接收了新的口令,原客户端的口令就会失效,所以还是四个客户端 可以使用随机数做口令,因为是一次性的,具有排他性,所以不需要考虑重放攻击。但是有可能和自己已存在的口令冲突,可以用时间戳+随机数做口令,不需要加密或签名 |
8
jedrek 2019-06-07 21:55:52 +08:00 1
我不确定 jwt 是否考虑了客户端拷贝 token 来实现更多客户端同时使用
|
9
jedrek 2019-06-07 22:19:12 +08:00 1
更正:
当第五个客户端登录时,服务端检测当前用户已有四个令牌了,拒绝登录。 当第五个客户端登录时,服务端检测当前用户已有四个口令了,拒绝登录。 令牌 -> 口令 |