今、「ウェブサイト制作をぐっと楽にするウェブサービス」を制作している。
ナウいウェブサービスを目指しているので、ログインフォームやアカウント作成フォームにはreCAPTCHAを設置して、ロボットによる不正なアクセスから防御したい。
今回、初めてreCAPTCHAを使うので、ざっと概要を確認してみた。
- バージョンが1~3まである
- クライアントサイドとサーバサイドの両方で処理が必要
- クライアントサイドでtokenを取得
- サーバサイドでtokenをAPIに送信して結果を取得する
バージョンの違いについては、以下のとおり。
- バージョン1
画像に埋め込まれた文字をタイプするやつ(面倒くさい) - バージョン2
ブロック状に並んだ画像から、提示された条件を満たす画像を選択するやつ(画像によっては選択しづらい) - バージョン3 – New!
ユーザーの行動パターンを捕捉してAIで自動判定するナウいやつ。ユーザーは何もしなくてよい!
1と2については、イライラさせられた経験を誰もがお持ちだと思う。とてもナウいとは言えない。当然、ユーザーが何もしなくてよいバージョン3を採用。
サーバーサイドで返却されるjsonデータに、scoreという0~1の数値を返すパラメータが含まれており、この数値を見て、人間なのかロボットなのかを判断してねという仕組みらしい。1に近いほど人間である可能性が高いけれど、どこを閾値にするかは、サービスの性質に合わせて各自判断してね、目安としては、0.5以上であれば概ね人間だと思うよ、ということみたい。
ここまで把握できたら、粛々と作業を開始しよう。
サイトキーとシークレットキーを取得
まずは、以下のサイトから、サイトキーとシークレットキーを取得しておく。利用するドメインを登録する際に、開発用にlocalhostも追加しておこう。
クライアントサイド
クライアントサイドについては、 npmにいろいろなライブラリが登録されていたけど、ここは素直に @nuxtjs チームの @nuxtjs/recaptcha モジュールを使えば良さそう。
npm i @nuxtjs/recaptcha
続いて、nuxt.config.jsに設定を書いていく。複数のページで使用する予定だけど、設定が一箇所で済むので助かる。Nuxtは本当に便利で好き。
{
modules: [
'@nuxtjs/recaptcha',
],
recaptcha: {
hideBadge: true, // Hide badge element
siteKey: 'xxxxxxxx site key xxxxxxx', // Site key for requests
version: 3 // Version
},
}
@nuxtjs/recaptchaモジュールのgithubにサンプルが用意されているので、それを参考にコーディングしていく。
- mountedでモジュールを初期化
- onSubmitでtokenを取得して、認証データにtokenを添えてサーバに送信
export default {
...
...
async mounted() {
await this.$recaptcha.init()
},
methods: {
async onSubmit() {
try {
const token = await this.$recaptcha.execute('login')
// ログイン処理
... tokenをログイン情報と共にサーバに送信する
...
} catch (error) {
console.log('Login error:', error)
}
}
}
}
tokenは取得から2分で無効になってしまうので、使用する直前に取得しなくてはならない。onSubmitで取得すると、レスポンス的にどうかと心配したけど、特に問題なさそうなので、onSubmitで取得する方法でいくことに。
また「こいつロボットくさいぞ」と判定された場合の処理も追加する必要がある。結果はサーバサイドで取得するので、サーバからの戻り値を見て、何らかのメッセージを表示するようにしたい。その点を心にしっかり留めて、サーバサイドのコーディングに進もう。
サーバサイド
今回のウェブサービスでは、サーバサイドは nodo.js + express を利用したログインのコードがすでに設置済みなので、それにreCAPTCHAの処理を追加していく。
ログインのミドルウェアに、reCAPTCHAのコードを追加していく。
// expressの初期化等は省略
app.post('/auth/login', async (req, res, next) => {
// reCAPTCHAの処理
// トークンが送信されているかチェックする
if (!req.body.recaptchaToken) {
return next({ code: 400, msg: 'recaptchaToken is required' })
}
const options = {
url: 'https://www.google.com/recaptcha/api/siteverify',
method: 'POST',
form: {
secret: CAPTCHA_SECRET,
response: req.body.recaptchaToken
},
json: true
}
request(options, (error, response, body) => {
if (error) {
return next({ code: 500, msg: 'recaptcha api error' })
}
// 閾値により判定する
if (body.score < 0.5) {
return next({ code: 403, msg: '正しい手順で操作されませんでした。' })
}
// 以下でログイン処理を行う
...
...
})
})
// エラー処理
app.use((err, _req, res, _next) => {
res.status(err.code || 500).send(err.msg || 'Internal Server Error')
})
既存のコードを書き換えることなく追加できた。素晴らしい。
再びクライアントサイド
上記のコードの場合、クライアントサイドでは、response.dataでエラーメッセージを受け取れるので、例外処理の部分を以下のようにすればOK。
} catch (error) {
this.errorMsg = error.response ? error.response.data : 'ログインに失敗しました!'
}
完成!
思いのほか簡単にナウいログインフォームが完成した。
問合せフォームや新規アカウント申込フォームなどにも、同様の方法で導入していこう。
ホームページの制作(デザイン・コーディング・プログラミング)に加え、ネットショップの販売促進に関するアドバイスも致します。 20年以上に渡って実際にネットショップを運営した経験を生かし、聞きかじりではない実績を伴ったノウハウを、自分の言葉でお伝えいたします。 プログラミングの経験は30年以上。HTML、CSS、JavaScript、TypeScript、Vue、NuxtJS、Node.js、MySQL、MongoDB、Elasticsearch、WordPress、PHP、Docker、Electronなど、幅広く対応します。
コメント