KOA(2)の - 認証(クッキー/セッション、トークン、およびOAuthの)

  以前は未分離前端と後端は、ページがバックグラウンドでレンダリングされ、ページに直接アクセスすることができた背景ロジックによって決定されます。前端と後端を分離した後、ページ要素がページ自体によって制御され、フロントエンドによって制御されるページ間のルーティング。もちろん、行う唯一のフロントエンドアクセス制御が十分ではありません、まだ各インターフェイスの背景を検証するために行われる必要があります。
  なぜ、フロントエンドのアクセス制御は、それの十分ではないのですか?ルートは、ページまたはボタンを非表示にすることができ、視覚的コントロール、フロントエンドの制御の先端のみであるため、要求を送信したが、まだ多くありますが、あなたはリクエストを送信するための操作ページをスキップすることができます。制御権限のフロントは非常にタイトやることがあっても、背景はまだ各インターフェースことを確認する必要があります。
  制御権の前端は主に3つである:経路制御(ルーティングジャンプ)、制御ビュー(ボタンレベル)と制御要求(リクエストインターセプタ)。いくつかの方法で、この後、次に詳細に入る、フロント仕上げアクセス制御は、まだ認証された各インターフェースの背景を確認する必要があります。今のフロントとリアエンドの認証方法で、主に以下のとおりです。

  1. セッションクッキー
  2. トークン認証(JWT)
  3. OAuthの(オープン認証)

セッションクッキー

クッキー

  HTTPプロトコルはステートレスなプロトコルであり、サーバがそれにアクセスするために、どのブラウザ最後に知っており、したがって、異なるブラウザ間で区別するために、サーバーのIDを必要としません。管理サーバーとクライアント間の状態を識別したクッキー。
  クッキー原理が初めてサーバがブラウザに、サーバー・セットSet-Cookieヘッダーフィールドは応答し、ブラウザは、応答を受信する要求を送信することであるとクッキーが設定される記憶、次回ブラウザがサーバに要求を送信し、自動的にリクエストヘッダにクッキーフィールドをもたらすでしょう、サーバーはクッキーが異なるブラウザ間で区別するために使用されている受信します。もちろん、ユーザとサーバの間で、このクッキーの対応は、最初の訪問であるはずです、あなたは、セッションを必要とします。

const http = require('http')
http.createServer((req, res) => {
  if (req.url === '/favicon.ico') {
    return
  } else {
    res.setHeader('Set-Cookie', 'name=zhunny')
    res.end('Hello Cookie')
  }
}).listen(3000) 
复制代码

セッション

  セッションは、セッションは、サーバーへのブラウザの最初の訪問は、サーバは、セッションを作成するセッションでブラウザを識別する情報を保存することを意味しています。これは、セッションが、サーバ側でキャッシュされているクッキーをクライアントにキャッシュされ、それらによって生成されたサーバー、ステートレスなHTTPプロトコルの欠陥を補うためには、クッキーとは異なります。

セッション・クッキー認証

  1. サーバーとのインタビューで、サーバ側でseesion、その後、seesionを(我々はメモリに保存することができseesionは、また、後者を使用することが推奨され、Redisの中に保存することができます)を保存するために、クライアントの最初の訪問を作成し、その後、このセッションは、固有の識別文字を生成しますストリング、応答ヘッダ内の一意の識別ストリングのこの種。
  2. 署名。クライアントを回避することによって、このステップのSID署名プロセス上のキーは、SIDを変更します。(非本質的なステップ)
  3. ブラウザが応答ヘッダを解析し、ローカルのクッキーにSIDを節約する要求応答を受信した場合、ブラウザのリクエストヘッダの次のHTTPリクエストは、このドメインのクッキー情報をもたらすでしょう。
  4. サーバーとのインタビューでクッキーsidのリクエストヘッダを解析し、このSIDに基づいてサーバ側に保存されているクライアントのセッションに移動し、その要求が正当なものであるかどうかを判断するために、クライアントを要求します。
