鉴权机制-OAuth2.0 && JWT

Posted by jabin on July 28, 2021

认证和授权

认证是通过账号密码等方式验证用户的身份 授权是用户能访问什么的过程,在认证之后

令牌token

基于令牌实现认证和授权—OAuth2.0 和 JWT

  • 无状态,特别适合微服务
  • 可以在任何地方生成, 生成和校验解耦
  • 细粒度的访问控制

OAuth2.0

适用场景

客户端 需要 安全可控地获取 用户 授权, 访问服务器资源, 如开放api平台,社交联合登录,第三方应用接入等

名词解释

角色(ROLE)

  • Resource Owner。 用户实体
  • Resource Server。 服务接口
  • Client。 SDK客户端
  • Authorization Server。 认证和返回token

令牌(TOKENS)

  • Access Token。 默认30天过期,client需要定时获取
  • Access Token生成可以通过JWT

授权许可(Authorization Grant)

  • Client Credentials。 客户端以自己的名义,访问约定好的资源
  • Authorization Code
  • Implicit Grant
  • Resource owner password credentials

流程

  • 创建应用,指定应用的权限范围,获取应用的api_key 和 api_secret
  • 通过api_key 和 api_secret 向授权服务申请access token(Client Credentials模式)
  • 通过access token访问api服务

JWT

  • 分布式站点的单点登录场景,通过用户名密码登录后,获取jwt, 后续的接口请求带这个jwt
  • 可以作为OAuth2.0的token使用
  • 格式:[header,声明类型和加密算法].[payload,存放数据].[签名,验证身份]
import "github.com/golang-jwt/jwt"
func (l *GenTokenLogic) GenToken(in *appauthrpc_v1.GenTokenReq) (*appauthrpc_v1.GenTokenResp, error) {
	nowTime := time.Now()
	expireTime := nowTime.Add(time.Duration(l.svcCtx.Config.ExpireIn) * time.Second)

	// 携带的数据
	claims := &jwt.StandardClaims{
		ExpiresAt: expireTime.Unix(),
	}

	// 指定加密算法
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	// 生成
	tokenString, err := token.SignedString([]byte(l.svcCtx.Config.TokenSigningKey))
	if err != nil {
		return nil, err
	}

	return &appauthrpc_v1.GenTokenResp{
		AccessToken: tokenString,
		ExpireIn: l.svcCtx.Config.ExpireIn,
	}, nil
}

func (l *CheckTokenLogic) CheckToken(in *appauthrpc_v1.CheckTokenReq) (*appauthrpc_v1.CheckTokenResp, error) {
	token, err := jwt.Parse(in.AccessToken, func(token *jwt.Token) (interface{}, error) {
		return []byte(l.svcCtx.Config.TokenSigningKey), nil
	})

	if err != nil || token == nil {
		return nil, err
	}

	resp := &appauthrpc_v1.CheckTokenResp{
		Ret: RET_OK,
		Msg: "ok.",
	}

	if ve, ok := err.(*jwt.ValidationError); ok {
		resp.Ret = RET_VALID_ERR
		if ve.Errors&jwt.ValidationErrorMalformed != 0 {
			resp.Msg = "not a token."
		} else if ve.Errors&(jwt.ValidationErrorExpired|jwt.ValidationErrorNotValidYet) != 0 {
			resp.Msg = "token expired."
		} else {
			resp.Ret = RET_VALID_UNKNOW
			resp.Msg = "invalid token."
		}
	} else if !token.Valid{
		resp.Ret = RET_VALID_UNKNOW
		resp.Msg = "invalid token."
	}

	return resp, nil
}

其它鉴权方式

session-cookie 和 账号密码

refer