【Go】GraphQL の Mutation(新規登録)とパスワードハッシュ化
Mutation(新規登録)とパスワードハッシュ化して登録、ハッシュ値と平文の比較の実装。
開発環境
- go 1.17
- github.com/99designs/gqlgen v0.17.1
- github.com/graph-gophers/dataloader v5.0.0+incompatible
本文
Mutation(新規登録)
「ブログを新規登録する」というケースを題材として Mutation を作った。
完成品は下記。
https://gitlab.com/k1350/daybreak_sample/-/tree/graphql_mutation_sample
まず定義から。今回はまだ入力値のバリデーションは実装していない。
type Mutation {
createBlog(input: CreateBlogInput!): Blog
}
input CreateBlogInput {
author: String!
name: String!
description: String
publishOption: PublishOption!
password: String
links: [BlogLinkInput!]
}
input BlogLinkInput {
name: String
url: String!
}
Query の実装と比較し、特筆するのはトランザクション処理が必要なケース。
ブログ本体は blog というテーブルに、リンク(著者の Twitter アカウントとかをブログと紐づけるようなケースを想定)というのを blog_link というテーブルに格納しているので、ブログ新規登録時はトランザクション処理が必要となる。
ここ からトランザクション処理の実装となるが、基本的には
tx, err := db.Begin()
で begindefer
で panic が起こったらロールバックする処理を書いておくtx.Exec
というように、いつもdb
を使っている部分をtx
に置き換えて処理を書く- エラーが発生したら
tx.Rollback()
- エラーなく最後まで到達したら
tx.Commit()
となる。
ラッパー関数書いたほうが最終的には良さそうだが、とりあえず今は愚直に書いた。
パスワードハッシュ化
パスワードで閲覧制限されたブログというのを想定しているので、新規登録時にパスワードをハッシュ化して blog テーブルに入れる。
パスワードハッシュ化には bcrypt を使う。
ハッシュ化に使うのは GenerateFromPassword
で
hashed, err := bcrypt.GenerateFromPassword([]byte(*input.Password), 12)
こんな感じで使う。cost はデフォルトが 10 なので大体それくらいにしておいた。
戻り値は []byte
なので、文字列として扱うときはキャストする。
ハッシュ化されたパスワードと平文のパスワードを比較する場合、CompareHashAndPassword
を使う。
pErr := bcrypt.CompareHashAndPassword([]byte(password), []byte(*opts.Password))
switch {
case pErr == bcrypt.ErrMismatchedHashAndPassword:
// パスワードが一致しない
case err != nil:
// パスワードが一致しない以外のエラー
default:
// パスワード一致
}
こんな感じになる。
注意点として GenerateFromPassword
で生成したハッシュ値はソルトが付与されていて、同じ文字列をハッシュ化しても毎回違う値が生成される。
そのため平文のパスワードをハッシュ化して DB に保存してある値と一致するか調べてもダメで、DB から取り出したハッシュ値と平文を CompareHashAndPassword
で比較する必要がある。
以上