const http = require('http')
//此时session存在内存中
const session = {}
http.createServer((req, res) => {
  const sessionKey = 'sid'
  if (req.url === '/favicon.ico') {
    return
  } else {
    const cookie = req.headers.cookie
    //再次访问,对sid请求进行认证
    if (cookie && cookie.indexOf(sessionKey) > -1) {
      res.end('Come Back')
    }
    //首次访问,生成sid,保存在服务器端
    else {
      const sid = (Math.random() * 9999999).toFixed()
      res.setHeader('Set-Cookie', `${sessionKey}=${sid}`)
      session[sid] = { name: 'zhunny' }
      res.end('Hello Cookie')
    }
  }
}).listen(3000)
复制代码

繰り返します

  Redisのが鍵サーバである、あなたは鍵ペアの特別セッションを置くことができます。:でKOAでセッションを使用する方法

const koa = require('koa')
const app = new koa()
const session = require('koa-session')

const redisStore = require('koa-redis')
const redis = require('redis')
const redisClient = redis.createClient(6379, 'localhost')

const wrapper = require('co-redis')
const client = wrapper(redisClient)

//加密sessionid
app.keys = ['session secret']

const SESS_CONFIG = {
  key: 'kbb:sess',
  //此时让session存储在redis中
  store: redisStore({ client })
}

app.use(session(SESS_CONFIG, app))

app.use(ctx => {
  //查看redis中的内容
  redisClient.keys('*', (errr, keys) => {
    console.log('keys:', keys)
    keys.forEach(key => {
      redisClient.get(key, (err, val) => {
        console.log(val)
      })
    })
  })
  if (ctx.path === '/favicon.ico') return
  let n = ctx.session.count || 0
  ctx.session.count = ++n
  ctx.body = `第${n}次访问`
})

app.listen(3000)
复制代码

ユーザーのログイン認証

  セッションクッキーは、ログイン認証、ログオン・ストレージ・セッションを行う使用している場合、他のインタフェースは、セッションの存在を確認するために、事前に操作のニーズにログインする必要がありながら、当時ログインページに存在しない、ページへのジャンプがあり、ログインセッションを削除。
  認証が必要なミドルウェア・インタフェースを使用してミドルウェアにおける検証KOAを行います。

//前端代码
async login() {
    await axios.post('/login', {
        username: this.username,
        password: this.password
    })
},
async logout() {
    await axios.post('/logout')
},
async getUser() {
    await axios.get('/getUser')
}
复制代码
//中间件 auth.js
module.exports = async (ctx, next) => {
  if (!ctx.session.userinfo) {
    ctx.body = {
      ok: 0,
      message: "用户未登录" };
  } else {
    await next();
} };
//需要验证的接口
router.get('/getUser', require('auth'), async (ctx) => {
  ctx.body = {
    message: "获取数据成功",
    userinfo: ctx.session.userinfo
  }
})
//登录
router.post('/login', async (ctx) => {
  const {
    body
  } = ctx.request
  console.log('body', body)
  //设置session
  ctx.session.userinfo = body.username;
  ctx.body = {
    message: "登录成功"
  }
})
//登出
router.post('/logout', async (ctx) => {
  //设置session
  delete ctx.session.userinfo
  ctx.body = {
    message: "登出系统"
  }
})
复制代码

トークン

  トークンが最初のブラウザがサーバーにアクセスする際にトークンが発行されます、トークンで、それぞれの後にブラウザがサーバーがトークンを認証しますアクセスするには、このトークンを運ぶ限り、サーバーが解読できるように、有効ですトークン、それは要求が正当であることを示し、トークンに含まれるユーザ情報は、異なるユーザIDとを区別することができます。一般トークンハッシュアルゴリズムによって暗号化されたユーザ情報、タイムスタンプと署名で構成されています。

トークン認証プロセス

  1. クライアントは、パスワードを要求して、ユーザのログイン名を使用しています
  2. サーバーは、ユーザー名とパスワードを確認するための要求を受け取ります
  3. 認証が成功した後、トークンを発行するサーバーは、その後、トークンはクライアントに送信されます
  4. クライアントトークンを受け取った後、それは、そのようなクッキーで内部またはローカルストレージ上のように、保存することができます
  5. サーバにリソースを要求するクライアントたびに、トークン発行したサーバで必要
  6. サーバは、要求、検証が成功した場合、それはクライアント端末に失敗した場合、認証が失敗した401の戻りエラーコードを要求されたデータを返し、内部で検証するクライアント要求を受信したトークン(Authorizationヘッダを追加するための要求)。

