前後の分離:tokenログインソリューションの使用

8100 ワード

ログイン時にtokenを取得し、後でインタフェースを呼び出すとtokenを連れてサーバに要求します
Tokenログインソリューションを使用した前後の分離
この記事では,前後の分離によるログインソリューションについて述べるが,現在ではほとんどがTokenをリクエストヘッダで携帯する形式を採用している.
書く前にまず考えを整理してください.
  • 初回ログイン時、バックエンドサーバはユーザーアカウントのパスワードが正しいと判断した後、ユーザーid、ユーザー名、定義された鍵、期限切れ時間に基づいてtokenを生成し、フロントエンドに戻る.
  • フロントエンドはバックエンドから戻ってきたtokenを受け取り、localStroageとVuexに格納される.
  • フロントエンドはルーティングごとにジャンプし、localStroageにtokenがあるかどうかを判断し、ない場合はログインページにジャンプし、ある場合はユーザー情報の取得を要求し、ログイン状態を変更する.
  • 毎回インタフェースを要求し、Axios要求ヘッダにtokenを携帯する.
  • バックエンドインタフェースは、要求ヘッダがtokenであるか、tokenが期限切れであるかを判断し、401を返す.
  • フロントエンドは401ステータスコードを取得し、ログインページにリダイレクトする.

  • 私のここのフロントエンドはVueを使って、住所:vue-token-login
    バックエンドはアリのeggを使用し、アドレス:egg-token-login
    まず、Axiosを軽くカプセル化します.
    私はTokenをlocalStroageに存在させて、Tokenがあるかどうかを検査して、毎回Axiosリクエストの頭の上で携帯することを要求します
    if (window.localStorage.getItem('token')) {
      Axios.defaults.headers.common['Authorization'] = `Bearer ` + window.localStorage.getItem('token')
    }
    
    
    

    responeブロッカーを用いて,2 xx状態コード以外の結果をブロッキングする.
    ステータスコードが401の場合、Tokenが期限切れになり、ログインページにジャンプする可能性があります.
    instance.interceptors.response.use(
      response => {
        return response
      },
      error => {
        if (error.response) {
          switch (error.response.status) {
            case 401:
              router.replace({
                path: 'login',
                query: { redirect: router.currentRoute.fullPath } //       path    ,           
              })
          }
        }
        return Promise.reject(error.response)
      }
    )
    
    
    

    ルーティングの定義:
    const router = new Router({
      mode: 'history',
      routes: [
        {
          path: '/',
          name: 'Index',
          component: Index,
          meta: {
            requiresAuth: true
          }
        },
        {
          path: '/login',
          name: 'Login',
          component: Login
        }
      ]
    })
    
    
    

    トップページのルーティングにrequiresAuthを加えたので、ルーティングフックを使用してナビゲーションをブロックし、localStroageにTokenがあればuserInfoを取得する方法を呼び出し、実行を続け、Tokenがなければログインを終了する方法を呼び出し、ログインページにリダイレクトします.
    router.beforeEach((to, from, next) => {
      let token = window.localStorage.getItem('token')
      if (to.meta.requiresAuth) {
        if (token) {
          store.dispatch('getUser')
          next()
        } else {
          store.dispatch('logOut')
          next({
            path: '/login',
            query: { redirect: to.fullPath }
          })
        }
      } else {
        next()
      }
    })
    
    
    

    ここでは2つのVuexのactionメソッドを使って、すぐに言います.
    Vuex
    まず、mutation_typesで定義:
    export const LOGIN = 'LOGIN' //   
    export const USERINFO = 'USERINFO' //     
    export const LOGINSTATUS = 'LOGINSTATUS' //     
    
    
    

    次にmutationで使用します.
    const mutations = {
      [types.LOGIN]: (state, value) => {
        state.token = value
      },
      [types.USERINFO]: (state, info) => {
        state.userInfo = info
      },
      [types.LOGINSTATUS]: (state, bool) => {
        state.loginStatus = bool
      }
    }
    
    
    

    Axiosをカプセル化したJSで要求インタフェースを定義します.
    export const login = ({ loginUser, loginPassword }) => {
      return instance.post('/login', {
        username: loginUser,
        password: loginPassword
      })
    }
    
    export const getUserInfo = () => {
      return instance.get('/profile')
    }
    
    
    

    Vuexのactionsに導入:
    import * as types from './types'
    import { instance, login, getUserInfo } from '../api'
    
    
    

    アクションの定義
    export default {
      toLogin ({ commit }, info) {
        return new Promise((resolve, reject) => {
          login(info).then(res => {
            if (res.status === 200) {
              commit(types.LOGIN, res.data.token) //    token
              commit(types.LOGINSTATUS, true)     //         
              instance.defaults.headers.common['Authorization'] = `Bearer ` + res.data.token //       token
              window.localStorage.setItem('token', res.data.token)  //     localStroage
              resolve(res)
            }
          }).catch((error) => {
            console.log(error)
            reject(error)
          })
        })
      },
      getUser ({ commit }) {
        return new Promise((resolve, reject) => {
          getUserInfo().then(res => {
            if (res.status === 200) {
              commit(types.USERINFO, res.data) //   userInfo    Vuex
            }
          }).catch((error) => {
            reject(error)
          })
        })
      },
      logOut ({ commit }) { //     
        return new Promise((resolve, reject) => {
          commit(types.USERINFO, null)        //    userInfo
          commit(types.LOGINSTATUS, false)  //        false
          commit(types.LOGIN, '')          //    token
          window.localStorage.removeItem('token')
        })
      }
    }
    
    
    

    インタフェース
    この時、バックエンドインタフェースを書くべきです.
    私はアリのeggフレームを使っていて、強い感じがします.
    まず、LoginControllerを定義します.
    const Controller = require('egg').Controller;
    const jwt = require('jsonwebtoken'); //    jsonwebtoken
    
    class LoginController extends Controller {
      async index() {
        const ctx = this.ctx;
    /*
              token ,        ,       
                     
    */
        const token = jwt.sign({       
          user_id: 1,      // user_id
          user_name: ctx.request.body.username // user_name
        }, 'shenzhouhaotian', { //   
            expiresIn: '60s' //     
        });
        ctx.body = {             //      
          token: token
        };
        ctx.status = 200;           //     200
      }
    }
    
    module.exports = LoginController;
    
    
    

    UserController:
    class UserController extends Controller {
      async index() {
        const ctx = this.ctx
        const authorization = ctx.get('Authorization');
        if (authorization === '') { //            token ,       401
            ctx.throw(401, 'no token detected in http header "Authorization"');
        }
        const token = authorization.split(' ')[1];
        // console.log(token)
        let tokenContent;
        try {
            tokenContent = await jwt.verify(token, 'shenzhouhaotian');     //   token        ,   401
            console.log(tokenContent)
            ctx.body = tokenContent     // token  ,   userInfo ;  ,                
        } catch (err) {
            ctx.throw(401, 'invalid token');
        }
      }
    }
    
    
    

    router.jsでインタフェースを定義します.
    module.exports = app => {
      const { router, controller } = app;
      router.get('/', controller.home.index);
      router.get('/profile', controller.user.index);
      router.post('/login', controller.login.index);
    };
    
    
    

    フロントエンドリクエスト
    インタフェースができたので、フロントエンドでお願いします.
    ここではログインコンポーネントを書きましたが、ログイン時のloginメソッドをクリックします.
        login () {
          if (this.username === '') {
            this.$message.warning('        ~~')
          } else if (this.password === '') {
            this.$message.warning('       ~~')
          } else {
            this.$store.dispatch('toLogin', {      // dispatch toLogin action
              loginUser: this.username,
              loginPassword: this.password
            }).then(() => {
              this.$store.dispatch('getUser')      // dispatch getUserInfo action
              let redirectUrl = decodeURIComponent(this.$route.query.redirect || '/')
              console.log(redirectUrl)
              //         
              this.$router.push({
                path: redirectUrl
              })
            }).catch((error) => {
              console.log(error.response.data.message)
            })
          }
        }
    
    
    

    ログインに成功したら、トップページの前にリダイレクトしたページにジャンプします.
    全体の流れが走り終わって、実現する主な機能は:
  • ログイン登録以外のルートにアクセスするには、ログイン権限が必要です.例えば、トップページでtokenの有無を判断し、あればアクセスに成功し、なければログインページにジャンプします.
  • ログインに成功した後、前にリダイレクトしたページにジャンプします.
  • tokenが期限切れになった後、インタフェースを要求すると、アイデンティティが期限切れになり、ログインページにジャンプし、2番目のステップを続行します.このステップは主に7日間の自動ログインなどの機能を使用しています.

  • 転載先:http://www.yyyweb.com/5144.html # [ vue token, token header ](https://www.cnblogs.com/xiaoyingainiaa/p/9810610.html)
    https://www.cnblogs.com/xiaoyingainiaa/p/9810610.html