golang 之JWT实现 golang jwt token 刷新问题
bigegpt 2024-10-29 12:58 6 浏览
什么是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 中,进行用户认证,请求接口资源。
- refresh token:用于当 Access Token过期后,客户端传递refresh token刷新 Access Token续期接口,获取新的Access Token和refresh token。其有效期比 Access Token有效期长。
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
}
}
?
运行结果:
token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVc2VySWQiOjExLCJleHAiOjE1OTA5MTk1NDAsImlzcyI6ImplcnJ5In0.FppmbbHRrS4wd5wen73vYPOvtzycOrn2JZlK6JRjEGk userinfo: &{11 { 1590919540 0 jerry 0 }}
以上用到了CustomClaims,也可以用简单的方法
示例
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: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1OTA5MzUzMDUsImlkIjoxMSwibmFtZSI6ImplcnJ5In0.fapE0IiOEe_TqoMCThbNTHUvgWiHPEk0rm-9uPIcvPU userinfo: map[exp:1.590935305e+09 id:11 name:jerry]
小结:
- 服务端生成的jwt返回客户端可以存到cookie也可以存到localStorage中(相比cookie容量大),存在cookie中需加上 HttpOnly 的标记,可以防止 XSS 攻击。
- 尽量用https带证书网址访问。
- session和jwt没有绝对好与不好,各有其擅长的应用环境,请根据实际情况选择。
参考资料
https://github.com/guyan0319/golang_development_notes
https://godoc.org/github.com/dgrijalva/jwt-go
https://blog.csdn.net/weixin_43613053/article/details/84642140
https://www.cnblogs.com/flipped/p/12973557.html
links
- 目录
相关推荐
- 悠悠万事,吃饭为大(悠悠万事吃饭为大,什么意思)
-
新媒体编辑:杜岷赵蕾初审:程秀娟审核:汤小俊审签:周星...
- 高铁扒门事件升级版!婚宴上‘冲喜’老人团:我们抢的是社会资源
-
凌晨两点改方案时,突然收到婚庆团队发来的视频——胶东某酒店宴会厅,三个穿大红棉袄的中年妇女跟敢死队似的往前冲,眼瞅着就要扑到新娘的高额钻石项链上。要不是门口小伙及时阻拦,这婚礼造型团队熬了三个月的方案...
- 微服务架构实战:商家管理后台与sso设计,SSO客户端设计
-
SSO客户端设计下面通过模块merchant-security对SSO客户端安全认证部分的实现进行封装,以便各个接入SSO的客户端应用进行引用。安全认证的项目管理配置SSO客户端安全认证的项目管理使...
- 还在为 Spring Boot 配置类加载机制困惑?一文为你彻底解惑
-
在当今微服务架构盛行、项目复杂度不断攀升的开发环境下,SpringBoot作为Java后端开发的主流框架,无疑是我们手中的得力武器。然而,当我们在享受其自动配置带来的便捷时,是否曾被配置类加载...
- Seata源码—6.Seata AT模式的数据源代理二
-
大纲1.Seata的Resource资源接口源码2.Seata数据源连接池代理的实现源码3.Client向Server发起注册RM的源码4.Client向Server注册RM时的交互源码5.数据源连接...
- 30分钟了解K8S(30分钟了解微积分)
-
微服务演进方向o面向分布式设计(Distribution):容器、微服务、API驱动的开发;o面向配置设计(Configuration):一个镜像,多个环境配置;o面向韧性设计(Resista...
- SpringBoot条件化配置(@Conditional)全面解析与实战指南
-
一、条件化配置基础概念1.1什么是条件化配置条件化配置是Spring框架提供的一种基于特定条件来决定是否注册Bean或加载配置的机制。在SpringBoot中,这一机制通过@Conditional...
- 一招解决所有依赖冲突(克服依赖)
-
背景介绍最近遇到了这样一个问题,我们有一个jar包common-tool,作为基础工具包,被各个项目在引用。突然某一天发现日志很多报错。一看是NoSuchMethodError,意思是Dis...
- 你读过Mybatis的源码?说说它用到了几种设计模式
-
学习设计模式时,很多人都有类似的困扰——明明概念背得滚瓜烂熟,一到写代码就完全想不起来怎么用。就像学了一堆游泳技巧,却从没下过水实践,很难真正掌握。其实理解一个知识点,就像看立体模型,单角度观察总...
- golang对接阿里云私有Bucket上传图片、授权访问图片
-
1、为什么要设置私有bucket公共读写:互联网上任何用户都可以对该Bucket内的文件进行访问,并且向该Bucket写入数据。这有可能造成您数据的外泄以及费用激增,若被人恶意写入违法信息还可...
- spring中的资源的加载(spring加载原理)
-
最近在网上看到有人问@ContextConfiguration("classpath:/bean.xml")中除了classpath这种还有其他的写法么,看他的意思是想从本地文件...
- Android资源使用(android资源文件)
-
Android资源管理机制在Android的开发中,需要使用到各式各样的资源,这些资源往往是一些静态资源,比如位图,颜色,布局定义,用户界面使用到的字符串,动画等。这些资源统统放在项目的res/独立子...
- 如何深度理解mybatis?(如何深度理解康乐服务质量管理的5个维度)
-
深度自定义mybatis回顾mybatis的操作的核心步骤编写核心类SqlSessionFacotryBuild进行解析配置文件深度分析解析SqlSessionFacotryBuild干的核心工作编写...
- @Autowired与@Resource原理知识点详解
-
springIOCAOP的不多做赘述了,说下IOC:SpringIOC解决的是对象管理和对象依赖的问题,IOC容器可以理解为一个对象工厂,我们都把该对象交给工厂,工厂管理这些对象的创建以及依赖关系...
- java的redis连接工具篇(java redis client)
-
在Java里,有不少用于连接Redis的工具,下面为你介绍一些主流的工具及其特点:JedisJedis是Redis官方推荐的Java连接工具,它提供了全面的Redis命令支持,且...
- 一周热门
- 最近发表
- 标签列表
-
- mybatiscollection (79)
- mqtt服务器 (88)
- keyerror (78)
- c#map (65)
- resize函数 (64)
- xftp6 (83)
- bt搜索 (75)
- c#var (76)
- mybatis大于等于 (64)
- xcode-select (66)
- mysql授权 (74)
- 下载测试 (70)
- linuxlink (65)
- pythonwget (67)
- androidinclude (65)
- logstashinput (65)
- hadoop端口 (65)
- vue阻止冒泡 (67)
- oracle时间戳转换日期 (64)
- jquery跨域 (68)
- php写入文件 (73)
- kafkatools (66)
- mysql导出数据库 (66)
- jquery鼠标移入移出 (71)
- 取小数点后两位的函数 (73)