トークンのセッションとの違い

  1. セッションクッキーの欠点は:(1)アプリを終了クッキーを使用することができない場合はクッキーがブラウザための機構であり、かつ、ブラウザでの認証の使用を制限しました。(2)グローバルな一貫性を満たすために、私たちはより良い行うにはRedisのセッションの永続性に格納されると思いますが、分散環境では、我々は、ストレージスペースの多くを取り上げ、各サーバ上でバックアップする必要があるかもしれません。(3)HTTPS契約のクッキーの使用は、CSRF、クロスサイトリクエストフォージェリ攻撃に対して脆弱ではありません。
  2. トークン短所:セッション・クッキーよりも、このような暗号認証トークン消費性能の(1)消費。(2)大セッションIDよりもトークンは、より多くの帯域幅を占めています。
  3. (1)トークン認証はクッキーに限定されるものではなく、そのためには、この認証方法は、複数のクライアントだけでなく、ブラウザをサポートすることができます:2との比較では、両者の差は明らかです。そして、それは、同一生成元ポリシーの影響を受けません。(2)CSRF攻撃を回避することができますクッキーを使用しないでください。(3)は、トークンに格納する必要はなく、トークンはステートレスサーバに、サーバにのみ定義された規則に従って、リスト上の正規のトークンを検証する必要があり、ユーザ情報に含まれています。また、スケーラビリティのトークンが強くなります。

JWT(JSONウェブトークン)

  認証サーバはJSONオブジェクトを生成した後、JWTの原理は、であり、これは確かに裸のJSONオブジェクトは、ユーザーに渡され、そして誰もが、このオブジェクトを改ざんできないようにする要求を送信することはできません。だから、JSONオブジェクトは、ユーザーのサーバー側の署名の暗号化に返され、コンテンツがユーザーあなたは、このトークンを持つ上のサーバにアクセスするたびに後に、トークンのリターンです。
  JSONオブジェクトは、時間のユーザー情報、ユーザーIDとトークンの有効期限が切れているコンテンツが含まれていてもよいです。

JWTの一部

  でサイトJWTは、JWTをデコードやエンコードすることができます。JWT形式:


  ヘッダ(頭)、ペイロード(負荷)、シグネチャ(署名):これは3つの部分から構成されています。

  1. ヘッダ部は、JSONオブジェクト、JWTを記述するメタデータです。暗号化アルゴリズムとトークントークン一般的な説明のタイプのための情報。{"alg": "HS256","typ": "JWT"}どちらのトークンタイプがJWTで、HS256は、暗号化トークンを使用することを意味します。これは、平文の基本部分に対応し、それが文字列に、このJSONオブジェクトのBase64トランスコーディングを行います。Base64では、復号化アルゴリズムをコードされ、復号化処理は可逆的です。デフォルトのヘッダ情報は、2つのフィールドを運びます。
  2. ペイロードが必要とされる実際のデータ転送を格納するために使用されるJSONオブジェクトの一部です。7つの公式のフィールドがありますが、あなたも、このセクションでプライベートフィールドを定義することができます。一般的なストアのユーザー名、ユーザーIDといくつかのJWTの説明フィールド。それはちょうどBase64エンコードなかっもあり、そのため、このようなログインパスワードなどの秘密情報を、格納することを確認することはできません。
  3. データの改ざんを防止するために、2つの前部に署名署名情報の上記二枚のサーバー修飾ヒトに送信された場合、情報の正確さを検証するために、このタイムサーバの署名に利用可能です。署名キーはサーバ側に保存されている、キーを必要とし、ユーザーは知りません。署名を計算した後、ヘッダ、ペイロード、署名は、ユーザに返すことができ、各セクション間の「点」で区切られた3つの部分の文字列を、(。)を構成します。
