• Go如何实现基于Token 的身份认证
  • 发布于 2个月前
  • 232 热度
    0 评论
基于 Token 认证机制
Token 认证机制其实就是服务端将认证信息返回给客户端,客户端访问其他页面时都需要携带认证信息给服务端,由服务端验证这个认证信息是否有效可使用。大致流程:
1.使用账号和密码请求登录,服务端接收到账号和密码进行验证
2.验证成功,就会签发一个Token,返回给客户端
3.前端接收Token并存储,后续客户端再请求其他页面都携带 Token
4.服务端接收到客户端的请求时,先验证请求中携带的 token,如果验证成功,就向客户端返回请求的数据

Token 生成和验证
现在网上有很多关于 JWT 的使用说明可以直接搜索使用。其中, Token = Header + '.' + Payload + '.' + Signature。
Header 是用来说明签名的加密算法
{
    "typ":"jwt"
    "alg":"HS256"
}
Payload 是记录我们可能需要的信息,除了现有已提供的标准的Claims ,还可以自定义一些可能需要的字段,比如 userId,userName 等。claims 是可以被解密的,所以不要存放重要而敏感的的信息。
// --- 标准
iss (issuer): JWT 的签发人
exp (expiration time): 过期时间
sub (subject): 主题,该jwt所面向的用户
aud (audience): 受众 接收该 jwt的一方
nbf (Not Before): 生效时间
iat (Issued At): 签发时间
jti (JWT ID): 编号

// 自定义 Claims
userId
userName
Signature 是由Header 中声明的加密算法进行生成的签名

实践

创建 jwtMaker 文件,定义 Maker 接口来规范 jwtMaker 只需要进行创建 CreateToken 和验证 VerifyToken两个方法。可以使用现在比较常用的一个jwt 包来完成 Token 的 ,直接获取:go get github.com/dgrijalva/jwt-go。


声明Claims
定义一个标识用户信息的结构体,这部分也是所需要的 Claims。如用户名称、签发时间、过期时间。
type CustomClaims struct {
   // 堆代码 duidaima.com
    ID uuid.UUID
    UserName string
    IssuedAt time.Time
    ExpiresAt time.Time
}

func NewCustomClaims(userName string, duration time.Duration)( * CustomClaims, error) {
    tokenId, err: = uuid.NewRandom()
    if err != nil {
        return nil, err
    }
    customClaim: = & CustomClaims {
        ID: tokenId,
        UserName: userName,
        IssuedAt: time.Now(),
        ExpiresAt: time.Now().Add(duration),
    }
    return customClaim, nil
}

func(c * CustomClaims) Valid() error {
    if time.Now().After(c.ExpiresAt) {
        return errors.New("token has expired")
    }
    return nil
}
创建签名
func(j * JwtMaker) CreateToken(userName string, duration time.Duration)(string, error) {
    payload, err: = NewCustomClaims(userName, duration)
    if err != nil {
        return "", err
    }

    jwtToken: = jwt.NewWithClaims(jwt.SigningMethodHS256, payload)
    return jwtToken.SignedString([] byte(j.SecretKey))
}
验证签名
func(j * JwtMaker) VerifyToken(token string)( * CustomClaims, error) {
    var result = new(CustomClaims)
    jwtToken, err: = jwt.ParseWithClaims(token, result, func(token * jwt.Token)(interface {}, error) {
        if _, ok: = token.Method.( * jwt.SigningMethodHMAC);
        !ok {
            return nil, errors.New("无效 Token")
        } else {
            return [] byte(j.SecretKey), nil
        }
    })
    if err != nil {
            return nil, err
        }
        // 校验 token
    if jwtToken.Valid {
        return result, nil
    }
    if err = result.Valid();
    err != nil {
        return nil, fmt.Errorf("token 已过期")
    }
    return nil, fmt.Errorf("invalid token")
}

验证签名时,通过解析 token ,验证 token 是否有效,再验证是否过期。验证Token 是用在用户登录后所有请求都需要携带 token ,然后服务端获取到 token 再进行验证过。


多设备登录登出
使用 Token 进行无状态认证后,就可以更方便实现多端登录。在 Claims 中可以加入签发 Token 的机器信息,这样就可以有效的控制多端的登录登出多种场景。

允许多端同时登录,任意端退出时其他端不退出:每个端生成都生成自己的Token,互相独立。
允许多端同时登录,任意端退出其他端也退出:增加用户设备表,记录用户的登录设备和对应的Token ,当退出时将该用户所有的Token 都失效。
多端选择性退出部分端:退出登录时增加一个需要退出端的对应设备类型或者设备ID,然后对应设备下的token 进行失效处理。

等等还有其他的使用场景,使用token 可以自由安全支持多端登录的机制。


最后
session 是用于记录服务器和客户端会话状态的机制。session 存储在服务端中,但当在集群或微服务架构中,多个服务端都在提供服务时,用户要获取自己的会话状态时,无论请求到那个服务端时都必须获取到自己相关的唯一 session 信息,所以此时需要多个服务端共享这个session。所以,如果将 session 存储再缓存中,就可以通过这种方式共享session。
用户评论