使用 Laravel Sanctum 构建基于令牌的 Vue.js 客户端 SPA 身份验证
身份验证系统是大多数现代应用程序的重要组成部分,因此应该得到适当实施。
在本文中,您将学习如何使用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
并设置DB_DATABASE=my-app
文件。.env
步骤#3
安装 Laravel Sanctum。
composer require laravel/sanctum
使用 Artisan 命令发布 Sanctum 配置和迁移文件vendor:publish
。sanctum
配置文件将放置在您的config
目录中:
php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"
运行数据库迁移来创建用于存储 API 令牌的数据库表:
php artisan migrate
将 Sanctum 的中间件添加到app/Http/Kernel.phpapi
中的中间件组
../app/Http/Kernel.php
use Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful;
...
protected $middlewareGroups = [
...
'api' => [
EnsureFrontendRequestsAreStateful::class,
'throttle:60,1',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
];
...
],
步骤#4
要为用户使用令牌,我们必须将其添加HasApiTokens
到app/User.phpUser
中的模型中。
../app/User.php
use Laravel\Sanctum\HasApiTokens;
class User extends Authenticatable
{
use HasApiTokens, Notifiable;
}
步骤#5
让我们为模型创建种子User
。稍后我们需要它来测试登录过程。
php artisan make:seeder UsersTableSeeder
现在让我们插入
DB::table('users')->insert([
'name' => 'John Doe',
'email' => 'john@doe.com',
'password' => Hash::make('password')
]);
进入数据库/seeds/UsersTableSeeder.phprun()
中的函数
要用用户种子users
表,让我们运行:
php artisan db:seed --class=UsersTableSeeder
现在我们的数据库中有一个名为John Doe
emailjohn@doe.com
和密码的新用户password
。
步骤#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
让我们向路由发送一个包含邮箱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
接下来,我们需要修改一些中间件。我们在/routes/api.php文件中将以下内容替换auth:api
为auth:sanctum
:
../routes/api.php
Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
return $request->user();
});
步骤#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',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
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
让我们(login/logout)
在 Vuex 中实现一些用户身份验证操作。
../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
如果用户刷新页面怎么办?我们应该要求他重新登录吗?
当然不是!
让我们created()
向实例添加一个方法Vue
来处理该场景。
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>
好的,我们的教程已经完成了。
我希望这些信息对您有帮助!
鏂囩珷鏉ユ簮锛�https://dev.to/romanpaprotsky/vue-js-token-based-authentication-with-laravel-sanctum-3a84