Django

Django Vuejs JWT認証の実装を開始(Total:4day17.5h)

フロントエンド側にVuexとRouterを作成

いやー、今回はかんり苦労しています💦

記録を取りながら進めようと思っていましたが、全然うまくいかずで修正しては、考えてを繰り返しました


Storeは、Indexファイルにすべて記載

storeで今後、なにか状態管理するものが出てきたらファイルの分割が必要になるかもですが、まぁ、先に全然進まず心が折れてしまうよりマシだということで進めていきます。

import Vue from "vue";
import Vuex from "vuex";
import $api from "@/services/index";

Vue.use(Vuex);

// 認証情報

const authenModule = {
  namespaced: true,
  state: {
    username: "",
    isLoggedIn: false
  },
  mutations: {
    set(state: any, payload: any) {
      state.username = payload.user.username;
      state.isLoggedIn = true;
    },
    clear(state: any) {
      state.username = "";
      state.isLoggedIn = false;
    }
  },
  actions: {
    /**
     * ログイン
     */
    login(context: any, payload: any) {
      console.log('kokomadekitayo')
      return $api.authen.doLogin({
          username: payload.username,
          password: payload.password
        })
        .then(response => {
          // 認証用トークンをlocalStorageに保存
          localStorage.setItem("access", response.data.access);
          // ユーザー情報を取得してstoreのユーザー情報を更新
          return context.dispatch("renew");
        });
    },
    /**
     * ログアウト
     */
    logout(context: any) {
      // 認証用トークンをlocalStorageから削除
      localStorage.removeItem("access");
      // storeのユーザー情報をクリア
      context.commit("clear");
    },
    /**
     * ユーザー情報更新
     */
    renew(context: any) {
      return $api.authen.getUserInfo().then(response => {
        const user = response.data;
        // storeのユーザー情報を更新
        context.commit("set", { user: user });
      });
    }
  }
};

const store: any = new Vuex.Store({
  modules: {
    authen: authenModule,
  }
});

export default store;

Djangoとの連携の確認までいけていないので、修正が入る可能性が大ですが、何となくの流はできているかなって思う。

リフレッシュトークンでの再認証はしないことにするかも

リフレッシュトークンで再認証させる方がいいのか悩みますが、まぁ、とりあえずなしで進もうかと思い、コメントアウトしました😢

いやーなんだかんだ挑戦したことは、未来の役に立つはずと信じて(笑)

import axios from 'axios'

// リクエストするURLとタイムアウトとヘッダーJson形式であること
const api = axios.create({
    baseURL: process.env.Vue_APP_ROUTE_API,
    timeout: 5000,
    headers: {
        "Content-Type": "application/json",
        "X-Requested-With": "XMLHttpRequest",
    }
})

//リクエスト処理の前に処理を行う
api.interceptors.request.use(
    config => {
        // Tokenがあればリクエストヘッダに追加
        const token = localStorage.getItem("access-token")
        if (config.headers && token) {
            config.headers.Authorization = "JWT " + token
            return config
        }
        return config
    },
    // エラー処理
    error => {
        return Promise.reject(error)
    }
)

// 共通処理
//const refreshTokenPromise = null

api.interceptors.response.use(
    response => {
        return response
    },
    // エラー処理
    error => {
        console.log(error)
        // errorstatusがなければ500セット
        const status = error.response ? error.responose.status: 500
        const refreshToken = localStorage.getItem('refresh-token')
        // リフレッシュトークンがあるなら
        if (error.config && error.response && error.response.status === 401 && refreshToken && !error.config._retry) {
            //認証エラー
            localStorage.clear()
            console.log('error')
            // if (!refreshTokenPromise) {
            //     //retryをtureに変更
            //     error.config._retry = true
            //     refreshTokenPromise = getRefeshToken().then(token => {
            //         refreshTokenPromise = null
            //         return token
            //     })
            // }
            // return refreshTokenPromise.then(token => {
            //     error.config.headers['refresh-token'] = token
            //     return 
            // })
        }
    }
)

export default api

