目次
バックグラウンドサービスでは、ユーザー認証がポイントとなります. ほとんどの要件はユーザー(トークン)を中心に設計されているため、ユーザー認証も注意が必要なコースです. この章では、egg-jwtを使用して実現します.ユーザー認証用ミドルウェア
ユーザー認証とは
Baidu Encyclopedia の「ユーザー認証」の定義を引用します。
ユーザー認証。通信ネットワークでサービス プロバイダーからのサービスにアクセスしようとするユーザーを認証する方法。ユーザーが DSMP にログインするか、データ サービスを使用すると、サービス ゲートウェイまたはポータルはこのメッセージを DSMP に送信して、ユーザーによるデータ サービスの使用の合法性と有効性 (ステータスがアクティブ化されているかどうか) を確認します。
複雑なものを単純化します。簡単に言うと、認証とは、ユーザーが Web ページまたは Web サイトを閲覧するApp
ときに。
次の 4 つの認証メカニズムがあります。
-
HTTP 基本認証
-
セッションクッキー
-
トークン トークン
-
OAuth (オープン認証)
一般的に言えば、私たちのバックグラウンドのほとんどはトークン トークン モデルを使用しており、トークンは Web ページ、クライアント、アプレット、ブラウザ プラグインなどの分野でも使用できます。Cookie 認証を使用する場合、クライアントおよびアプレットでこのインターフェースを使用することはできません。ドメインの概念がないからです。クッキーは特定のドメインに存在します。
登録インターフェース
バックグラウンドまたは小さなプログラムを開発したことがある人は、登録インターフェースの一般的なプロセスが次のようになっていることを知っているでしょう。
ページを開きます - ユーザー名とパスワードを入力します - 登録をクリックします - サーバーは受信後に重複があるかどうかを判断します - 重複がない場合はユーザー名とパスワードをユーザーテーブルに保存します - 登録成功メッセージを返します
まず、新しいeggプロジェクトを作成する必要があります。覚えていない場合は、最初の章を読むことができます。
次に、mysql プラグインを構成する必要があります.config/config.default.js で、データベースを独自のデータベースと対応するユーザー情報テーブルに変更します。
ここでのユーザー テーブルは user と呼ばれ、テーブルの内容は次のとおりです。
その他のプロジェクト構成は前の章と似ているため、忘れた場合は忘れずに確認してください。ここではあまり言いません。
mysql を設定する前に、egg-mtsql プラグインを忘れずにダウンロードしてください。
正式に書かれた
冒頭と同じように、登録時にフロントエンドがユーザー名とパスワードをバックグラウンドに渡すことがわかっているので、サーバーでこれら2つの値を取得する必要があります
-
関連するコードを記述するために、controller ディレクトリに新しい user.js を作成する必要があります。
// controller/user.js
const Controller = require('egg').Controller;
class UserController extends Controller {
async register() {
const { ctx } = this;
const { username, password } = ctx.request.body; // 获取注册需要的参数
}
}
module.exports = UserController;
ユーザー名とパスワードが空かどうかを判断する
if (!username || !password) {
ctx.body = {
code: 500,
msg: '账号密码不能为空',
data: null
}
return
}
-
同名かどうかの判定では、同名判定は2段階に分かれています。
最初のステップは、サービスを作成することです。これは、そのような値があるかどうかを確認するためにインターフェースをクエリする必要があるためです。
2つ目は、判断に使うコントローラーについてです。
// service/user.js
const Service = require('egg').Service;
class UserService extends Service {
// 通过用户名获取用户信息
async getUserByName(username) {
const { app } = this;
try {
const result = await app.mysql.get('user', { username });
return result;
} catch (error) {
console.log(error);
return null;
}
}
}
module.exports = UserService;
// controller/user.js
async register() {
...
// 验证数据库内是否已经有该账户名
const userInfo = await ctx.service.user.getUserByName(username) // 获取用户信息
// 判断是否已经存在
if (userInfo && userInfo.id) {
ctx.body = {
code: 500,
msg: '账户名已被注册,请重新输入',
data: null
}
return
}
}
2 層の判断の後、ユーザー名とパスワードをデータベースに挿入できます。
// controller/user.js
const defaultAvatar = '默认图片地址'
// 调用 service 方法,将数据存入数据库。
const result = await ctx.service.user.register({
username,
password,
signature: '个性签名',
avatar: defaultAvatar,
ctime: new Date(),
});
if (result) {
ctx.body = {
code: 200,
msg: '注册成功',
data: null
}
} else {
ctx.body = {
code: 500,
msg: '注册失败',
data: null
}
}
-
付帯サービスインサートデータベース方式
async register(params) {
const { app } = this;
try {
const result = await app.mysql.insert('user', params);
return result;
} catch (error) {
console.log(error);
return null;
}
}
-
要求インターフェイス アドレス、ルーターを忘れずに書き込んでください。
router.post('/api/user/register', controller.user.register);
上記を完了したら、postman を使用して試すことができます
インターフェイスを呼び出すと、成功が表示されます。データベースをもう一度見てみましょう
も挿入。
同時に、前の 2 つの判断を試すことができます。
最初の判断は、ユーザー名とパスワードのいずれかが空であることです
パスワードを空に設定した後、戻り値は正しいです。
降りて、ユーザー名の重複問題を判断してください。
同じユーザー名のデータは以前に挿入されているため、再度挿入すると、既に存在することが示されます。2回の判定終了。
登録インターフェースが終了します。
ログイン インターフェイス
登録が完了したら, ユーザーは成功したユーザー名とパスワードでログインする必要があります, これが私たちのログインプロセスです. ログインが成功すると, トークンが返されます. このトークンのために, Egg-jwt プラグインを組み合わせて追加します.私たち自身の定義された暗号化された文字列の生成。
一般的に言えば、フロントエンドがトークンを取得した後、トークンはフロントエンド (ブラウザのローカル) に保存されますが、有効期限が必要です. 通常、有効期限は 24 分に設定されています. そうでない場合一部の情報に敏感なウェブサイトやアプリでは、より長い時間設定できます。
トークンとは、リクエストが行われるたびに、データを取得するかデータを送信するかに関係なく、このリクエストがどのユーザーの行動に対するものであるかを識別するためにトークンを持ってくる必要があるということです。
Egg-jwt の特徴は、トークンを暗号化して生成できることと、トークンを復号化してユーザー情報を取得できることです。トークンは、おそらくログインしているユーザーの基本情報です。
たとえば、ログイン ユーザーのユーザー名とパスワードは次のとおりです。
{
userName:'赵小左',
password: '123',
}
次に、トークンには上記のユーザー情報が含まれ、復号化はユーザー名が Zhao Xiaozuo と等しいという基本情報に自然に対応します。
Egg-jwt をインストールする
npm install egg-jwt -S
卵にプラグインを追加する
// config/plugin.js
jwt: {
enable: true,
package: 'egg-jwt'
}
カスタム暗号化文字列を構成する
// config/config.default.js
config.jwt = {
secret: 'Nick',
};
secret
暗号化された文字列は、後でユーザー情報を組み合わせて文字列を生成するために使用されますtoken
。secret
サーバーコードに配置されており、一般のユーザーはブラウザから見つけることができないため、漏洩してはなりません。悪意のある人に使用される可能性があります。
リクエストレスポンスのログイン方法を追加
ログイン時の判定条件は2つあります。
1つ目は、ユーザー名が存在するかどうかです
2つ目はパスワードが正しいかどうか
// controller/user.js
async login() {
// app 为全局属性,相当于所有的插件方法都植入到了 app 对象。
const { ctx, app } = this;
const { username, password } = ctx.request.body
// 根据用户名,在数据库查找相对应的id操作
const userInfo = await ctx.service.user.getUserByName(username)
// 没找到说明没有该用户
if (!userInfo || !userInfo.id) {
ctx.body = {
code: 500,
msg: '账号不存在',
data: null
}
return
}
// 找到用户,并且判断输入密码与数据库中用户密码。
if (userInfo && password != userInfo.password) {
ctx.body = {
code: 500,
msg: '账号密码错误',
data: null
}
return
}
}
判定後、正式ログイン開始
async login () {
...
// 生成 token 加盐
// app.jwt.sign 方法接受两个参数,第一个为对象,对象内是需要加密的内容;第二个是加密字符串,上文已经提到过。
const token = app.jwt.sign({
id: userInfo.id,
username: userInfo.username,
exp: Math.floor(Date.now() / 1000) + (24 * 60 * 60) // token 有效期为 24 小时
}, app.config.jwt.secret);
ctx.body = {
code: 200,
message: '登录成功',
data: {
token
},
};
}
ここで app.jwt.sign を使用する理由は、app がグローバル コンテキストに精通しているからです.config のプラグインと config.default.js の属性は、app.xxx と app.config.xx を介して取得できます。
ログインすると、userinfo の id 属性と username 属性を、jwt の sign メソッドを介して暗号化された jwt.secret 文字列と組み合わせて、非常に長い文字列になるトークンを生成します。
上記を完了したら、ルーターにログイン インターフェイス API をスローできます。
router.post('/api/user/login', controller.user.login);
郵便配達員を開いて電話して試すことができます
最初のステップは先ほどと同じですが、まず 2 つの判定を検証してみましょう。
-
ユーザー名が存在しないと判断(間違ったユーザー名を入力)
-
確認に合格しました
-
パスワードエラー判定(間違ったパスワードを入力)
も確認済み。次に、テスト用の公式アカウントのパスワードを入力します
正式なユーザー名とパスワードを入力し、トークンを返し、システムのログインに成功し、インターフェースの検証に合格します。
jwt 復号化トークン
上記では、login を使用してトークンの生成を完了しました。現時点で、ログインにトークンが必要なインターフェースがある場合、ログイン後にトークンを解析してユーザー情報を取得するにはどうすればよいでしょうか?
テストと呼ぶことができる新しいメソッドをコントローラーに追加します
// controller/user.js
async test() {
const {ctx,app} = this;
const token = ctx.request.header.token; // 获取header 的token
const decode = app.config.jwt.verify(token, app.config.jwt.secret);
ctx.body = {
code: 200,
msg: '成功',
data:{
...decode
}
}
}
ルーターメソッドを追加することを忘れないでください
router.get('/api/user/getinfo', controller.user.text)
最後に、郵便配達員を使用して呼び出します
完全に明らかにしました。
認証ミドルウェアの実装
上記では、トークンの実現と解析に成功しました。
もちろん、必要な認証の 1 つは、トークンを必要とするすべてのインターフェイスに認証が必要であることです。
以前のように厳密に if else を使用することはできません。このように、書いた数のインターフェースを判断しなければなりません。このように、1つ目は硬直性であり、私たちのインターフェースに書かれているのと同じくらい多くの判断トークンです。2 つ目は、拡張が容易ではないことです.トークンが後で変更された場合、すべてのインターフェイスを変更する必要がありますか?
じゃあどうすればいいの?認証ミドルウェアを実装します。インターフェイスが要求されるたびに、最初にミドルウェアによって判断されます
新しいミドルウェア
最初に、プロジェクトの app の下に新しいフォルダー middleware を作成し、その下に jwtErr.js ファイルを追加します。
次に、jwtErr.js に判定メソッドを追加します。
module.exports = (secret) => {
return async function jwtErr(ctx, next) {
const token = ctx.request.header.token; // 若是没有 token,返回的是 null 字符串
let decode
if(token != 'null' && token) {
try {
decode = ctx.app.jwt.verify(token, secret); // 验证token
await next();
} catch (error) {
console.log('error', error)
ctx.status = 200;
ctx.body = {
msg: 'token已过期,请重新登录',
code: 401,
}
return;
}
} else {
ctx.status = 200;
ctx.body = {
code: 401,
msg: 'token不存在',
};
return;
}
}
}
まず、ミドルウェアはデフォルトで関数をスローし、非同期メソッドを返しますjwtErr
.jewErr
メソッドにはコンテキストctx
である、ctx
グローバル オブジェクトは で取得できますapp
。
まず、token ctx.app.jwt.verify token await next() token`の場合はctx.request.header.token 获取到请求头中的
token 属性,它便是我们请求接口是携带的
token 值,如果没有携带
nullを渡します。,该值为字符串
。我们通过
语句判断如果有
的情况下,使用
方法验证该
是否存在并且有效,如果是存在且有效,则通过验证
继续执行后续的接口逻辑。否则判断是失效还是不存在该
次に、ルーターで構成します
// app/router.js
module.exports = app => {
const { router,controller,middleware } = app;
const _jwt = middleware.jwtErr(app.config.jwt.secret); 传入加密字符串
router.post('/api/user/getinfo', _jwt, controller.user.test ) // / 放入第二个参数,作为中间件过滤项。判断第二个参数的接口是否有jwt。可以在进入接口逻辑之前就进行判断
}
郵便配達員で実行してみることができます
-
まずはtokenでインターフェースを真似る
正しく表示されました。次に、トークンなしのインターフェイスを模倣してみましょう
表示も正しい。したがって、認証はそれが何であるかです。ルーターに 2 番目のパラメーターを追加し、_jwt
メソッドを、インターフェイス ロジックに入る前にユーザーのアクセス許可を判断できるようにします。jwt判定が必要な後続のインターフェースは上記のとおりです。