JWTの機能
  1. JWTのデフォルトは暗号化されていませんが、また、暗号化することができます。オリジナルのトークンを生成した後、それは一度キーで再暗号化することができます。
  2. 暗号化なしのJWTの場合、秘密データは、JWTに書き込むことができません。
  3. JWTは、認証のために使用することができるだけでなく、情報を交換するために使用することができます。JWTの有効活用は、サーバーの数は、データベースを削減することができる照会します。
  4. JWT最大の欠点は、サーバがセッション状態を保存していないため、トークンは当然に廃止、またはトークンの権限を変更することができない、ということです。これは、サーバーが追加のロジックを展開する場合を除き、満期日まで有効のままになり、一度JWT発行、です。
  5. JWT自体は開示されたときに、誰もがトークンのすべての権限を取得することができ、認証情報が含まれています。盗難を減らすために、JWTの妥当性が比較的短く設定する必要があります。より重要な権利のいくつかのために、再び使用中のユーザを認証する必要があります。
  6. 盗難を減らすために、JWTは、HTTPSプロトコルを使用するように、HTTPプロトコルの伝送符号を使用してはなりません。
JWT認証ユーザーログイン
//前端代码
//axios的请求拦截器,在每个request请求头上加JWT认证信息
axios.interceptors.request.use(
    config => {
        const token = window.localStorage.getItem("token");
        if (token) {
        // 判断是否存在token,如果存在的话,则每个http header都加上token
        // Bearer是JWT的认证头部信息
            config.headers.common["Authorization"] = "Bearer " + token;
        }
        return config;
    },
    err => {
        return Promise.reject(err);
    }
);
//登录方法:在将后端返回的JWT存入localStorage
async login() {
    const res = await axios.post("/login-token", {
        username: this.username,
        password: this.password
    });
    localStorage.setItem("token", res.data.token);
},
//登出方法:删除JWT
async logout() {
    localStorage.removeItem("token");
},
async getUser() {
    await axios.get("/getUser-token");
}
复制代码
//后端代码
const jwt = require("jsonwebtoken");
const jwtAuth = require("koa-jwt");
//用来签名的密钥
const secret = "it's a secret";

router.post("/login-token", async ctx => {
  const { body } = ctx.request;
  //登录逻辑,略,即查找数据库,若该用户和密码合法,即将其信息生成一个JWT令牌传给用户
  const userinfo = body.username;
  ctx.body = {
    message: "登录成功",
    user: userinfo,
    // 生成 token 返回给客户端
    token: jwt.sign(
      {
        data: userinfo,
        // 设置 token 过期时间,一小时后,秒为单位
        exp: Math.floor(Date.now() / 1000) + 60 * 60
      },
      secret
    )
  };
});

//jwtAuth这个中间件会拿着密钥解析JWT是否合法。
//并且把JWT中的payload的信息解析后放到state中,ctx.state用于中间件的传值。
router.get(
  "/getUser-token",
  jwtAuth({
    secret
  }),
  async ctx => {
    // 验证通过,state.user
    console.log(ctx.state.user);
    ctx.body = {
      message: "获取数据成功",
      userinfo: ctx.state.user.data 
    };
  }
)
//这种密码学的方式使得token不需要存储,只要服务端能拿着密钥解析出用户信息,就说明该用户是合法的。
//若要更进一步的权限验证,需要判断解析出的用户身份是管理员还是普通用户。
复制代码

OAuthの

  ログイン三者は、主にOAuth 2.0に基づきます。OAuthプロトコルは、許可されたユーザのリソースの安全なオープン・シンプルなスタンダードを提供します。サードパーティのOAuth認証は、ユーザーがリソースのユーザー名とパスワードを使用することなく、第三者に適用できるということを、(ユーザー名やパスワードなど)のアカウント情報を触れないであろうことを除いて、前の認可方法に承認、OAuthが安全です。私たちは、共通のOAuth認証サービス会社アリペイ、QQ、マイクロ手紙を提供しています。このライセンスは、彼らがより自分のアプリケーションを促進することができ、ユーザーは低いしきい値を使用することができます。
  OAuthの関連記事ルアンYifengの先生は、一連の記事推奨のOAuth 2.0を

