简介
JSON Web Token(JWT)技术是一种编码字符串或令牌,包含无限的信息,URL安全,用于API身份验证。它是加密签名的。JWT身份验证的最大优点是令牌一旦发送,中间人就无法攻击和修改令牌。主要是JWT用于认证和发送机密信息。JWT使用了不同的算法(如HMAC、RSA)作为私钥/公钥进行数字签名。
JWT结构
- Header
- 主要包括算法和token类型,主要是jwt
- 有效载荷
- 包含数据信息
- 签名
- 它需要标头和有效负载进行编码。它验证令牌不会更改或受到攻击
package main
import (
"errors"
"fmt"
"net/http"
"os"
"time"
"github.com/dgrijalva/jwt-go"
"github.com/gin-gonic/gin"
)
// Title JWT SERVER
// JWT只能在过期时失效
// JWT过期后需要重新登录,会导致用户体验差,可以通过Redis存储JWT,实现JWT刷新
// JWT可能会被黑客劫持,实现一个一键注销功能,不需要等待过期
/**
* jwt.StandardClaims 声明
*/
type AuthRoleClaims struct {
v interface{}
Method *JWTMethod
jwt.StandardClaims
}
type JWTMethod struct {
ExpiresAt int64
Issuer string
key string
}
type JWTService interface {
//生成TOKEN
GeneratorToken(phone string) string
//验证TOKEN
ValidatedToken(jwtToken string) (*jwt.Token, error)
//刷新TOKEN
RefreshToken(jwtToken string) (*jwt.Token, error)
//注销TOKEN
DeleteToken(jwtToken string) (*jwt.Token, error)
}
// jwt.NewWithClaims 生成JWT
// jwt.NewWithClaims 方法根据 Claims 结构体创建 Token 示例。
// param 1 jwt.SigningMethod {jwt.SigningMethodHS256, HS384, HS512}
// param 2 StandardClaim 自定义类型
/**
type StandardClaims struct {
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 f `json:"nbf,omitempty"`
Subject string `json:"sub,omitempty"`
}
*/
func (auth *AuthRoleClaims) GeneratorToken() string {
claims := &AuthRoleClaims{
v: auth.v,
StandardClaims: jwt.StandardClaims{
ExpiresAt: auth.Method.ExpiresAt,
Issuer: auth.Method.Issuer,
IssuedAt: time.Now().Unix(),
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
//编码为String
jwt, err := token.SignedString([]byte(auth.Method.key))
if err != nil {
panic(err)
}
return jwt
}
func (auth *JWTMethod) ValidatedToken(jwtToken string) (*jwt.Token, error) {
return jwt.Parse(jwtToken, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("Invalid token", token.Header)
}
return []byte(jwtToken), nil
})
}
//全局变量
var (
START_PORT = "8080"
)
type WebGroup struct {
Version uint
GroupPath string
}
type LoginInfo struct {
Phone string `json:"phone"`
Password string `json:"password"`
}
func ResponseStatus(status int) string {
switch status {
case 200:
return "Success"
case 400:
return "User operatal error"
case 500:
return "Server Error"
}
return ""
}
func LoginServer() gin.HandlerFunc {
return func(ctx *gin.Context) {
login := &LoginInfo{}
if err := ctx.ShouldBindJSON(login); err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{
"msg": ResponseStatus(http.StatusBadRequest)})
}
secret := os.Getenv("SECRET")
if secret == "" {
ctx.JSON(http.StatusInternalServerError,
errors.New("Error"))
}
claims := &AuthRoleClaims{
v: login.Phone,
Method: &JWTMethod{
ExpiresAt: time.Now().Add(time.Second * 5).Unix(),
Issuer: "user",
key: secret,
},
}
token := claims.GeneratorToken()
ctx.JSON(http.StatusOK, gin.H{
"jwtToken": token})
}
}
func RegisterServer() gin.HandlerFunc {
return func(ctx *gin.Context) {
register := &LoginInfo{}
if err := ctx.ShouldBindJSON(register); err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{
"msg": ResponseStatus(http.StatusBadRequest)})
}
if len(register.Phone) != 11 || len(register.Password) <= 6 {
ctx.JSON(http.StatusBadRequest, gin.H{
"msg": ResponseStatus(http.StatusBadRequest)})
}
ctx.JSON(http.StatusOK, gin.H{
"msg": ResponseStatus(http.StatusBadRequest)})
}
}
///AUTH FUNCTIONS
func InitializeAuthServer(engine *gin.Engine) {
group := &WebGroup{
Version: 1,
GroupPath: "/login",
}
v1 := engine.Group(group.GroupPath)
v1.POST("/login", LoginServer())
v1.POST("/registher", RegisterServer())
}
func SetupRouter() *gin.Engine {
//初始化Gin服务
server := gin.New()
//注册验证服务
InitializeAuthServer(server)
return server
}
func amain() {
router := SetupRouter()
router.Run(":" + START_PORT)
}