// const getRefeshToken = () => {
//     // refreshTokeで再取得挑戦
//     try {
//         const response = await axios.post(
//             `${process.env.Vue_APP_ROUTE_API}api/token/refresh/`,
//             {
//                 refresh: localStorage.getItem('refresh-token')
//             },
//             api.defaults.headers.common {
//                 'Authorization'
//             }
//         )
    
//     }
// }

ほとんどコメントアウト状態…

これから、エラー処理等も入れながら完成に向かいたいので、まだまだですが…

リクエストを管理するauthen.tsファイル

GitHubでいろいろ検索して参考にしていたのですが、リクエストするURLやmethodを管理している方法がたくさんあって、めっちゃ参考になりました(^▽^)

GitHubって自分のコードの保存場所と思っていたのですが、検索したらめっちゃいっぱい公開されていてうれしい限りです。

ポイント

GitHubで検索するとSampleコードがたくさんあるよー

import api from '@/services/api'

export function doLogin(data: { username: string; password: string }) {
  return (
    api.request({
      method: 'post',
      url: '/api/token/login/',
      data
    })
  )
}

export function getUserInfo() {
    return (
      api.request({
        method: 'get',
        url: '/api/users/info/',
      })
    )
  }

コメントすることは、特にないですかね。

見た目のまんまです

Roter Indexファイルを修正

ここがかなり苦労してしまいました(´;ω;`)ウッ…

強制的にログイン画面に送還することが思ったように動作せずになんか、ループしまくったりで本当に大変でした💦

誰かの参考になれば幸いです

import Vue from "vue";
import VueRouter, { RouteConfig } from "vue-router";
import store from "@/store";
import { doLogin } from "@/services/authen/authen";

Vue.use(VueRouter);

const routes: Array<RouteConfig> = [
  {
    path: "/",
    redirect: '/login'
  },
  {
    path: "/login",
    name: "Login",
    component: () =>
      import("@/views/authen/Login.vue"),
  },
  {
    path: "/home",
    name: "Home",
    component: () =>
      import("../views/Home.vue"),
  },
  {
    path: "/:catchAll(.*)",
    redirect: "/lgoin"
  }
];

const router = new VueRouter({
  mode: "history",
  routes,
});

router.beforeEach((to, from, next) => {
  const isLoggedIn = store.state.authen.isLoggedIn
  const token = localStorage.getItem("access-token")
  const publicPage = ['/login']
  const ToLoginPage = publicPage.includes(to.path)

  if (!ToLoginPage) {
    if (!isLoggedIn){
      if (token != null) {
        store
        .dispatch("authen/renew")
        .then(() => {
          // 再取得
          next();
        })
        .catch(() => {
          // 取得できずログイン画面へ送還
          console.log("acess")
          forceToLoginPage(to)
        })
      } else {
        forceToLoginPage(to)
      }
    } else {
      //tokenないのでログイン画面へ
      forceToLoginPage(to)
    }
  } else {
    //tokenないのでログイン画面へ
    next()
  }
  next()
})

function forceToLoginPage(to: any) {
  router.replace({
    path: "/login",
  });
}
export default router;

いやー振返ってみれば何でこんな苦労したんだろう…ですが、JavaScriptを全然知らない自分がここまでやっていることを考えたら、当然ですね。

どちらかと言えば、まだ早めに解決できたって思う。

いやー本当に、よく頑張ってるなぁ。

Django corsheadersをインストールして異なるオリジンからの接続を許可

今のままだと、VueからリクエストをしてもDjangoにつながることはできません。

そこで、Djnago側で許可をするための設定等を行います

https://github.com/adamchainz/django-cors-headers

python -m pip install django-cors-headers
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    #サードパーティー
    'rest_framework',
    'corsheaders' #追加
    
    #アプリ
    'users',
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    "corsheaders.middleware.CorsMiddleware", #追加
    "django.middleware.common.CommonMiddleware", #追加
]

# CORS追加
CORS_ALLOW_ALL_ORIGINS = False
CORS_ALLOWED_ORIGINS = [
    'http://localhost:8080',
    'http://127.0.0.1:8080',
]

これで、アクセストークンを取得の処理をするとちゃんと返ってきました!

いやーなかなか時間がかかりました💦

ログインもまだまだ完成までかかりそうですが、着実にすすんでいることは間違いありません。

-Django

© 2022 ごろう@縁紡ぐ