OAuth認証プロセス

  OAuthが承認メカニズムの一種です。データシステムの所有者に知らせる、システムにこれらのデータへのアクセスを、サードパーティのアプリケーションを承認することに合意しました。このシステムは、短期的なエントリトークン(トークン)を生成するサードパーティ製アプリケーションを使用するためのパスワードの代わりに使用されています。
  クライアントID:OAuthのトークンが最初に、最初のファイリングシステムを歩んで自分自身を識別し、その後、2つの識別コードを取得する必要があります前に適用するためにトークンを取得するための4つの方法、ライセンス、サードパーティ製アプリケーションの関係なく、どのような種類があります。 (クライアントID)とクライアントシークレット(クライアントシークレット)。これは、トークンの乱用を防ぐためである、トークンを取得することはありませんサードパーティ製アプリケーションのレコードがありませんでした。
  フロントの分離状況の後端が、私たちは多くの場合、認証コードモードを使用し、サードパーティ製のアプリケーションは、認証コードのために適用し、取得したトークンコードを使用することを意味します。

GitHubの例でログイン

  私たちは道に認証コードのプロセスを明確にするために例を使用しています。

  1. サードパーティ製のアプリケーションでGitHubのレコードは、それがクライアントIDとクライアントシークレットに属し得ます。

  中githubの-設定 - 開発者の設定にはOAuthアプリケーションを作成します。そして、関連するコンテンツを記入。充填はGithubのはあなたのクライアントIDとクライアントシークレットを与える完了しています。


Githubのログインリンクを提供することができ、あなたのサードパーティのサイトのこの時点で2は、ユーザーがGitHubのにジャンプしますリンクをクリックします。この手順は、クライアントのIDコードはGitHubのに認証コードを要求しました。

const config = {
  client_id: '28926186082164bbea8f',
  client_secret: '07c4fdae1d5ca458dae3345b6d77a0add5a785ca'
}

router.get('/github/login', async (ctx) => {
  var dataStr = (new Date()).valueOf();
  //重定向到认证接口,并配置参数
  var path = "https://github.com/login/oauth/authorize";
  path += '?client_id=' + config.client_id;

  //转发到授权服务器
  ctx.redirect(path);
})
复制代码
  1. Githubのユーザにジャンプ、ユーザー名とパスワードのGithubを入力し、ユーザーはGithubのIDを使用して、第三者のウェブサイトにログインすることに同意します。このとき、認証コードのコードでバック第三者のウェブサイトへジャンプします。OAuthのが設定されて作成するときのアドレスに戻ってジャンプします。http://localhost:3000/github/callback
  2. サードパーティのサイトでは、認可コード、クライアントIDとクライアントシークレット行方Githubのaccess_tokenはリクエストトークンを保持することができ、認証コードを受け取りました。
  3. リクエストのGithubの領収書は、サードパーティのウェブサイトへのトークンを発行します。
  4. サードパーティのサイトには、トークンを受信した後、あなたは一時的に、情報を得るために、この情報をユーザーが、トークン自身の第三者のウェブサイトを構築し、関連する認証操作を行うことができます得るために許可Githubにいくつかの要求は、そのようなユーザーを持つことができます。
router.get('/github/callback', async (ctx) => {
  console.log('callback..')
  const code = ctx.query.code;
  const params = {
    client_id: config.client_id,
    client_secret: config.client_secret,
    code: code
  }
  let res = await axios.post('https://github.com/login/oauth/access_token', params)
  const access_token = querystring.parse(res.data).access_token
  res = await axios.get('https://api.github.com/user?access_token=' + access_token)
  console.log('userAccess:', res.data)
  ctx.body = `
        <h1>Hello ${res.data.login}</h1>
        <img src="${res.data.avatar_url}" alt=""/>
    `

})
复制代码

参照

クッキーは、セッション愚かなあなたは言うことができませんでしたか?
クッキーは、明確な話をした
エンド周りの認証方法の一般的なタイプ
(X)フロントエンド認証-あなたが言うこと面接のフロントエンド
フロントエンドのパーミッションについての話を
、次の4つの方法でのOAuth 2.0

ます。https://juejin.im/post/5cdb83fe51882569223af7aeで再現

おすすめ

転載: blog.csdn.net/weixin_33722405/article/details/93179552