golang之jwt的token登录
什么是 JSON Web Token?
JSON Web Token(JWT)是一个开放标准(RFC 7519),它定义了一种紧凑且自包含的方式,用于在各方之间以 JSON 方式安全地传输信息。由于此信息是经过数字签名的,因此可以被验证和信任。可以使用秘密(使用** HMAC 算法)或使用 RSA 或 ECDSA 的公钥/私钥对对 JWT 进行签名**。
直白的讲 jwt 就是一种用户认证(区别于 session、cookie)的解决方案。
出现的背景
众所周知,在 jwt 出现之前,我们已经有 session、cookie 来解决用户登录等认证问题,为什么还要 jwt 呢?
这里我们先了解一下 session,cookie。
session
熟悉 session 运行机制的同学都知道,用户的 session 数据以 file 或缓存(redis、memcached)等方式存储在服务器端,客户端浏览器 cookie 中只保存 sessionid。服务器端 session 属于集中存储,数量不大的情况下,没什么问题,当用户数据逐渐增多到一程度,就会给服务端管理和维护带来大的负担。
session 有两个弊端:
1、无法实现跨域。
2、由于 session 数据属于集中管理里,量大的时候服务器性能是个问题。
优点:
1、session 存在服务端,数据相对比较安全。
2、session 集中管理也有好处,就是用户登录、注销服务端可控。
cookie
cookie 也是一种解决网站用户认证的实现方式,用户登录时,服务器会发送包含登录凭据的 Cookie 到用户浏览器客户端,浏览器会将 Cookie 的 key/value 保存用户本地(内存或硬盘),用户再访问网站,浏览器会发送 cookie 信息到服务器端,服务器端接收 cookie 并解析来维护用户的登录状态。
cookie 避免 session 集中管理的问题,但也存在弊端:
1、跨域问题。
2、数据存储在浏览器端,数据容易被窃取及被 csrf 攻击,安全性差。
优点:
1、相对于 session 简单,不用服务端维护用户认证信息。
2、数据持久性。
jwt
jwt 通过 json 传输,php、java、golang 等很多语言支持,通用性比较好,不存在跨域问题。传输数据通过数据签名相对比较安全。客户端与服务端通过 jwt 交互,服务端通过解密 token 信息,来实现用户认证。不需要服务端集中维护 token 信息,便于扩展。当然 jwt 也有其缺点。
缺点:
1、用户无法主动登出,只要 token 在有效期内就有效。这里可以考虑 redis 设置同 token 有效期一直的黑名单解决此问题。
2、token 过了有效期,无法续签问题。可以考虑通过判断旧的 token 什么时候到期,过期的时候刷新 token 续签接口产生新 token 代替旧 token。
jwt 设置有效期
可以设置有效期,加入有效期是为了增加安全性,即 token 被黑客截获,也只能攻击较短时间。设置有效期就会面临 token 续签问题,解决方案如下
通常服务端设置两个 token
- Access Token:添加到 HTTP 请求的 header 中,进行用户认证,请求接口资源。 一般该值有效期比较短, 例如:10min
- refresh token:用于当 Access Token 过期后,客户端传递 refresh token 刷新 Access Token 续期接口,获取新的 Access Token 和 refresh token。其有效期比 Access Token 有效期长。例如:7d
jwt 构成:
- Header:TOKEN 的类型,就是 JWT,签名的算法,如 HMAC SHA256、HS384
- Payload:载荷又称为 Claim,携带的信息,比如用户名、过期时间等,一般叫做 Claim
- Signature:签名,是由 header、payload 和你自己维护的一个 secret 经过加密得来的
jwt 使用
这里推荐个使用比较多的开源项目、[github.com/dgrijalva/jwt-go](),更多文档。
示例:
package main import (
"fmt"
"github.com/dgrijalva/jwt-go"
"time"
)
const (
SECRETKEY = "243223ffslsfsldfl412fdsfsdf"//私钥
)
//自定义 Claims
type CustomClaims struct {
UserId int64
jwt.StandardClaims
}
func main() {
//生成 token
maxAge:=60*60*24
customClaims :=&CustomClaims{
UserId: 11,//用户 id
StandardClaims: jwt.StandardClaims{
ExpiresAt: time.Now().Add(time.Duration(maxAge)*time.Second).Unix(), // 过期时间,必须设置
Issuer:"jerry", // 非必须,也可以填充用户名,
},
}
//采用 HMAC SHA256 加密算法
token:=jwt.NewWithClaims(jwt.SigningMethodHS256, customClaims)
tokenString,err:= token.SignedString([]byte(SECRETKEY))
if err!=nil {
fmt.Println(err)
}
fmt.Printf("token: %v\n", tokenString) //解析 token
ret,err :=ParseToken(tokenString)
if err!=nil {
fmt.Println(err)
}
fmt.Printf("userinfo: %v\n", ret)
} //解析 token
func ParseToken(tokenString string)(*CustomClaims,error) {
token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
}
return []byte(SECRETKEY), nil
})
if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid {
return claims,nil
} else {
return nil,err
}
}
Claims:
Audience string `json:"aud,omitempty"`
ExpiresAt int64 `json:"exp,omitempty"`
Id string `json:"jti,omitempty"`
IssuedAt int64 `json:"iat,omitempty"`
Issuer string `json:"iss,omitempty"`
NotBefore int64 `json:"nbf,omitempty"`
Subject string `json:"sub,omitempty"` aud: 接收 jwt 的一方
exp: jwt 的过期时间,这个过期时间必须要大于签发时间
jti: jwt 的唯一身份标识,主要用来作为一次性 token, 从而回避重放攻击。
iat: jwt 的签发时间
iss: jwt 签发者
nbf: 定义在什么时间之前,该 jwt 都是不可用的。就是这条 token 信息生效时间。这个值可以不设置,但是设定后,一定要大于当前 Unix UTC, 否则 token 将会延迟生效。
sub: jwt 所面向的用户
示例:
package main import (
"fmt"
"github.com/dgrijalva/jwt-go"
"time"
)
const (
SECRETKEY = "243223ffslsfsldfl412fdsfsdf"//私钥
)
//自定义 Claims
type CustomClaims struct {
UserId int64
jwt.StandardClaims
}
func main() {
//生成 token
maxAge:=60*60*24
// Create the Claims
//claims := &jwt.StandardClaims{
// // ExpiresAt: time.Now().Add(time.Duration(maxAge)*time.Second).Unix(), // 过期时间,必须设置,
// // Issuer: "jerry",// 非必须,也可以填充用户名,
// //} //或者用下面自定义 claim
claims := jwt.MapClaims{
"id": 11,
"name": "jerry",
"exp": time.Now().Add(time.Duration(maxAge)*time.Second).Unix(), // 过期时间,必须设置,
} token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
tokenString, err := token.SignedString([]byte(SECRETKEY))
if err!=nil {
fmt.Println(err)
}
fmt.Printf("token: %v\n", tokenString) //解析 token
ret,err :=ParseToken(tokenString)
if err!=nil {
fmt.Println(err)
}
fmt.Printf("userinfo: %v\n", ret)
} //解析 token
func ParseToken(tokenString string)(jwt.MapClaims,error) {
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
// Don't forget to validate the alg is what you expect:
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
} // hmacSampleSecret is a []byte containing your secret, e.g. []byte("my_secret_key")
return []byte(SECRETKEY), nil
})
if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
return claims,nil
} else {
return nil,err
}
}
前面已经说明了,Token在有效期内可以一直访问资源,就产生了使用refresh_token来刷新access_token, 其中将access_token设置有效期时间段一些, refresh_token会设置的时间长一些, 如果access_token被窃取,只可以在较短的时间段内进行资源的访问,再次使用refresh_token去获取access_token的时候, 可以通过对refresh_token的控制来控制access_token的获取. 就此引入refresh_token:
- 客户端使用用户名密码进行认证
- 服务端生成有效时间较短的 Access Token(例如 10 分钟),和有效时间较长的 Refresh Token(例如 7 天)
- 客户端访问需要认证的接口时,携带 Access Token
- 如果 Access Token 没有过期,服务端鉴权后返回给客户端需要的数据
- 如果携带 Access Token 访问需要认证的接口时鉴权失败(例如返回 401 错误),则客户端使用 Refresh Token 向刷新接口申请新的 Access Token
- 如果 Refresh Token 没有过期,服务端向客户端下发新的 Access Token
- 客户端使用新的 Access Token 访问需要认证的接口

