Golang | JWT身份认证
概述
最近要在http接口上加一个token认证,但是接口很多,有没有一个省时省力的办法来解决。token的使用流程是:
- 用户使用帐号密码登陆到服务器1. 服务器验证登陆成功,根据帐号密码生成token。把token返回给客户端
- 客户端请求接口时需要带上token。服务器需要验证token。
接口是用golang的
net/http
库写的。为了实现目的,还需要添加的功能是: - 生成和解析token的方法;
- 一个中间件,拦截请求,进行token认证,根据认证通是否过来决定是否继续执行请求;
JWT
身份认证可以解决第一个问题,mux.Router
的Use
方法函数可以作为一个中间件, 拦截请求。
func (r *Router) Use(mwf ...MiddlewareFunc) {
for _, fn := range mwf {
r.middlewares = append(r.middlewares, fn)
}
}
JWT 身份认证
下载地址:
github.com/dgrijalva/jwt-go"
实现的功能如下:
- token生成
- 中间件拦截请求进行token认证 结合代码:
// jwt_svr.go
package main
import (
"context"
"fmt"
"github.com/dgrijalva/jwt-go"
"github.com/dgrijalva/jwt-go/request"
"github.com/gorilla/mux"
"net/http"
"strings"
"time"
)
//自定义Claims结构体
type LoginClaims struct {
Uid string
Name string
Pwd string
LoginTime time.Duration
token string
jwt.StandardClaims
}
//var nowDate = time.Now().Format("2020-02-22 15")
var nowDate = "123123123"
var secretKey = fmt.Sprintf("%v%v",nowDate,"dingding")
//生成token
func Sign(uid, name, pwd string) (string, error) {
//开始生成token
now := time.Now()
exp := now.Add(time.Duration(2) * time.Minute) //2分钟后过期
claims := LoginClaims{
Uid: uid,
Name: name,
Pwd : pwd,
LoginTime : time.Duration(now.Unix()),
StandardClaims: jwt.StandardClaims{
ExpiresAt: exp.Unix(),
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString([]byte(secretKey))
}
//解析token
func ParseToken(token string, secret string) (*LoginClaims, error) {
loginclaims := new(LoginClaims)
claim, err := jwt.ParseWithClaims(token, loginclaims, func(token *jwt.Token) (interface{}, error) {
return []byte(secret), nil
})
if err != nil {
return loginclaims, err
}
if !claim.Valid {
return loginclaims, fmt.Errorf("claim.Valid error")
}
return loginclaims, nil
}
func stripBearerPrefixFromTokenString(tok string ) (string, error) {
if len(tok) > 4 && strings.ToUpper(tok[0:4]) == "JWT " {
return tok[4:], nil
}
return tok, nil
}
func main () {
r := mux.NewRouter()
//生成token
t, err := Sign("1", "zjjj", "123")
if nil != err {
fmt.Errorf(err.Error())
return
}
//解析token
l, err := ParseToken(t, secretKey)
if err != nil {
fmt.Printf(err.Error())
return
}
fmt.Printf("generate token: %s\n", t)
fmt.Printf("uid: %s\n", l.Uid)
//中间件 拦截请求,用来token认证。
r.Use(func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
mapClaims := new(LoginClaims) //自定义类型
var myToken string
// 如果token存在于Authorization中
token, err := request.ParseFromRequest(r, &request.PostExtractionFilter{
Extractor: request.HeaderExtractor{"Authorization"},
Filter: stripBearerPrefixFromTokenString,
}, func(token *jwt.Token) (interface{}, error) {
return []byte(secretKey), nil
}, request.WithClaims(mapClaims))
if !token.Valid {
// 如果token存在于header中
for k, v := range r.Header {
if strings.ToLower(k) == "token" {
myToken = v[0]
break
}
}
mapClaims, err = ParseToken(myToken, secretKey)
if err != nil {
w.Write([]byte("token invalid\n"))
return
}
}else {
//myToken = strings.Split(r.Header["Authorization"][0], " ")[1] //第二种获取Authorization的方式
fmt.Printf("id: %s\n", mapClaims.Uid)
}
//把解析好的token数据放到context中
longtext := fmt.Sprintf("解析好的tokne数据 id: %s, name: %s, pwd: %s\n", mapClaims.Uid, mapClaims.Name, mapClaims.Pwd)
w.Write([]byte(longtext))
ctx := context.WithValue(r.Context(), "id", mapClaims.Uid)
ctx = context.WithValue(ctx, "name", mapClaims.Name)
r = r.WithContext(ctx)
next.ServeHTTP(w, r)
})
})
r.HandleFunc("/test", func(w http.ResponseWriter, r *http.Request) {
fmt.Printf("handleFunc test\n")
w.Write([]byte("test is success"))
})
r.HandleFunc("/login", func(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
//fmt.Fprintln(w,r.Form)
name := r.FormValue("name")
pwd := r.FormValue("pwd")
fmt.Printf("login: name: %s, pwd: %s\n", name, pwd)
login, err := Sign("1", name, pwd)
if nil != err {
fmt.Errorf(err.Error())
w.Write([]byte(err.Error()))
return
}
w.Write([]byte(login))
})
fmt.Printf("Server listen on 8080...\n")
http.ListenAndServe(":8080", r)
}
验证一下, 启动程序:
$ go run jwt_svr.go
generate token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVaWQiOiIxIiwiTmFtZSI6InpqamoiLCJQd2QiOiIxMjMiLCJMb2dpblRpbWUiOjE2MTQxMzg1MzIsImV4cCI6MTYxNDEzODY1Mn0.kHZKlpZXY_qLFaOfr8g1i9uPssjZB3vZFrMqx8S0Ef4
uid: 1
Server listen on 8080...
已经监听8080端口,使用idea的rest client
来模拟请求,在Headers
中加入
Authorization:jwt eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVaWQiOiIxIiwiTmFtZSI6InpqamoiLCJQd2QiOiIxMjMiLCJMb2dpblRpbWUiOjE2MTQxMzg1MzIsImV4cCI6MTYxNDEzODY1Mn0.kHZKlpZXY_qLFaOfr8g1i9uPssjZB3vZFrMqx8S0Ef4
点击 rest client
的运行按钮,得到数据:
解析好的tokne数据 id: 1, name: zjjj, pwd: 123
test is success
第二种方法是请求login
接口生成token,参数 name=''
, pwd=''
, 生成token后再按照上述方法,请求test
接口
--完--
- 原文作者: 留白
- 原文链接: https://zfunnily.github.io/2021/02/JWT/
- 更新时间:2024-04-16 01:01:05
- 本文声明:转载请标记原文作者及链接