Auth in nuxtjs

Hôm trước mình có chia sẽ bài viết về JWT trong django, hôm nay mình sẽ hướng dẫn xử dụng api này để login bằng nuxtjs

Cài đặt

Giả sử các bạn đã cài sẵn nuxtjs, bây giờ chúng ta đi cài đặt những packages cần thiết Thêm vào file package.json:

"@nuxtjs/auth": "^4.8.1",
"@nuxtjs/axios": "^5.0.0",

Ở đây mình sử dụng 2 gói có sẵn của nuxtjs là authaxios

sau khi thêm vào các bạn chạy npm install để down load thư viện mới về:

Trong file nuxt.config.js config như sau:

...

  modules: [
    // Doc: https://github.com/nuxt-community/axios-module#usage
    '@nuxtjs/axios',
    // Doc: https://github.com/bootstrap-vue/bootstrap-vue
    'bootstrap-vue/nuxt',
    '@nuxtjs/dotenv',
    '@nuxtjs/auth',
  ],

 /*
  ** Axios module configuration
  */
  axios: {
    baseURL: process.env.API_URL || ' http://localhost:8000/api/',
    debug: process.env.DEBUG || false,
    proxyHeaders: false,
    credentials: false,
  },
  
  auth: {
    strategies: {
      local: {
        endpoints: {
          login: {
            url: 'accounts/token/',
            method: 'post',
            propertyName: 'access',
          },
          user: {
            url: 'accounts/me/',
            method: 'get',
            propertyName: 'users'
          },
          tokenRequired: true,
          logout: false
        }
      },
      watchLoggedIn: true,
      redirect: {
        login: '/login',
        logout: '/',
        callback: '/login',
        home: '/'
      }
    }
  },
...

mình giải thích một chút:

đầu tiên là config của axios, chỗ này cần config cái base url, tất cả request sẽ nhận base này, ko cần phải gọi full url đến một api nữa.

auth thì cân có 2 api là get access tokenapi me, sau khi login module này sẽ tự động gọi api me để lấy thông tin người dùng về (để hiển thì tên user đã login chẳng hạn).

Code thôi

Login

Tạo một file Notification.vue trong thư mục components có nội dung như sau:

<template>
  <div class="notification is-danger bg-danger">
    {{ message }}
  </div>
</template>

<style>
  .notification {
    padding: 11px;
    margin: 10px 0px;
  }
</style>

<script>
  export default {
    name: 'Notification',
    props: ['message']
  }
</script>

file này đơn giản chỉ là thông báo ra khi có lỗi mà thôi

Tạo một file login.vue trong thư mục pages có nội dung như sau:

<template>
  <div class="app flex-row align-items-center">
    <div class="container">
      <b-row class="justify-content-center login">
        <b-col md="6">
          <b-card-group>
            <b-card no-body class="p-4">
              <b-card-body>
                <h1>Login</h1>
                <Notification :message="error" v-if="error"/>
                <p class="text-muted">Sign In to your account</p>
                <b-input-group class="mb-3">
                  <b-input-group-prepend>
                    <b-input-group-text><i class="icon-user"></i></b-input-group-text>
                  </b-input-group-prepend>
                  <input type="text" class=" form-control" v-model="username"
                         placeholder="username">
                </b-input-group>

                <b-input-group class="mb-4">
                  <b-input-group-prepend>
                    <b-input-group-text><i class="icon-lock"></i></b-input-group-text>
                  </b-input-group-prepend>
                  <input type="password" class="form-control" v-model="password" placeholder="Password">
                </b-input-group>

                <b-row>
                  <b-col cols="6">
                    <b-button variant="primary" class="px-4" @click="login">Login</b-button>
                  </b-col>
                </b-row>
              </b-card-body>
            </b-card>
          </b-card-group>
        </b-col>
      </b-row>
    </div>
  </div>
</template>

<script>
  import Notification from '~/components/Notification'

  export default {
    name: 'Login',
    layout: 'clean',
    components: {
      Notification,
    },

    data() {
      return {
        username: '',
        password: '',
        error: null
      }
    },

    methods: {
      async login() {
        try {
          const response = await this.$axios.post('accounts/token/', {
            username: this.username,
            password: this.password
          });

          await this.$auth.setToken('local', "Bearer " + response.data.access);
          await this.$auth.setRefreshToken('local', response.data.refresh);
          await this.$auth.setUserToken(response.data.access);
        } catch (e) {
          this.error = 'Username or Password not valid'
        }
      }
    }
  }
</script>

Mình có sử dụng bootstrap-vue nên cứ pháp html có hơi khác form bình thường một chút. các bạn có thể xem thêm về package này, hoặc bỏ đi viết luôn code html thuần vào cũng được.

File login này có 1 form login, chắc ai cũng biết cái này rồi mình sẽ không nói lại nữa.

ở đây mình có custon lại phương thức login mặc định của module auth một chút, vì auth hiện tại không hỗ trợ refresh token.

đến đây chúng ta đã xong phần login rồi, login xong nó sẽ redirect về trang / của project, bạn có thể thay đổi trong file nuxt.config.js phần redirect.

Lấy thông tin users

Tạo một file là index.js trong phần store để lấy thông tin của users khi đã login thành công:

export const getters = {
  isAuthenticated(state) {
    return state.auth.loggedIn
  },

  loggedInUser(state) {
    return state.auth.user
  }
};

Trong file header của vue:

<template>
  <b-nav-item-dropdown right no-caret>
    <template slot="button-content">
      <img src="~static/img/avatars/6.jpg" class="img-avatar" alt="">
      <span class="hidden-sm hidden-xs">{{ loggedInUser.username }}</span>
    </template>

    <b-dropdown-header tag="div" class="text-center"><strong>Account</strong></b-dropdown-header>
    <b-dropdown-item @click="logout"><i class="fa fa-lock"></i>Logout</b-dropdown-item>
  </b-nav-item-dropdown>
</template>

<script>
  import {mapGetters} from 'vuex'

  export default {
    name: 'header-dropdown',
    computed: {
      ...mapGetters(['loggedInUser'])
    },

    methods: {
      async logout() {
        await this.$auth.logout();
        this.$router.push('/login');
      },
    },
  }
</script>

Tạm thời các bạn chỉ cần quan tâm đến phần js thôi, cái bên trên là code html, có thể thay đổi theo ý muốn của mọi người.

Khi users đã login xong thì chúng ta có thể lấy thông tin users qua biến loggedInUser

Refresh token

Tiếp theo đến phần refresh token, Tạo một file axios.js trong thư mục plugins có nội dung như sau:

export default function ({$axios, app}) {
  $axios.onError(error => {
    const statusCode = error.response.status;

    // refresh token if it expired
    if (statusCode === 403) {
      const originalRequest = error.config;
      if (!originalRequest._retry) {
        originalRequest._retry = true;
        return $axios.post('accounts/refresh-token/', {'refresh': app.$auth.getRefreshToken('local')})
          .then((response) => {
            originalRequest.headers['Authorization'] = 'Bearer ' + response.data.access;
            app.$auth.setToken('local', "Bearer " + response.data.access);
            return $axios(originalRequest);
          });
      }
    }

    return Promise.reject(error);
  });
}

Trong file nuxt.config.js thêm vào nội dung sau:

  /*
  ** Plugins to load before mounting the App
  */
  plugins: [
  ....
    '~plugins/axios',
   ....
  ],

Vậy là xong rồi, khi token hết hạn nó sẽ tự động gọi lại để lấy lại token mới.

Tham khảo


All Rights Reserved