

皆さん、こんにちはこんばんは。
今回はAdonisJS v5のVol.1の続きを投稿していきます。
Vol.1ではログイン機能を実装する上で前準備の記事を投稿していますので、まだ前準備が済んでいない方はこちらから実践してみてください。
それではさっそく始めていきましょう!
バリデーション部分の実装
ログイン時とアカウント作成時のバリデーションを実装していきます。
まずは以下のファイルを変更していきましょう。
app/Validators/UserLoginValidator.ts
1 2 3 4 5 6 7 8 9 10 11 |
public schema = schema.create({ login_id: schema.string([ rules.required(), rules.alphaNum({allow: ['dash']}) ]), password: schema.string([ rules.required(), rules.maxLength(100), rules.alphaNum() ]) }) |
“public schema”の行をこのように変更しました。
続いて、以下のファイルも変更していきます。
app/Validators/UserRegisterValidator.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
import UserModel from 'App/Models/Users' // ←ここを追加 ~ public schema = schema.create({ username: schema.string([ rules.required(), rules.trim() ]), login_id: schema.string([ rules.required(), rules.trim(), rules.minLength(4), rules.maxLength(50), rules.alphaNum({allow: ['dash']}), rules.unique({ table: UserModel.table, column: 'login_id' }) ]), password: schema.string([ rules.required(), rules.trim(), rules.minLength(6), rules.maxLength(100), rules.alphaNum(), rules.confirmed('password_confirmation') ]), password_confirmation: schema.string([ rules.required() ]), emai: schema.string.nullableAndOptional([ rules.trim(), rules.email(), rules.unique({ table: UserModel.table, column: 'email' }) ]) }) |
これでログイン時とアカウント作成時のバリデーションの処理は完了しました。
バリデーションの内容を全て説明することは省きますが、特殊なバリデーションの箇所だけ説明したいと思います。
rules.unique()
このrulesに内包されている”unique”というメソッドは対象のカラムに対して受け取った値が既にデータベースに登録されている場合にエラーを出力してくれます。
“table”と”column”は対象になる箇所を指定し、”where”で条件を付与することもできます。
もし、エラーメッセージをカスタムしたいということであれば、以下のように対象のバリデーションファイルを変更することでエラーメッセージをカスタムすることができます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
public messages: CustomMessages = { 'username.required': 'ユーザー名は必須です。', 'login_id.required': 'ログインIDは必須です。', 'login_id.unique': 'このログインIDは利用できません。', 'login_id.alphaNum': '英数字、-、_のみ使用できます。', 'login_id.minLength': 'ログインIDは4文字以上です。', 'login_id.maxLength': 'ログインIDは50文字までです。', 'password.required': 'パスワードは必須です。', 'password.minLength': 'パスワードは6文字以上です。', 'password.maxLength': 'パスワードは50文字までです。', 'password.alphaNum': 'パスワードは英数字のみです。', 'password.confirmed': 'パスワードが一致しません。', 'password_confirmation.required': 'パスワード確認は必須です。', 'email.email': 'Emailを正しく入力してください。', 'email.unique': 'このEmailは利用できません。' } |
これでバリデーションの実装は完了しました。続いてモデルを作っていきましょう。
モデルを作っていく
それではUserのモデルの中身を作っていきます。
以下のファイルを変更していきます。
app/Models/Users.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
import Hash from '@ioc:Adonis/Core/Hash' // ← ここを追加 export default class User extends BaseModel { // ここから public static table = 'users' @column() public username: string @column() public login_id: string @column() public email: string | null @column({ serializeAs: null }) public password: string public static boot() { if(this.booted) { return } super.boot() this.before('create', async (inst) => { if(inst.$dirty.password) { inst.password = await Hash.make(inst.password) } }) } // ここまで追加 } |
カラムの指定とUserデータ作成時にパスワードをハッシュ化する処理を追加しています。カラムの指定をしておかないと、ユーザー作成時にプロパティにアクセスできずデータベースへの書き込みができなくなってしまうため、更新するカラムがある場合は必ず追加をしましょう。
また、”this.before()”というメソッドですが、今回SQLでcreate文を実行する前にコールバックされるように指定をし、パスワードをハッシュ化しています。
“password”のカラムに”{ serializeAs: null }”とオプションを追加しています。
これは、データベースからデータを取得した時にレスポンスとしてJSON形式で返却するんですが、オブジェクトデータからJSON形式に変換する際に”password”のカラムを除外することができます。
“password”はログイン認証の時にしか利用しないのと、レスポンスで返却する必要がないので特に理由がない限りはこのオプションを付与しておきましょう。
モデルの最低限の変更が完了したので、続いてコントローラーを作成していきます。
コントローラーを作っていく
ユーザー用のコントローラーはすでに作成済みですので、処理周りを作っていきます。
まずは、ユーザーを作成する処理になります。以下のファイルを変更していきます。
app/Controllers/Api/UsersController.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
import UserRegister from 'App/Validators/UserRegisterValidator' // ← ここを追加 import UserModel from 'App/Models/Users' // ← ここを追加 ~ public async register(ctx: HttpContextContract) { try { await ctx.request.validate(AuthRegister) } catch(error) { return ctx.response.status(400).json({ result: false, errors: error.messages }) } const user = await UserModel.create({ login_id: ctx.request.input('login_id'), password: ctx.request.input('password'), username: ctx.request.input('username'), email: ctx.request.input('email') }) return ctx.response.status(200).json({result: true}) } |
“register”というメソッドを追加しました。大まかにやることとしては、リクエストで受け取ったPOSTデータをチェックし、問題がなければユーザー登録をするという流れになります。
バリデーションのチェックはtry文で行い、”ctx.request.validate()”メソッドの第1引数に先ほど用意しておいたバリデーションクラスを渡してあげます。
エラーが発生した場合はcatch文が実行されるためエラー内容をレスポンスとして返却しています。
バリデーションに問題がなければ”UserModel.create”メソッドが実行されユーザーデータを作成することができます。
ここまでが登録処理の流れになります。
もし、ユーザー登録後ログイン状態を維持させたいということであれば、そのままセッションに保存させリダイレクトさせることもできます。
その際は是非ログイン処理を参考に組み込んでみてください。
それでは続いてログイン処理も作って行きましょう。
以下のファイルを変更していきます。
app/Controllers/Api/UsersController.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
import UserLogin from 'App/Validators/UserLoginValidator' // ←ここを追加 import Hash from '@ioc:Adonis/Core/Hash' // ← ここを追加 ~ public async login(ctx: HttpContextContract) { try{ await ctx.request.validate(UserLogin) } catch(error) { return ctx.response.status(400).json({ result: false, errors: error.messages }) } const user = await UserModel.query() .where('login_id', ctx.request.input('login_id')) .first() if(!user) return ctx.response.status(400).json({ result: false, errors: { system: 'ログインできません。' } }) if(!await Hash.verify(user.password, ctx.request.input('password'))) return ctx.response.status(400).json({ result: false, errors: { system: 'ログインできません。' } }) await ctx.session.put('user_id', user.id) return ctx.response.status(200).json({result: true}) } |
こちらが最低限のログイン処理になります。
最初にあるtry文のバリデーション部分は登録処理と同じことをしているので説明は省いていきます。
“UserModel.query()”メソッドでログインIDを条件に一致するユーザーデータを検索しています。
この時点でユーザーが取得できない場合は存在しないログインIDの値を受け取っているのでエラーとしてレスポンスを返却します。
ログインIDを条件にユーザーデータが見つかった場合は次にパスワードのチェックを行います。
パスワードはハッシュ化されたデータとしてデータベースに保存されているため、”Hash”モジュールを使って検証します。
もしパスワードが検証できなかった場合はエラーとしてレスポンスを返却します。
※ここで注意なのが、ログインIDやパスワードが間違っていた場合エラーメッセージとして”ログインIDが間違っています。”や”パスワードが間違っています。”と表示させないことです。
このようなメッセージを表示させてしまうと“ログインID”が存在し”パスワード”が違っていた、と存在するログインIDを晒してしまうことになるからです。
そうなると攻撃者は存在するログインIDに対して不正ログインを行うためにパスワード入力のアタックを仕掛けてくる可能性があるので、漏洩するようなメッセージ内容は控えるようにしましょう。
問題なくログイン認証が成功したらセッションデータにユーザーIDを保存をし成功したことをレスポンスとして返却してあげます。
後はログイン有無を確認するためにセッションから値を取り出して有効なデータであるかを確認できればアカウントの機能周りは実装していけると思います。
最後に
少し長くなってしまいましたが、AdonisJS v5でアカウント管理、ログイン機能を実装についていかがでしたでしょうか。
アカウント管理の機能やログイン機能はほとんどのアプリケーションで当たり前のように実装されています。
今回はそんなアプリケーションに必要不可欠な機能の実装をご紹介しました。
今後もAdonisJSで実装できる色々な機能をご紹介していきたいと思います。
それではよいAdonisライフを!