在 Go 的refresh_token机制中,device_id的生成没有统一标准,核心目标是为用户当前使用的设备(如浏览器、手机App)创建一个稳定且唯一的标识,用于将刷新令牌和设备绑定。
这样设计可以实现“退出登录不影响其他设备”的功能,同时也能在刷新令牌时校验设备身份,提升安全性。
🔑 主流生成思路:服务端 vs 客户端
生成device_id主要有两种思路,各有侧重。
| 生成方式 | 核心逻辑 | 优点 | 缺点 |
|---|---|---|---|
| 服务端生成 (推荐) | 客户端首次连接时,服务端根据请求特征(如User-Agent、IP前缀等)组合哈希生成。 | 稳定:只要设备特征不变,ID不变;安全:服务端可控,无法被篡改。 | 依赖请求头信息,在纯API调用或用户更换网络时可能变化。 |
| 客户端生成 | 客户端利用本地信息(MAC地址、系统UUID等)生成,首次登录时提交给服务端绑定。 | 精确:能获取硬件级唯一标识。 | 隐私风险:需获取设备敏感权限;可靠性低:用户可能禁用权限,且ID可被伪造。 |
🚀 实战方案与代码示例
结合搜索到的资料,下面是几种具体的实现方式:
方案一:服务端生成“设备指纹” (轻量、易用)
服务端从HTTP请求中提取信息,组合后计算哈希。这是“无感登录”或Web应用的常用方式。
go
// 示例:服务端生成 device_id func generateDeviceID(r *http.Request) string { // 提取请求特征,User-Agent 是关键,IP 前缀可选 fingerprint := fmt.Sprintf("%s|%s", r.Header.Get("User-Agent"), strings.Split(r.RemoteAddr, ":")[0], // 取IP前缀 ) // 使用 SHA256 生成哈希作为 device_id hash := sha256.Sum256([]byte(fingerprint)) return fmt.Sprintf("%x", hash) }注意:应避免仅依赖单一不稳定的字段(如纯IP),组合多个特征能让ID更稳定。
方案二:使用三方库生成客户端硬件ID (更精确)
如果项目运行在可控的客户端(如内部系统),可以使用github.com/craig-jarvis/deviceid这个库来生成一个基于硬件信息的ID。
go
import "github.com/craig-jarvis/deviceid" func getClientDeviceID() (string, error) { builder := deviceid.NewBuilder() // 添加默认的硬件信息组件(如主板序列号、系统UUID等) // 在Windows上还会包含 WindowsDeviceId 和 MachineGuid return builder.GetDeviceIdWithDefaults() }🔐 关键:如何将 device_id 与 Refresh Token 绑定
生成device_id只是第一步,关键是在服务端使用它来管理会话,通常结合Redis实现。
登录时存储:用户登录成功后,生成
refresh_token时,将user_id和device_id组合作为Redis的Key,Value存储令牌信息,并设置有效期(如7天)。go
// 示例 Key 设计:refresh_token:{user_id}:{device_id} redisKey := fmt.Sprintf("refresh_token:%s:%s", userID, deviceID) // 将 refresh_token 信息存入 Redis,并设置过期时间刷新时校验:在
/auth/refresh接口中,客户端请求时必须携带其device_id。服务端首先根据user_id和传来的device_id组合Key去Redis查询。如果存在,说明是该设备的合法刷新请求,生成新的
access_token(也可同时轮换refresh_token)。如果不存在,说明该设备从未登录或已被单独踢下线,拒绝刷新,返回401。
实现“单设备退出”:当用户在当前设备点击“退出登录”时,Logout接口只需删除Redis中对应的
refresh_token:{user_id}:{device_id}这一条记录,其他设备的会话完全不受影响。
💡 补充建议
ID存储位置:服务端生成的
device_id应返回给客户端,客户端在后续所有需要认证的请求中(如放在HeaderX-Device-ID)携带,以便服务端进行校验。安全存储:对于敏感度高的应用,可以考虑使用更复杂的“设备指纹”技术,结合更多特征(如屏幕分辨率、时区等),甚至利用TLS指纹(如
tls.ClientHelloInfo)来提升设备识别的准确性和抗伪造能力。