网站建设技术服务计入什么科目,优秀建筑方案设计文本,网站建设哪家质量好,西安网站开发公司JWT
JSON Web Token (JWT) 是一种开放标准 (RFC 7519)#xff0c;提供一种简洁且自包含的方式#xff0c;以JSON形式在通信双方间传递信息。这些信息可通过数字签名进行验证#xff0c;确保其可信度。JWT 可以使用密钥#xff08;HMAC#xff09;或 RSA 或 ECDSA 的公钥/…JWT
JSON Web Token (JWT) 是一种开放标准 (RFC 7519)提供一种简洁且自包含的方式以JSON形式在通信双方间传递信息。这些信息可通过数字签名进行验证确保其可信度。JWT 可以使用密钥HMAC或 RSA 或 ECDSA 的公钥/私钥对进行签名。虽然JWT可以加密以提供私密消息但我们一般指的是已签名的 tokens。
JWT作用
身份验证和授权JWT常被用来在身份提供者和服务提供者之间传递被认证的用户身份信息以便于从资源服务器获取资源。这种方式特别适用于分布式站点的登录场景357。此外JWT还可以包含用户标识、用户角色和权限等信息进一步实现细粒度的授权控制。信息交换由于JWT的载荷部分可以自定义它可以存储任何JSON格式的数据这使得JWT成为一种便捷的方式来进行信息交换。例如可以使用JWT来存储用户喜好、配置信息等自定义功能所需的信息。单点登录SSOJWT因其小巧和自包含的特性特别适合用于实现单点登录SSO。在单点登录场景中用户只需进行一次登录操作便可以在多个服务之间自由切换而无需重复登录极大地提高了用户体验。API密钥管理JWT也广泛应用于API密钥管理领域。通过使用JWT作为API请求的一部分可以有效地管理和控制对API的访问权限。替代传统的Session和Cookie机制在某些情况下JWT可以用来替代传统的Session和Cookie机制。与Session和Cookie相比JWT具有更小的服务器开销和更好的扩展性因为它直接将用户数据下发给客户端每次请求时附带发送给服务器。跨域请求Cross-Origin Requests:JWT可用于跨域请求的身份验证。由于JWT是自包含的它不依赖于任何服务器端的会话存储因此非常适合用于跨域API调用。
认证流程
前端通过Web将用户名和密码发送至后端通常采用HttpPost请求。后端在核对用户名和密码后会将用户ID及其他信息作为JWT payload并生成一个token返回给前端。前端保存这个token可以选择将其保存在localStorage或sessionStorage中退出登录时删除token即可。在后续的每次请求中前端需要在header中放入token。后端会检查token的有效性包括签名是否正确token是否过期等。当后端验证token通过后就会进行业务处理并返回相应结果。
JWT的组成
一个JWT实际上由三个部分组成它们之间用点.分隔
头部Header: 通常包含两个部分token的类型typ这里固定为JWT以及所使用的签名算法alg例如HS256表示使用HMAC和SHA256算法进行签名。例如{alg: HS256, typ: JWT} 载荷Payload: 包含了一系列可以被添加到JWT中的声明claims。这些声明可以分为三类 公共声明Public Claims: 任何JWT都可以包含的声明如exp过期时间、iat发行时间和sub主题。注册声明Registered Claims: 一组预定义的声明具有特定的语义意义如iss发行者、aud接收方等。私有声明Private Claims: 由应用程序定义的声明可以在标准声明之外添加额外的信息。 例如{sub: 1234567890, name: John Doe, iat: 1516239022} 签名Signature: 为了确保JWT的安全性使用头部中指定的算法和服务器的密钥对头部和载荷进行签名。接收方可以使用相同的密钥和算法来验证签名的有效性。签名的生成通常使用Base64 URL编码的头部和载荷然后与密钥一起通过签名算法进行加密。
将这三部分编码Base64 URL编码并用点连接起来就形成了一个完整的JWT
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cU5O62AvvewJHsfqh1EJpTbuE7sBqVb64hjSDgZSTrZDRbky3ofGkeXBXBQgTJgzTyTTh0b2tlbg在这个例子中JWT由三部分组成每部分都用.分隔。第一部分是头部指定了签名算法第二部分是载荷包含了用户信息和过期时间等声明第三部分是签名用于验证JWT的真实性。
代码实例
1.安装JWT库
安装golang-jwt/jwt库
go get -u [github.com/golang-jwt/jwt](http://github.com/golang-jwt/jwt)2.创建JWT密钥
import (github.com/golang-jwt/jwt
)// 生成JWT密钥
key : jwt.New(jwt.SigningMethodHS256)3.创建JWT令牌
创建JWT令牌时你需要定义载荷Payload中的声明Claims并使用密钥对令牌进行签名
import (github.com/golang-jwt/jwttime
)
//创建JWT令牌
func createToken(claims map[string]interface{})(string,error){//创建JWT令牌token:jwt.NewWithClaims(key,jwy.MapClaims(claims))//设置过期时间token.Claims.(jwt.MapClaims)[exp]time.Now().Add(5*time.Minute).Unix()//签发令牌tokenString, err : token.SignedString()if err ! nil {return , err}return tokenString, nil
}4.验证JWT令牌
验证JWT令牌时你需要使用相同的密钥和签名方法来验证签名的有效性并检查令牌是否过期
// parseToken 从给定的token字符串中解析出一个有效的jwt.Token实例。
// 函数接受一个tokenString参数表示待解析的JWT字符串
// 并返回一个指向jwt.Token的指针以及一个error。
// 如果解析成功返回的jwt.Token将包含解码后的JWT声明信息
// 否则返回一个非nil error说明解析失败的原因。
func parseToken(tokenString string)(*jwt.Token,error){
// 使用jwt.ParseWithClaims方法解析tokenString// 将声明部分解析为jwt.MapClaims类型一个可索引的声明映射。// 提供一个自定义的解析密钥验证函数作为第三个参数。tokne,err:jwt.ParseWithClaims(tokenString,jwt.MapClaims{},func(tokekn *jwt.Token)(interface{},error){return key,nil})if err!nil{return nil,error}// 如果token解析成功且有效即其声明经过验证且未过期等// 则尝试将其声明部分转换为jwt.MapClaims类型并检查转换是否成功。// 若成功且token仍处于有效状态token.Valid true则返回解析得到的token及其原始声明。if claims, ok : token.Claims.(*jwt.MapClaims); ok token.Valid {return token, nil}// 如果上述条件均不满足即解析或验证过程中出现错误// 或token已过期、被篡改等导致无效则返回一个表示无效JWT的预定义错误。return nil, jwt.ErrInvalid
}此函数的主要功能是接收一个JWT字符串通过调用jwt.ParseWithClaims方法对其进行解析。在解析过程中它会使用传入的密钥key验证JWT的签名确保其完整性和有效性。如果解析和验证成功函数返回解析后的jwt.Token实例以及nil错误。否则返回nil的jwt.Token指针和相应的错误信息。
5.实现登录和注册逻辑
在后端服务中实现登录和注册逻辑并在登录成功后返回JWT令牌
func login(w http.ResponseWriter, r *http.Request) {// 假设你已经验证了用户的凭据// 创建JWT令牌claims : jwt.MapClaims{userID: user-id,exp: time.Now().Add(5 * time.Minute).Unix(),}tokenString, err : createToken(claims)if err ! nil {// 处理错误}// 返回JWT令牌w.Header().Set(Content-Type, application/json)json.NewEncoder(w).Encode(map[string]string{token: tokenString})
}6. 保护路由
创建一个中间件来保护你的路由确保只有携带有效JWT令牌的请求才能访问
// requireAuth 是一个HTTP中间件函数用于对请求进行JWT身份验证。
// 它接收一个http.HandlerFunc类型的next参数代表待执行的下一个HTTP处理器。
// 函数返回一个新的http.HandlerFunc该处理器在实际执行next之前
// 会对传入的请求进行JWT令牌验证。若验证失败则返回HTTP 401 Unauthorized响应。
func requireAuth(next http.HandlerFunc) http.HandlerFunc {// 返回一个匿名http.HandlerFunc作为实际执行的身份验证中间件。return func(w http.ResponseWriter, r *http.Request) {// 从请求头的Authorization字段中提取JWT令牌字符串。tokenString : r.Header.Get(Authorization)// 如果请求头中没有提供Authorization字段或其值为空// 则向客户端发送HTTP 401 Unauthorized响应并附带错误消息。if tokenString {http.Error(w, Authorization header is required, http.StatusUnauthorized)return}// 使用parseToken函数解析并验证JWT令牌字符串。// 如果解析或验证过程中发生错误或者令牌本身无效// 则向客户端发送HTTP 401 Unauthorized响应并附带错误消息。token, err : parseToken(tokenString)if err ! nil || !token.Valid {http.Error(w, Invalid token, http.StatusUnauthorized)return}// 如果JWT令牌验证成功继续执行传入的next处理器即后续的HTTP处理逻辑。next(w, r)}
}此代码实现了HTTP中间件requireAuth其作用是在接收到HTTP请求时检查请求头中的Authorization字段是否存在并含有有效的JWT令牌。如果请求缺少Authorization字段或令牌无效中间件会立即向客户端返回HTTP 401 Unauthorized状态码及相应的错误消息。反之若令牌验证通过则允许请求继续传递到下一个HTTP处理器next参数指定的函数进行后续处理。这样的设计使得您可以轻松地将requireAuth中间件应用于特定路由或全局以确保只有持有有效JWT令牌的客户端才能访问受保护的资源。 然后使用这个中间件来保护你的路由
http.HandleFunc(/protected, requireAuth(func(w http.ResponseWriter, r *http.Request) {fmt.Fprintf(w, Welcome to the protected route!)
}))实例
在Go语言中实现JWT鉴权的完整应用实例。
package mainimport (fmtnet/httptimejwtgo github.com/golang-jwt/jwt
)// jwtKey 是用于签署 JWT 令牌的秘密密钥必须保密。
var jwtKey []byte(my_secret_key)// User 定义了用户的结构体包含用户的ID和姓名。
// 这些字段将被编码到 JWT 令牌中。
type User struct {ID int json:id // 用户的唯一标识符Name string json:name // 用户的姓名
}// createToken 根据提供的 User 对象创建一个新的 JWT 令牌。
// 它设置了一个 userID 声明和一个 5 分钟后过期的时间。
func createToken(user *User) (string, error) {// 使用 HS256 签名方法创建一个新的 JWT 实例。token : jwtgo.New(jwtgo.SigningMethodHS256)// 将 token 的 Claims 设置为 MapClaims 类型以便我们可以添加自定义声明。claims : token.Claims.(jwtgo.MapClaims)// 添加 userID 声明到 JWT 载荷中。claims[userID] user.ID// 设置 exp 声明为当前时间加上 5 分钟的 Unix 时间戳。claims[exp] time.Now().Add(5 * time.Minute).Unix()// 使用 jwtKey 签署令牌并将其转换为字符串。tokenString, err : token.SignedString(jwtKey)if err ! nil {return , err}return tokenString, nil
}// parseToken 接受一个 JWT 令牌字符串并使用 jwtKey 验证其签名。
// 如果令牌有效它将返回解析后的 jwtgo.Token 对象。
func parseToken(tokenString string) (*jwtgo.Token, error) {// 使用 jwtgo.ParseWithClaims 解析 JWT 令牌并使用 MapClaims 类型作为期望的声明类型。token, err : jwtgo.ParseWithClaims(tokenString, jwtgo.MapClaims{}, func(token *jwtgo.Token) (interface{}, error) {// 提供 jwtKey 作为密钥来验证令牌的签名。return jwtKey, nil})if err ! nil {return nil, err}// 确保解析后的声明是 MapClaims 类型。if _, ok : token.Claims.(*jwtgo.MapClaims); !ok {return nil, fmt.Errorf(invalid claims)}// 检查令牌是否有效例如是否已过期。if !token.Valid {return nil, fmt.Errorf(token is not valid)}return token, nil
}// register 是一个 HTTP 处理函数用于处理用户注册请求。
// 它创建一个新的 User 对象并为其生成一个 JWT 令牌。
func register(w http.ResponseWriter, r *http.Request) {// 示例中省略了实际的用户注册逻辑如保存用户信息到数据库。// 直接创建一个示例用户和令牌。user : User{ID: 1, Name: John Doe}token, err : createToken(user)if err ! nil {http.Error(w, Error creating token, http.StatusInternalServerError)return}// 设置响应头为 application/json 并将令牌编码为 JSON 返回。w.Header().Set(Content-Type, application/json)json.NewEncoder(w).Encode(map[string]string{token: token})
}// login 是一个 HTTP 处理函数用于处理用户登录请求。
// 它验证用户凭据示例中省略并返回一个 JWT 令牌。
func login(w http.ResponseWriter, r *http.Request) {// 示例中省略了实际的用户登录逻辑如验证用户凭据。// 直接创建一个示例用户和令牌。user : User{ID: 1, Name: John Doe}token, err : createToken(user)if err ! nil {http.Error(w, Error creating token, http.StatusInternalServerError)return}w.Header().Set(Content-Type, application/json)json.NewEncoder(w).Encode(map[string]string{token: token})
}// protected 是一个 HTTP 处理函数用于处理需要身份验证的请求。
// 它从 HTTP 头中提取 JWT 令牌并验证其有效性。
func protected(w http.ResponseWriter, r *http.Request) {// 从 Authorization HTTP 头中提取 JWT 令牌。tokenString : r.Header.Get(Authorization)if tokenString {http.Error(w, Unauthorized access, http.StatusUnauthorized)return}// 解析并验证 JWT 令牌。token, err : parseToken(tokenString)if err ! nil {http.Error(w, Unauthorized access, http.StatusUnauthorized)return}// 如果令牌有效返回成功响应。w.WriteHeader(http.StatusOK)w.Write([]byte(Welcome to the protected route!))
}func main() {// 设置 HTTP 路由处理函数。http.HandleFunc(/register, register)http.HandleFunc(/login, login)http.HandleFunc(/protected, protected)// 启动 HTTP 服务并监听 8080 端口。http.ListenAndServe(:8080)
}这个程序实现了一个简单的 JWT 鉴权系统。它提供了用户注册和登录功能这两个功能都会生成一个 JWT 令牌。此外它还提供了一个受保护的路由该路由只允许携带有效 JWT 令牌的请求访问。