# Go Langで新規登録のWebAPIを作成したので、認証周りをJWTで作成する。

6 min read
Table of Contents

期待するもの

新規登録のAPIを作成したので、次はログイン機能を作成したい。

ログインは、メールアドレスとパスワードを送り一致したら、tokenを取得する。

で、tokenを使って認証付きのAPIを叩けるようになりたい。

tokenにはjwtを使用する。

jwtってなに?

これはどこかで調べてまとめる(気が向いた時)。

JWT認証を作成する

パッケージのインストール

github.com/dgrijalva/jwt-goパッケージの情報が多かったので、それに従うことにする。長い物には巻かれるべきで間違いない。

go get github.com/dgrijalva/jwt-go

パッケージをインストールする。

Tokenを生成する

まずjwt.goというファイルを作成する。

今回はauthフォルダの中に作成した。

それでは書いていく。

まずは、jwtを生成するためのシークレットキーを作成する。

var jwtKey = []byte("your_secret_key")

ここでは任意の文字列(今回は”your_secret_key”)をバイト列に変換し、jwtKeyに代入している。

バイト列というのがピンとこないので、

fmt.Printlnで書き出しみると、[121 111 117 114 95 115 101 99 114 101 116 95 107 101 121]となった。なんとなく理解した。

このようにして文字列をバイト列に変換することは、多くの場面でデータの操作や処理に利用されます。JWTの署名などのセキュリティ関連の操作では、シークレットキーをバイト列として扱うことが一般的です。

とのことでした。

次に、JWTのクレームを表す構造体であるClaimsを定義する。

これは任意のJSONデータっていう意味っぽい。

type Claims struct {
Name string `json:"name"`
jwt.StandardClaims
}

今回はnameを含めることにする。

と思ったけど、ユニークで必要がありそうなので、ユーザーIDにした。

下記のように変更する。

type Claims struct {
UserID uint `json:"user_id"`
jwt.StandardClaims
}

次に、Tokenを作成する関数を作成する。

コードの詳細はコメントアウトに書いた。

func generateToken(userId uint) (string, error) {
// トークンの有効期限を設定(この場合は15分)
expirationTime := time.Now().Add(15 * time.Minute)
// トークンのClaimsを構築
claims := &Claims{
UserID: userId, // ユーザーIDをトークンに含める
StandardClaims: jwt.StandardClaims{
ExpiresAt: expirationTime.Unix(), // トークンの有効期限を設定
},
}
// 新しいJWTトークンを作成
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
// トークンを署名して文字列に変換
tokenString, err := token.SignedString(jwtKey)
if err != nil {
return "", err
}
// : JWTトークンを文字列として表現したもの&エラーが発生しなかったことを示すための値を返却する
return tokenString, nil
}

これでTokenを作成することができた。

ログイン時にこのTokenを返却することにする。

Tokenを認証する

次に認証を行う関数を作成する。

func Authenticate(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// Authorizationヘッダーからトークン文字列を取得します。
tokenString := r.Header.Get("Authorization")
// トークンが存在しない場合は、Unauthorizedエラーを返して処理を中断します。
if tokenString == "" {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
// トークン文字列から"Bearer "を削除します。
tokenString = strings.Replace(tokenString, "Bearer ", "", 1)
// JWTトークンをパースし、クレーム(Claims)を含むトークンオブジェクトを取得します。
token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) {
return jwtKey, nil
})
// トークンのパースに失敗した場合は、Unauthorizedエラーを返して処理を中断します。
if err != nil {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
// トークンのクレームを取得し、有効であるかを検証します。
claims, ok := token.Claims.(*Claims)
if !ok || !token.Valid {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
// 認証されたユーザーの情報をログに出力します。
fmt.Printf("Authenticated user: %s\n", strconv.Itoa(int(claims.UserID)))
// 次のハンドラ関数を呼び出します。
next(w, r)
}
}

ちなみにこの関数の関数名が大文字始まりなのは下記の通り。

Go言語では、関数や変数が大文字で始まる場合には他のパッケージから参照可能(エクスポート可能)となりますが、小文字で始まる場合には同じパッケージ内からのみアクセス可能となります。

で、このコントローラーを下記のように噛ませる。

http.HandleFunc("/users", auth.Authenticate(FetchUsers))

これで認証が必要になった。

実際に叩くときは

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjo4LCJleHAiOjE3MDMwODUxNzR9.bOJ6jYFLr3BneqtEhQdFb9b2y3lAubm3MpqXbzVQzQY

こんな感じでヘッダーを持たせる必要がある。

これでJWT認証ができるようになった。

My avatar

Thanks for reading my blog post! Feel free to check out my other posts or contact me via the social links in the footer.


More Posts

Comments