将生成的 Refresh Token 以及过期时间存储在服务端的数据库中,由于 Refresh Token 不会在客户端请求业务接口时验证,只有在申请新的 Access Token 时才会验证,所以将 Refresh Token 存储在数据库中,不会对业务接口的响应时间造成影响,也不需要像 Session 一样一直保持在内存中以应对大量的请求。
上述的架构,提供了服务端禁用用户 Token 的方式,当用户需要登出或禁用用户时,只需要将服务端的 Refresh Token 禁用或删除,用户就会在 Access Token 过期后,由于无法获取到新的 Access Token 而再也无法访问需要认证的接口。这样的方式虽然会有一定的窗口期(取决于 Access Token 的失效时间),但是结合用户登出时客户端删除 Access Token 的操作,基本上可以适应常规情况下对用户认证鉴权的精度要求。
小结:
- 服务端生成的 jwt 返回客户端可以存到 cookie 也可以存到 localStorage 中(相比 cookie 容量大),存在 cookie 中需加上
HttpOnly的标记,可以防止 XSS) 攻击。 - 尽量用 https 带证书网址访问。
- session 和 jwt 没有绝对好与不好,各有其擅长的应用环境,请根据实际情况选择。
golang之jwt的token登录的更多相关文章
- 个人博客开发之blog-api 项目整合JWT实现token登录认证
前言 现在前后端分离,基于session设计到跨越问题,而且session在多台服器之前同步问题,肯能会丢失,所以倾向于使用jwt作为token认证 json web token 导入java-jwt ...
- 基于JWT的Token登录认证(一)
1.JWT简介 JSON Web Token(缩写 JWT),是目前最流行的跨域认证解决方案. session登录认证方案:用户从客户端传递用户名.密码等信息,服务端认证后将信息存储在session中 ...
- 基于JWT的Token登录认证
1.JWT简介 JSON Web Token(缩写 JWT),是目前最流行的跨域认证解决方案. 2.JWT的原理 JWT的原理是,服务器认证以后,生成一个JSON格式的对象,发回给客 ...
- 国服最强JWT生成Token做登录校验讲解,看完保证你学会!
转载于:https://blog.csdn.net/u011277123/article/details/78918390 Free码农 2017-12-28 00:08:02 JWT简介 JWT(j ...
- JWT生成Token做登录校验
一.JWT的优点 1.服务端不需要保存传统会话信息,没有跨域传输问题,减小服务器开销. 2.jwt构成简单,占用很少的字节,便于传输. 3.json格式通用,不同语言之间都可以使用. 二.使用JWT进 ...
- 常用功能系列---【JWT生成Token实现接口登录认证方案思路】
JWT生成Token实现接口登录认证方案思路 方案一(双token实现无感刷新) 在token中,refreshToken的作用主要是避免token过期时,前端用户突然退出登录,跳转至登录页面. 但是 ...
- 使用 JWT 生成 Token 代码示例
JSON Web Token,简称 JWT, 是一个开放的标准(RFC 7519),它定义了以一种紧凑的.自包含的 JSON 对象在各方之间安全传输信息的方式.该信息含有数字签名,可以被验证和信任. ...
- 基于jwt的用户登录认证
最近在app的开发过程中,做了一个基于token的用户登录认证,使用vue+node+mongoDB进行的开发,前来总结一下. token认证流程: 1:用户输入用户名和密码,进行登录操作,发送登录信 ...
- ASP.NET WebApi 基于JWT实现Token签名认证
一.前言 明人不说暗话,跟着阿笨一起玩WebApi!开发提供数据的WebApi服务,最重要的是数据的安全性.那么对于我们来说,如何确保数据的安全将会是需要思考的问题.在ASP.NET WebServi ...
- Django+JWT实现Token认证
对外提供API不用django rest framework(DRF)就是旁门左道吗? 基于Token的鉴权机制越来越多的用在了项目中,尤其是对于纯后端只对外提供API没有web页面的项目,例如我们通 ...
随机推荐
- .NET 多版本兼容的精美 WinForm UI控件库
前言 有粉丝小伙伴在后台留言咨询有没有WinForm 控件库推荐,现在就给安排上. .NET 平台进行 Windows 应用程序开发的我们来说,找一个既美观又实用的 WinForm UI 控件库至关重 ...
- JS插入排序完全理解
插入排序是JS中的一种常见数组排序算法,记录一下如何理解并实现插入排序的功能; 首先看一下最直观的动态图 图片来源:https://www.javascriptc.com/ 从图像可以很直观的看出,插 ...
- 如何将图片转换为向量?(通过DashScope API调用)
本文介绍如何通过模型服务灵积DashScope将 图片转换为向量 ,并入库至向量检索服务DashVector中进行向量检索. 模型服务灵积DashScope,通过灵活.易用的模型API服务,让各种模态 ...
- OData – Routing
前言 以前我都是把 ODataController 和普通 API Controller 分开做. (因为 OData 实在多 Bug, 好东西尽量不要掺和它) Read 的部分用 OData, CU ...
- 痞子衡嵌入式:如果i.MXRT离线无法启动,试着分析ROM启动日志
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是恩智浦i.MXRT系列MCU的ROM启动日志. 关于 i.MX RT 启动问题解决的文章,痞子衡写过非常多,其中大部分都是具体到某一类启 ...
- Git常用命令,分支合并与管理
Git 自我感觉最大的优点是对版本库和分支的管理 各自的电脑都对项目保存一份版本,分支合并也极其方便. 而且Git的目录操作命令跟Linux一样,使用起来还是非常顺手的. Git详解 Git 常用命令 ...
- 基于Python后端构建多种不同的系统终端界面研究
在我们一般开发系统的时候,往往会根据实际需要做出各种不同的系统终端界面,如基于BS的,CS.APP.小程序等等,一般都是基于一个统一接入的Web API后端,本篇系统探寻对基于Python后端构建多种 ...
- 什么是 js 事件循环 event loop
知识储备 : js 的执行 机制 js 的底层执行机制 : 对于 js 代码 分为了同步 和 异步 代码 ,异步代码 较少比如:setInterval setTimeout 等(不会超过10 个) 其 ...
- C#扩展方法 Where Any Count Signal SignalOrDefault First 等方法的使用
using System; using System.Collections.Generic; using System.Linq; using System.Net.Http; using Syst ...
- day17-打印三角形及Debug
打印三角形 之前学习C++的时候也曾做过,于是乎凭借记忆又试了试,也还能试出来.代码如下: #include<stdio.h> int main(){ int i,j; for(i= ...