[Golang]JWTを実装してみる

## 必要なライブラリ

```
go get github.com/dgrijalva/jwt-go
```

## 色々な動作確認

```golang
package main

import (
	"fmt"
	"time"

	"github.com/dgrijalva/jwt-go"
)

func CreateToken(secretKey string) (string, error) {
	claims := jwt.MapClaims{}
	claims["authorized"] = true
	claims["user_id"] = "sample"
	claims["exp"] = time.Now().Add(time.Second * 2).Unix() // 有効期限を1時間に設定

	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	return token.SignedString([]byte(secretKey))
}

func ValidateToken(tokenString string, secretKey string) (*jwt.Token, error) {
	token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
		if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
			return nil, fmt.Errorf("予期しない署名方法: %v", token.Header["alg"])
		}
		return []byte(secretKey), nil
	})

	if err != nil {
		return nil, err
	}

	return token, nil
}

func IsAuthorized(token *jwt.Token) bool {
	if _, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
		return true
	}
	return false
}

// falseの場合は期限が切れていない
func IsExpired(token *jwt.Token) bool {
	// トークンのクレームをMapClaimsとして取得し、トークンが有効であるか確認
	if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
		// `exp`クレームをUnixタイムスタンプとして取得
		if exp, ok := claims["exp"].(float64); ok {
			// 現在時刻と比較し、有効期限が切れているか確認
			return int64(exp) < time.Now().Unix()
		}
	}
	// 有効期限の情報がない場合は、期限切れとみなす
	return true
}

func GetUserID(token *jwt.Token) string {
	if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
		return claims["user_id"].(string)
	}
	return ""
}

func main() {

	secretKey := "abcdesfghijklmnopqrstuvwxyz"
	wrongKey := "abcdesfghijklmnopqrstuvwxy"
	tokenString, err := CreateToken(secretKey)
	if err != nil {
		fmt.Println(err)
	}
	fmt.Println(tokenString)

	token, err := ValidateToken(tokenString, secretKey)
	if err != nil {
		fmt.Println(err)
	}
	fmt.Println(token)

	// secretKeyが違う場合
	wrongToken, err := ValidateToken(tokenString, wrongKey)
	if err != nil {
		fmt.Println(err)
	}
	fmt.Println(wrongToken)

	isAuthorized := IsAuthorized(token)
	fmt.Println(isAuthorized)

	// 本当はこの処理は不要
	isExpired := IsExpired(token)
	fmt.Println(isExpired)

	// wait 2 seconds
	time.Sleep(5 * time.Second)

	wrongToken2, err := ValidateToken(tokenString, secretKey)
	if err != nil {
		fmt.Println("有効期限切れ")
		fmt.Println(err)
	}
	fmt.Println(wrongToken2)

	isExpired = IsExpired(token)
	fmt.Println(isExpired)

	userID := GetUserID(token)
	fmt.Println(userID)

}

```

## GolangでJWTをPythonで認証できるかやってみる

JWTの有効期限を1時間に変更する

```golang
func CreateToken(secretKey string) (string, error) {
	claims := jwt.MapClaims{}
	claims["authorized"] = true
	claims["user_id"] = "sample"
	claims["exp"] = time.Now().Add(time.Hour * 1).Unix() // 有効期限を1時間に設定

	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	return token.SignedString([]byte(secretKey))
}
```
HS256アルゴリズムを使用してJWTトークンを生成しているためPythonのように言語が違ってもトークンとシークレットキーがあればトークンをデコードできます。

```python
import jwt

# トークンをデコードする関数
def decode_token(token, secret_key):
    try:
        # トークンをデコード
        decoded = jwt.decode(token, secret_key, algorithms=["HS256"])
        return decoded
    except jwt.ExpiredSignatureError:
        # トークンの有効期限切れ
        return "Expired token."
    except jwt.InvalidTokenError:
        # トークンが無効
        return "Invalid token."

# トークンと秘密鍵
token = ""  # ここにGo言語のコードで生成されたトークンを入れる
secret_key = "abcdesfghijklmnopqrstuvwxyz"  # ここに秘密鍵を入れる

# トークンをデコード
decoded_token = decode_token(token, secret_key)
print(decoded_token)


```