Laravel 教程:使用 Laravel Sanctum 作为 API 认证来构建 Vue.js 应用 
                            
                                                    
                        
                    
                    
  
                    
                    
身份验证系统是大多数现代应用程序的重要组成部分,因此应适当实施。
在本文中,您将学习到如何使用 Vue.js 和 Laravel Sanctum (以前的 Airlock )构建身份验证系统。
我们会创建一个前后端分离的项目,前后端将通过 REST API 相互交互。
让我开始吧!
后端 (Laravel)
第 1 步
有关 Laravel 安装说明,请移步 官方文档 。
在终端中创建一个新的 Laravel 项目
laravel new my-app
或者
composer create-project --prefer-dist laravel/laravel my-app
我正在使用 Laravel Valet,它会自动将应用创建在 http://my-app.test 域名下。
你可以根据你本地的开发环境来配置并访问它。
第 2 步
创建一个名为 my-app 的数据库,并在 .env 文件中设置数据库连接,DB_DATABASE=my-app。
第 3 步
安装 Laravel Sanctum
composer require laravel/sanctum
使用 vendor:publish 命令发布 Sanctum 配置文件和数据库迁移文件。sanctum 配置文件会创建在 config 目录中。
php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"
运行数据库迁移,以创建用于存储API令牌的数据库表:
php artisan migrate
将 Sanctum 中间件添加到 app/Http/Kernel.php 中的 api 中间件组中
../app/Http/Kernel.php
use Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful;
...
    protected $middlewareGroups = [
        ...
        'api' => [
            EnsureFrontendRequestsAreStateful::class,
            'throttle:60,1',
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
        ],
    ];
    ...
],
第 4 步
要为用户使用令牌,我们必须在 app/User.php 的 User 模型中添加 HasApiTokens。
../app/User.php
use Laravel\Sanctum\HasApiTokens;
class User extends Authenticatable
{
    use HasApiTokens, Notifiable;
}
第5步 #5
让我们为 User 模型创建一个 Seeder。我们稍后会需要它来测试登录过程。
php artisan make:seeder UsersTableSeeder
现在,让我们在database/seeds/UsersTableSeeder.php 的 run()方法中,插入以下代码:
DB::table('users')->insert([
    'name' => 'John Doe',
    'email' => 'john@doe.com',
    'password' => Hash::make('password')
]);
给 users 表生成 user, 让我们运行:
php artisan db:seed --class=UsersTableSeeder
现在我们的数据库里有一个新用户叫 "John Doe",邮箱是 "john@doe.com",密码是 "password"。
第6步 #6
让我们在routes/api.php文件中创建一个/login路由:
../routes/api.php
use App\User;
use Illuminate\Support\Facades\Hash;
Route::post('/login', function (Request $request) {
    $data = $request->validate([
        'email' => 'required|email',
        'password' => 'required'
    ]);
    $user = User::where('email', $request->email)->first();
    if (!$user || !Hash::check($request->password, $user->password)) {
        return response([
            'message' => ['These credentials do not match our records.']
        ], 404);
    }
    $token = $user->createToken('my-app-token')->plainTextToken;
    $response = [
        'user' => $user,
        'token' => $token
    ];
    return response($response, 201);
});
第7步 #7
让我们发送一个以邮件john@doe.com和密码password为参数的POST请求到http://my-app.test/api/login路由。你可以使用Postman或Insomnia软件包来完成。
如果一切顺利,我们会收到一个JSON对象作为对我们请求的响应:
{
    "user": {
        "id": 1,
        "name": "John Doe",
        "email": "john@doe.com",
        "email_verified_at": null,
        "created_at": null,
        "updated_at": null
    },
    "token": "AbQzDgXa..."
}
第8步 #8
接下来,我们需要改变一些中间件。我们在/routes/api.php文件中把auth:api替换成auth:sanctum:
../routes/api.php
Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
    return $request->user();
});
第9步 #9
在我们开发前端之前,我们必须先设置跨源请求CORS处理。
../config/cors.php
    'paths' => ['api/*', 'login', 'logout'],
    'allowed_methods' => ['*'],
    'allowed_origins' => ['*'],
    'allowed_origins_patterns' => [],
    'allowed_headers' => ['*'],
    'exposed_headers' => [],
    'max_age' => 0,
    'supports_credentials' => true,
../.env
SANCTUM_STATEFUL_DOMAINS=127.0.0.1
前端 (Vue.js)
我们将使用 Vuex来进行状态管理, 用Vue Router 进行路由以及 axios 来进行HTTP请求
步骤 #1
我们将使用 Vue CLI 来创建一个新的Vue项目。 如果你不熟悉这个标准的Vue.js开发工具,请阅读这个 指南.
在我们项目的目录中,让我们运行以下命令
vue create my-vue-app
选择  Manually select features ,然后选择Router 和 Vuex

在成功创建了my-vue-app项目后,运行以下命令:
cd my-vue-app
npm run serve
现在我们的应用程序应该可以在http://localhost:8080/域名中被访问。
步骤 #2
让我们创建一个新的Login视图文件
..src/views/Login.vue
<template>
  <div>
    <h1>Login</h1>
    <form @submit.prevent="login">
      <input type="email" name="email" v-model="email">
      <input type="password" name="password" v-model="password">
      <button type="submit">Login</button>
    </form>
  </div>
