JSON Web Token(JWT)是目前最流行的跨域身份验证解决方案。简单说,OAuth 就是一种受权机制。数据的全部者告诉系统,赞成受权第三方应用进入系统,获取这些数据。系统从而产生一个短时间的进入令(token),用来代替密码,供第三方应用使用。。git
传统的受权认证方式,须要持久化session数据,写入数据库或文件持久层等,且受权校验依赖于数据持久层。 这样的方式,对于结构维护成本大
,实现单点登陆较为复杂
,且没有分布式架构,没法支持横向扩展
,风险较大
(若是持久层失败,整个认证体系都会挂掉)。github
JWT则无须持久化会话数据,是以加密签名的方式实现了用户身份认证受权,很好的解决了跨域身份验证
,分布式session共享
、单点登陆
和横向扩展
等问题。web
JWT由三部分组成,即头部
、负载
与签名
。Token格式为 token=头部+'.'+载荷+'.'+签名
。算法
{"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1Nzg0MDQ5OTUsImlhdCI6MTU3ODQwMTM5NX0.waG8rvOZLM2pKDeKg7frMKlV8lAty1Og5LDjrVMJRsI"}
1. Header: 用于说明签名的加密算法等,下面类型的json通过base64编码后获得JWT头部。数据库
{ "typ": "JWT", "alg": "HS256" }
2. Payload: 标准定义了7个字段,载荷json通过base64编码后获得JWT的载荷。json
{ iss (issuer):签发人 exp (expiration time):过时时间 sub (subject):主题 aud (audience):受众 nbf (Not Before):生效时间 iat (Issued At):签发时间 jti (JWT ID):编号 }
示例:跨域
{ "sub": "1", "iss": "http://localhost:8000/user/sign_up", "iat": 1451888119, "exp": 1454516119, "nbf": 1451888119, "jti": "37c107e4609ddbcc9c096ea5ee76c667" }
3.Signature: 将头部和载荷用'.'号链接,再加上一串密钥,通过头部声明的加密算法加密后获得签名。服务器
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret )
1. 数据结构session
// A JWT Token. Different fields will be used depending on whether you're // creating or parsing/verifying a token. type Token struct { Raw string // The raw token. Populated when you Parse a token Method SigningMethod // The signing method used or to be used Header map[string]interface{} // (头部)The first segment of the token Claims Claims // (负载)The second segment of the token Signature string // (签名)The third segment of the token. Populated when you Parse a token Valid bool // Is the token valid? Populated when you Parse/Verify a token }
// Structured version of Claims Section, as referenced at // https://tools.ietf.org/html/rfc7519#section-4.1 // See examples for how to use this with your own claim types type StandardClaims struct { Id string `json:"jti,omitempty"` //编号 Subject string `json:"sub,omitempty"` //主题 Issuer string `json:"iss,omitempty"` //签发人 Audience string `json:"aud,omitempty"` //受众 ExpiresAt int64 `json:"exp,omitempty"` //过时时间 IssuedAt int64 `json:"iat,omitempty"` //签发时间 NotBefore int64 `json:"nbf,omitempty"` //生效时间 }
2. 生成Token
var( key []byte = []byte("This is secret!") ) // 产生json web token func GenToken() string { claims := &jwt.StandardClaims{ NotBefore: int64(time.Now().Unix()), ExpiresAt: int64(time.Now().Unix() + 1000), Issuer: "Bitch", } token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) ss, err := token.SignedString(key) if err != nil { logs.Error(err) return "" } return ss }
3. 校验Token
// 校验token是否有效 func CheckToken(token string) bool { _, err := jwt.Parse(token, func(*jwt.Token) (interface{}, error) { return key, nil }) if err != nil { fmt.Println("parase with claims failed.", err) return false } return true }
handler/auth.go
package handler import ( "fmt" "github.com/dgrijalva/jwt-go" "github.com/dgrijalva/jwt-go/request" "net/http" ) const ( SecretKey = "ODcyNDYsIMzY0N" ) func ValidateTokenMiddleware(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) { token, err := request.ParseFromRequest(r, request.AuthorizationHeaderExtractor, func(token *jwt.Token) (interface{}, error) { return []byte(SecretKey), nil }) if err == nil { if token.Valid { next(w, r) } else { w.WriteHeader(http.StatusUnauthorized) fmt.Fprint(w, "Token is not valid") } } else { w.WriteHeader(http.StatusUnauthorized) fmt.Fprint(w, "Unauthorized access to this resource") } }
handler/account.go
package handler import ( "encoding/json" "fmt" "github.com/dgrijalva/jwt-go" "log" "net/http" "strings" "time" ) func fatal(err error) { if err != nil { log.Fatal(err) } } type UserCredentials struct { Username string `json:"username"` Password string `json:"password"` } type User struct { ID int `json:"id"` Name string `json:"name"` Username string `json:"username"` Password string `json:"password"` } type Response struct { Data string `json:"data"` } type Token struct { Token string `json:"token"` } func LoginHandler(w http.ResponseWriter, r *http.Request) { var user UserCredentials err := json.NewDecoder(r.Body).Decode(&user) if err != nil { w.WriteHeader(http.StatusForbidden) fmt.Fprint(w, "Error in request") return } if strings.ToLower(user.Username) != "admin" { if user.Password != "123456" { w.WriteHeader(http.StatusForbidden) fmt.Fprint(w, "Invalid credentials") return } } // 建立Token token := jwt.New(jwt.SigningMethodHS256) claims := make(jwt.MapClaims) claims["exp"] = time.Now().Add(time.Hour * time.Duration(1)).Unix() claims["iat"] = time.Now().Unix() token.Claims = claims //if err != nil { // w.WriteHeader(http.StatusInternalServerError) // fmt.Fprintln(w, "Error extracting the key") // fatal(err) //} tokenString, err := token.SignedString([]byte(SecretKey)) if err != nil { w.WriteHeader(http.StatusInternalServerError) fmt.Fprintln(w, "Error while signing the token") fatal(err) } response := Token{tokenString} JsonResponse(response, w) } func FundHandler(w http.ResponseWriter, r *http.Request) { response := Response{"帐户余额:1000W"} JsonResponse(response, w) } func JsonResponse(response interface{}, w http.ResponseWriter) { obj, err := json.Marshal(response) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.WriteHeader(http.StatusOK) w.Header().Set("Content-Type", "application/json") w.Write(obj) }
main.go
import ( "github.com/codegangsta/negroni" "log" "net/http" "proxy/handler" ) func Server() { http.HandleFunc("/login", handler.LoginHandler) http.Handle("/data", negroni.New( negroni.HandlerFunc(handler.ValidateTokenMiddleware), //中间件 negroni.Wrap(http.HandlerFunc(handler.FundHandler)), )) log.Println("服务已启动...") http.ListenAndServe(":8080", nil) } func main() { Server() }
客户端收到服务器返回的 JWT,能够储存在 Cookie
里面,也能够储存在 localStorage
。
此后,客户端每次与服务器通讯,都要带上这个 JWT。你能够把它放在 Cookie 里面自动发送,可是这样不能跨域
。因此更好的作法是放在HTTP请求的头信息Authorization
字段里面(或放在POST 请求的数据体里面)。
Authorization: Bearer <token>
参考:
http://www.ruanyifeng.com/blog/2019/04/oauth_design.html关于OAuth2.0
http://www.javashuo.com/article/p-fpklooxc-no.html
https://blog.csdn.net/idwtwt/article/details/80865209
https://baijiahao.baidu.com/s?id=1608021814182894637&wfr=spider&for=pc
http://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html