Skip to content

Commit

Permalink
go jwt example
Browse files Browse the repository at this point in the history
  • Loading branch information
fwqaaq committed Jul 1, 2024
1 parent 14b5922 commit eb19b7e
Show file tree
Hide file tree
Showing 8 changed files with 257 additions and 1 deletion.
1 change: 1 addition & 0 deletions http/go_example/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module grpc_example
go 1.21.3

require (
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
github.com/golang/protobuf v1.5.3 // indirect
golang.org/x/net v0.17.0 // indirect
golang.org/x/sys v0.13.0 // indirect
Expand Down
2 changes: 2 additions & 0 deletions http/go_example/go.sum
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
Expand Down
53 changes: 53 additions & 0 deletions http/go_example/jwt/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package main

import (
"grpc_example/jwt/mod"
"log"
"net/http"
)

// import (
// "fmt"
// "grpc_example/jwt/mod"
// "time"

// "github.com/golang-jwt/jwt/v5"
// )

// type MyClaims struct {
// Name string
// Gender int
// Age int
// jwt.RegisteredClaims
// }

// func main() {
// claims := MyClaims{
// Name: "Jeremy",
// Gender: 1,
// Age: 18,
// RegisteredClaims: jwt.RegisteredClaims{
// ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour)),
// IssuedAt: jwt.NewNumericDate(time.Now()),
// NotBefore: jwt.NewNumericDate(time.Now()),
// },
// }

// hs := mod.HS{
// Key: "fwqaaq",
// }

// sign, err := hs.Encode(claims)
// fmt.Println(sign, err)
// var outClaims MyClaims
// err = hs.Decode(sign, &outClaims)
// fmt.Println(outClaims, err)
// }

func main() {
http.HandleFunc("/signin", mod.Signin)
http.HandleFunc("/welcome", mod.Welcome)
http.HandleFunc("/refresh", mod.Refresh)

log.Fatal(http.ListenAndServe(":8080", nil))
}
48 changes: 48 additions & 0 deletions http/go_example/jwt/mod/mod.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package mod

import (
"github.com/golang-jwt/jwt/v5"
)

// Key struct
type Key struct {
Key []byte
}

type Vaildator interface {
Encode(claims jwt.Claims) (string, error)
Decode(sign string, claims jwt.Claims) error
}

func (k *Key) Encode(c jwt.Claims) (string, error) {
// Create the JWT token
token := jwt.NewWithClaims(jwt.SigningMethodHS256, c)
// Create a string from the token
sign, err := token.SignedString(k.Key)
return sign, err
}

func (k *Key) Decode(sign string, c jwt.Claims) error {
_, err := jwt.ParseWithClaims(sign, c, func(t *jwt.Token) (interface{}, error) {
return k.Key, nil
})
return err
}

var users = map[string]string{
"user": "password",
}

var jwtKey = Key{
Key: []byte("fwqaaq"),
}

type Credentials struct {
Username string `json:"username"`
Password string `json:"password"`
}

type Claims struct {
Username string `json:"username"`
jwt.RegisteredClaims
}
52 changes: 52 additions & 0 deletions http/go_example/jwt/mod/refresh.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package mod

import (
"net/http"
"time"

"github.com/golang-jwt/jwt/v5"
)

func Refresh(w http.ResponseWriter, r *http.Request) {
c, err := r.Cookie("token")
if err != nil {
if err == http.ErrNoCookie {
w.WriteHeader(http.StatusUnauthorized)
return
}
w.WriteHeader(http.StatusBadRequest)
return
}

token := c.Value
claims := &Claims{}

err = jwtKey.Decode(token, claims)
if err != nil {
if err == jwt.ErrSignatureInvalid {
w.WriteHeader(http.StatusUnauthorized)
return
}
w.WriteHeader(http.StatusBadRequest)
return
}

// Refresh the token when old token is going to expire(in the 30s before expiration)
if time.Until(claims.ExpiresAt.Time) < 30*time.Second {
w.WriteHeader(http.StatusAccepted)
return
}

expirationTime := time.Now().Add(5 * time.Minute)
claims.ExpiresAt = jwt.NewNumericDate(expirationTime)
tokenString, err := jwtKey.Encode(claims)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}
http.SetCookie(w, &http.Cookie{
Name: "token",
Value: tokenString,
Expires: expirationTime,
})
}
50 changes: 50 additions & 0 deletions http/go_example/jwt/mod/signin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package mod

import (
"encoding/json"
"net/http"
"time"

"github.com/golang-jwt/jwt/v5"
)

func Signin(w http.ResponseWriter, r *http.Request) {
var creds Credentials
// Decode the request body into the struct
err := json.NewDecoder(r.Body).Decode(&creds)

if err != nil {
w.WriteHeader(http.StatusBadRequest)
return
}

expectedPassword, ok := users[creds.Username]
if !ok || expectedPassword != creds.Password {
w.WriteHeader(http.StatusUnauthorized)
return
}

claims := &Claims{
Username: creds.Username,
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(time.Now().Add(5 * time.Minute)),
IssuedAt: jwt.NewNumericDate(time.Now()),
NotBefore: jwt.NewNumericDate(time.Now()),
},
}

tokenString, err := jwtKey.Encode(claims)

if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}

http.SetCookie(w, &http.Cookie{
Name: "token",
Value: tokenString,
Expires: time.Now().Add(5 * time.Minute),
})
json.NewEncoder(w).Encode(map[string]string{"message": "Successfully signed in"})

}
40 changes: 40 additions & 0 deletions http/go_example/jwt/mod/welcom.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package mod

import (
"net/http"

"github.com/golang-jwt/jwt/v5"
)

func Welcome(w http.ResponseWriter, r *http.Request) {
// Get the token from the cookie
c, err := r.Cookie("token")
if err != nil {
if err == http.ErrNoCookie {
// If the cookie is not set, return an unauthorized status
w.WriteHeader(http.StatusUnauthorized)
return
}
// Return bad request for other errors
w.WriteHeader(http.StatusBadRequest)
return
}

token := c.Value

claims := &Claims{}

err = jwtKey.Decode(token, claims)

if err != nil {
if err == jwt.ErrSignatureInvalid {
w.WriteHeader(http.StatusUnauthorized)
return
}
w.WriteHeader(http.StatusBadRequest)
return
}

w.Write([]byte("Welcome " + claims.Username))

}
12 changes: 11 additions & 1 deletion http/http_authorized.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,17 @@ Session:浏览器和服务器是在进行会话,然而比较模糊的就是

参考:<https://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html>

查看示例:[Go JWT 示例](./go_example/jwt/main.go)

```shell
# sign in
curl -v -X POST "http://localhost:8080/signin" -d '{"username": "user", "password": "password"}' --header "Content-Type: application/json"
# welcome
curl -b "cookie" http://localhost:8080/welcome
# refresh(在 30s 之内)
curl -v -b "cookie" http://localhost:8080/refresh
```

### 总结

1. Session 是由服务器诞生并且保存在服务器中的,由服务器主导
Expand All @@ -119,4 +130,3 @@ Session:浏览器和服务器是在进行会话,然而比较模糊的就是
如果在保存密码的时候是明文,那么撞库之后,用户密码信息将完全被破解。而当你使用 hash 运算,但是没有进行加密,那么攻击者在得知使用的是什么加密算法的情况下,使用**彩虹表**可以很快的破解密码。而加**之后,由于 hash 运算是单向的,彩虹表很难或者根本收集不到密码的内容,很难进行逆向解析。

参考:<https://zhuanlan.zhihu.com/p/20407064?theme=dark>

0 comments on commit eb19b7e

Please sign in to comment.