</template>
<script>
export default {
  data () {
    return {
      email: '',
      password: ''
    }
  },
  methods: {
    login () {
      this.$store
        .dispatch('login', {
          email: this.email,
          password: this.password
        })
        .then(() => {
          this.$router.push({ name: 'About' })
        })
        .catch(err => {
          console.log(err)
        })
    }
  }
}
</script>
在 "Vue Router "中,我们必须为 "Login "视图实现一个路由。
../src/router/index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'
Vue.use(VueRouter)
const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home
  },
  {
    path: '/about',
    name: 'About',
    // 路由级别代码分割
    // 这会为这个路由生成一个单独的块(about.[hash].js)。
    // 当路由被访问的时候进行懒加载.
    component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
  },
  {
    path: '/login',
    name: 'Login',
    component: () => import(/* webpackChunkName: "login" */ '../views/Login.vue')
  }
]
const router = new VueRouter({
  mode: 'history',
  base: process.env.BASE_URL,
  routes
})
export default router
现在,如果我们在浏览器中导航到http://localhost:8080/login,我们可以看到一个登录页面。

步骤 #3
我们必须在前端目录下安装axios来进行HTTP请求:
npm install axios
步骤 #4
让我们在Vuex中实现一些用户认证操作(login/logout)。
../src/store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
import axios from 'axios'
Vue.use(Vuex)
axios.defaults.baseURL = 'http://app-backend.test/api'
export default new Vuex.Store({
  state: {
    user: null
  },
  mutations: {
    setUserData (state, userData) {
      state.user = userData
      localStorage.setItem('user', JSON.stringify(userData))
      axios.defaults.headers.common.Authorization = `Bearer ${userData.token}`
    },
    clearUserData () {
      localStorage.removeItem('user')
      location.reload()
    }
  },
  actions: {
    login ({ commit }, credentials) {
      return axios
        .post('/login', credentials)
        .then(({ data }) => {
          commit('setUserData', data)
        })
    },
    logout ({ commit }) {
      commit('clearUserData')
    }
  },
  getters : {
    isLogged: state => !!state.user
  }
})
登录成功后,我们要在user变量和localStorage中存储一些用户数据。
步骤 #5
让我们来定义已认证和未认证页面的路由。
我们可以让 About 页面只对认证过的用户开放。
为了这个目的,让我们把 meta字段添加到 About 路径中。
让我们使用Vue Router的beforeEach方法来检查用户是否登录。如果用户没有通过认证,我们将把他们重定向回登录页面。
../src/router.index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'
Vue.use(VueRouter)
const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home
  },
  {
    path: '/about',
    name: 'About',
    meta: {
      auth: true
    },
    component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
  },
  {
    path: '/login',
    name: 'Login',
    component: () => import(/* webpackChunkName: "login" */ '../views/Login.vue')
  }
]
const router = new VueRouter({
  mode: 'history',
  base: process.env.BASE_URL,
  routes
})
router.beforeEach((to, from, next) => {
  const loggedIn = localStorage.getItem('user')
  if (to.matched.some(record => record.meta.auth) && !loggedIn) {
    next('/login')
    return
  }
  next()
})
export default router
步骤 #6
如果用户刷新了一个页面怎么办?要不要让他重新登录?
当然不是!
让我们在 Vue 实例中添加一个 created()方法来处理这种情况。
created () {
  const userInfo = localStorage.getItem('user')
  if (userInfo) {
    const userData = JSON.parse(userInfo)
    this.$store.commit('setUserData', userData)
  }
}
步骤 #7
我们还需要处理令牌过期或用户未授权的情况。
让我们在created()方法中通过使用拦截器来实现。
然后我们修改后的 main.js文件看起来是这样的:
../src/main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
Vue.config.productionTip = false
new Vue({
  router,
  store,
  created () {
    const userInfo = localStorage.getItem('user')
    if (userInfo) {
      const userData = JSON.parse(userInfo)
      this.$store.commit('setUserData', userData)
    }
    axios.interceptors.response.use(
      response => response,
      error => {
        if (error.response.status === 401) {
          this.$store.dispatch('logout')
        }
        return Promise.reject(error)
      }
    )
  },
  render: h => h(App)
}).$mount('#app')
步骤 #8
我们还没有实现 Logout功能。让我们在 App.vue文件中实现这个功能。
另外,让我们只在用户登录时显示About和 Logout按钮。
../src/App.vue
<template>
  <div id="app">
    <div id="nav">
      <router-link to="/">Home</router-link> |
      <router-link to="/about" v-if="isLogged">About</router-link>
      <router-link to="/login" v-else>Login</router-link>
      <button type="button" @click="logout()" v-if="isLogged">
        Logout
      </button>
    </div>
    <router-view/>
  </div>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
  computed: {
    ...mapGetters([
      'isLogged'
    ])
  },
  methods: {
    logout () {
      this.$store.dispatch('logout')
    }
  }
}
</script>
好了,我们的教程结束了。
希望这些信息对你有所帮助!
查看源码
本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。
          
            
                    
                    
有人登录的时候遇到过这种情况吗?Postman能正常返回,各种百度找不到解决办法。求大咖帮忙···
          
          
                关于 LearnKu
              
                    
                    
                    
 
推荐文章: