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 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。
推荐文章: