关键词:
【中文标题】使用 Laravel 作为后端刷新 Vue.js SPA 的身份验证令牌【英文标题】:Refreshing authentication tokens for a Vue.js SPA using Laravel for the backend 【发布时间】:2018-04-12 11:20:50 【问题描述】:我正在使用 Laravel (5.5) 作为后端构建一个带有 Vue (2.5) 的单页应用程序。一切正常,除了注销后直接再次登录。在这种情况下,对 /api/user 的调用(以检索用户的帐户信息并再次验证用户的身份)失败并出现 401 未授权(即使登录成功)。作为响应,用户被直接弹回登录屏幕(我自己编写了这个度量作为对 401 响应的响应)。
有效的方法是注销,使用 ctrl/cmd+R 刷新页面,然后再次登录。页面刷新解决了我的问题,让我有理由相信我没有正确处理 X-CSRF-TOKEN 的刷新,或者可能忘记了 Laravel 使用的某些 cookie(如 here 所述)。
这是用户单击登录按钮后执行的登录表单代码的 sn-p。
login()
// Copy the form data
const data = ...this.user;
// If remember is false, don't send the parameter to the server
if(data.remember === false)
delete data.remember;
this.authenticating = true;
this.authenticate(data)
.then( this.refreshTokens )
.catch( error =>
this.authenticating = false;
if(error.response && [422, 423].includes(error.response.status) )
this.validationErrors = error.response.data.errors;
this.showErrorMessage(error.response.data.message);
else
this.showErrorMessage(error.message);
);
,
refreshTokens()
return new Promise((resolve, reject) =>
axios.get('/refreshtokens')
.then( response =>
window.Laravel.csrfToken = response.data.csrfToken;
window.axios.defaults.headers.common['X-CSRF-TOKEN'] = response.data.csrfToken;
this.authenticating = false;
this.$router.replace(this.$route.query.redirect || '/');
return resolve(response);
)
.catch( error =>
this.showErrorMessage(error.message);
reject(error);
);
);
,
authenticate()
方法是一个 vuex 动作,在 laravel 端调用登录端点。
/refreshTokens 端点只是调用这个 Laravel 控制器函数,它返回当前登录用户的 CSRF 令牌:
public function getCsrfToken()
return ['csrfToken' => csrf_token()];
重新获取令牌后,用户将被重定向到主页(或其他页面,如果提供的话)
使用this.$router.replace(this.$route.query.redirect || '/');
并在那里调用api/user
函数来检查当前登录用户的数据。
我是否应该采取任何其他措施来完成这项工作,但我忽略了?
感谢您的帮助!
编辑:2017 年 11 月 7 日
在所有有用的建议之后,我想补充一些信息。我在 Laravel 端使用 Passport 进行身份验证,并且 CreateFreshApiToken 中间件就位。
我一直在查看我的应用程序设置的 cookie,特别是 laravel_token
,据说它保存了 Passport 将用来验证来自 JavaScript 应用程序的 API 请求的加密 JWT。注销时,laravel_token cookie 被删除。之后直接再次登录时(使用 axios 发送 AJAX 发布请求)没有设置新的laravel_token
,所以这就是它不对用户进行身份验证的原因。我知道 Laravel 没有在登录 POST 请求上设置 cookie,但是之后直接对 /refreshTokens (不受保护)的 GET 请求应该设置 cookie。但是,这似乎没有发生。
我已尝试增加对/refreshTokens
的请求和对/api/user
的请求之间的延迟,以可能给服务器一些时间来整理东西,但无济于事。
为了完整起见,这里是我的 Auth\LoginController 正在处理登录请求服务器端:
class LoginController extends Controller
use AuthenticatesUsers;
/**
* Where to redirect users after login.
*
* @var string
*/
protected $redirectTo = '/';
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
// $this->middleware('guest')->except('logout');
/**
* Get the needed authorization credentials from the request.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
protected function credentials(\Illuminate\Http\Request $request)
//return $request->only($this->username(), 'password');
return ['email' => $request->$this->username(), 'password' => $request->password, 'active' => 1];
/**
* The user has been authenticated.
*
* @param \Illuminate\Http\Request $request
* @param mixed $user
* @return mixed
*/
protected function authenticated(\Illuminate\Http\Request $request, $user)
$user->last_login = \Carbon\Carbon::now();
$user->timestamps = false;
$user->save();
$user->timestamps = true;
return (new UserResource($user))->additional(
['permissions' => $user->getUIPermissions()]
);
/**
* Log the user out of the application.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function logout(\Illuminate\Http\Request $request)
$this->guard()->logout();
$request->session()->invalidate();
【问题讨论】:
“注销”功能是如何实现的? 它通过post请求调用laravel的注销功能。所以很简单axios.post('/logout')
;
当您观察 Ajax 的注销/登录请求来回切换时,您是否看到 /refreshtokens 生成并返回了一个新令牌,并且该令牌用于后续(和失败)调用到服务器?
是的,我看到正在生成一个新令牌。我做了更多研究,发现了一些新信息:问题是 refreshToken 返回的令牌是用于防止跨站点请求伪造的 CSRF 令牌。它不是用于对用户进行身份验证的 JWT 令牌。这是在一个名为 laravel_token
的 cookie 中设置的,它是一个 httpOnly cookie,因此在 JS 中无法访问。许多消息来源说浏览器应该处理这个令牌,即使它是通过 ajax 响应返回的,但显然这里不是这种情况......
这里有更多关于这个问题的信息:github.com/laravel/passport/issues/293
【参考方案1】:
考虑到您使用 api 进行身份验证,我建议使用 Passport 或 JWT Authentication 来处理身份验证令牌。
【讨论】:
谢谢。我正在使用护照。它说它应该根据laravel.com/docs/5.5/… 自动处理 JWT 身份验证。这很好用,除了注销登录问题。我的想法是我没有正确处理根据laravel.com/docs/5.5/csrf#csrf-x-csrf-token 随每个响应返回的 cookie 数据 啊抱歉不明显。我会检查您的配置和设置,并确保一切正确 我已经这样做了,但找不到任何异常。我认为我的程序是正确的,但我错过了难题的某些部分(比如处理 cookie 数据)。我怀疑我是第一个遇到这个问题的人,所以希望有人发现错误。 显然我将用于防止跨站点请求伪造的 CSRF 令牌与用于身份验证的laravel_token
cookie 混淆了。此 cookie 是 httpOnly 的,因此 JS 无法访问。它应该由浏览器自动处理,但在我的情况下不会发生这种情况。 (即使我通过调用 /refreshTokens 执行了设置 cookie 所需的额外 GET 请求)。
您的配置中显然缺少某些内容或不正确。来自开发工具的一些请求/响应转储会很有帮助。此外,请确保您已将 \Laravel\Passport\Http\Middleware\CreateFreshApiToken::class
添加到您的 web
中间件组中,如此处所述 laravel.com/docs/5.5/…【参考方案2】:
终于修好了!
通过直接在 LoginControllers authenticated
方法中返回 UserResource,它不是有效的 Laravel 响应(但我猜是原始 JSON 数据?)所以可能没有附加 cookie 之类的东西。我必须在资源上附加对 response() 的调用,现在一切似乎都运行良好(尽管我需要进行更广泛的测试)。
所以:
protected function authenticated(\Illuminate\Http\Request $request, $user)
...
return (new UserResource($user))->additional(
['permissions' => $user->getUIPermissions()]
);
变成
protected function authenticated(\Illuminate\Http\Request $request, $user)
...
return (new UserResource($user))->additional(
['permissions' => $user->getUIPermissions()]
)->response(); // Add response to Resource
Laravel 文档中关于将一个部分归因于此的万岁: https://laravel.com/docs/5.5/eloquent-resources#resource-responses
此外,登录的 POST 请求没有设置 laravel_token,并且调用 refreshCsrfToken() 也没有成功,可能是因为它受到了来宾中间件的保护。
最终对我有用的是在登录功能返回(或承诺已履行)后立即对“/”执行虚拟调用。
最后我在组件中的登录功能如下:
login()
// Copy the user object
const data = ...this.user;
// If remember is false, don't send the parameter to the server
if(data.remember === false)
delete data.remember;
this.authenticating = true;
this.authenticate(data)
.then( csrf_token =>
window.Laravel.csrfToken = csrf_token;
window.axios.defaults.headers.common['X-CSRF-TOKEN'] = csrf_token;
// Perform a dummy GET request to the site root to obtain the larevel_token cookie
// which is used for authentication. Strangely enough this cookie is not set with the
// POST request to the login function.
axios.get('/')
.then( () =>
this.authenticating = false;
this.$router.replace(this.$route.query.redirect || '/');
)
.catch(e => this.showErrorMessage(e.message));
)
.catch( error =>
this.authenticating = false;
if(error.response && [422, 423].includes(error.response.status) )
this.validationErrors = error.response.data.errors;
this.showErrorMessage(error.response.data.message);
else
this.showErrorMessage(error.message);
);
而我的 vuex 商店中的 authenticate()
动作如下:
authenticate( dispatch , data)
return new Promise( (resolve, reject) =>
axios.post(LOGIN, data)
.then( response =>
const csrf_token, ...user = response.data;
// Set Vuex state
dispatch('setUser', user );
// Store the user data in local storage
Vue.ls.set('user', user );
return resolve(csrf_token);
)
.catch( error => reject(error) );
);
,
因为除了对/
的虚拟调用之外,我不想对refreshTokens
进行额外调用,因此我将 csrf_token 附加到后端的 /login 路由的响应中:
protected function authenticated(\Illuminate\Http\Request $request, $user)
$user->last_login = \Carbon\Carbon::now();
$user->timestamps = false;
$user->save();
$user->timestamps = true;
return (new UserResource($user))->additional([
'permissions' => $user->getUIPermissions(),
'csrf_token' => csrf_token()
])->response();
【讨论】:
【参考方案3】:您应该在 Web 中间件中使用 Passports CreateFreshApiToken 中间件passport consuming-your-api
web => [...,
\Laravel\Passport\Http\Middleware\CreateFreshApiToken::class,
],
此附件将正确的 csrftoken()
作为 request_cookies 附加到您的所有请求标头
【讨论】:
感谢您的建议,但建议已经到位,我正在使用 CreateFreshApiToken 中间件将 Vue js 前端和 laravel 后端(api 路由)托管到共享服务器?
】将Vuejs前端和laravel后端(api路由)托管到共享服务器?【英文标题】:HostingaVuejsfrontendandlaravelbackend(apiroutes)toasharedserver?【发布时间】:2019-12-0920:09:42【问题描述】:我用vuejs作为前端库,laravel作为后端框架构建了一个单页web... 查看详情
vue.js的“Firebase + Validation Example”中如何使用mysql作为数据持久化后端?
...【发布时间】:2016-04-2800:09:15【问题描述】:我正在学习Laravel5.2和vue.js。问题是:vue.js的《F 查看详情
使用 Laravel 和 Vue.js 分离前端和后端
】使用Laravel和Vue.js分离前端和后端【英文标题】:SeparationoffrontendandbackendwithLaravelandVue.js【发布时间】:2017-06-1618:54:54【问题描述】:我们正计划为工作申请开发一个网络应用程序。在头两年,我们预计每天的访问量约为3,000-6,0... 查看详情
我想从 laravel 中的 vue.js 代码运行工匠命令
】我想从laravel中的vue.js代码运行工匠命令【英文标题】:Iwanttorunartisancommandfromvue.jscodeinlaravel【发布时间】:2020-01-2102:53:15【问题描述】:我使用vue.js作为前端,使用laravel作为后端。单击按钮时,我想从代码中运行工匠命令。... 查看详情
如何使用 vue.js laravel 自动显示数据而不刷新
】如何使用vue.jslaravel自动显示数据而不刷新【英文标题】:Howtoshowdataautomaticallywithoutrefreshwithvue.jslaravel【发布时间】:2020-01-1422:13:52【问题描述】:我无法立即看到已成功输入数据库的数据。我在Laravel5.8框架中使用vue.js,带有a... 查看详情
如何在 Vue.js 中显示错误数组?使用 Laravel 进行后端验证
】如何在Vue.js中显示错误数组?使用Laravel进行后端验证【英文标题】:HowtoshowArrayoferrorsinVue.js?BackendValidationwithLaravel【发布时间】:2021-06-2303:45:18【问题描述】:我有一些复杂的数据,我想在vue文件中显示验证错误数组数据,但... 查看详情
vue.js 组件使用 laravel 刀片作为模板
】vue.js组件使用laravel刀片作为模板【英文标题】:vue.jscomponentusinglaravelbladeforthetemplate【发布时间】:2016-03-1809:31:43【问题描述】:我的想法是在vue/vueify组件的<template></template>中使用laravel刀片。我不确定如... 查看详情
Laravel 响应良好实践
】Laravel响应良好实践【英文标题】:Laravelresponsesgoodpractice【发布时间】:2019-01-1820:44:30【问题描述】:我正在使用Laravel5.6作为我的个人项目的后端,并且我一直在做一些(在我看来)是不好的做法,无论哪种方式,我都想知道... 查看详情
如何在 Laravel TailwindCss Vue Js 项目中添加和使用本地自定义字体?
】如何在LaravelTailwindCssVueJs项目中添加和使用本地自定义字体?【英文标题】:HowtoaddanduseLocalcustomfontinaLaravel+TailwindCss+Vuejsproject?【发布时间】:2021-06-0905:49:01【问题描述】:正如标题所示,我正在尝试在我的项目中添加和使用... 查看详情
LARAVEL + Vue.js:使用刀片在 MPA 中显示整个页面的 vue.js 组件,对 SEO 不友好
】LARAVEL+Vue.js:使用刀片在MPA中显示整个页面的vue.js组件,对SEO不友好【英文标题】:LARAVEL+Vue.js:usingbladetoshowwholepagevue.jscomponentsinMPA,notSEOfriendly【发布时间】:2019-02-1911:23:42【问题描述】:所以我正在考虑我的餐厅网页的结构,... 查看详情
Vue Js 作为单独安装或将 VueJs 与 Laravel 一起使用
】VueJs作为单独安装或将VueJs与Laravel一起使用【英文标题】:VueJsasseparateInstallationoruseVueJswithinLaravel【发布时间】:2020-10-2916:59:27【问题描述】:我们将使用Laravel和VueJs开始一个项目。我们需要的是我们是否将VueJs作为单独的安装... 查看详情
有没有更好的方法来处理 laravel 和 vue js 的 axios 错误
】有没有更好的方法来处理laravel和vuejs的axios错误【英文标题】:isthereanybetterwaytohandlingaxioserrorwithlaravelandvuejs【发布时间】:2018-08-2201:46:48【问题描述】:所以我正在使用laravel作为我的后端和vuejs作为spa前端框架来创建spa网络应... 查看详情
无法通过 Vue.js(Axios 帖子)从 Laravel 后端下载文件(pdf)
】无法通过Vue.js(Axios帖子)从Laravel后端下载文件(pdf)【英文标题】:Unabletodownloadafile(pdf)fromLaravelbackendviaVue.js(Axiospost)【发布时间】:2019-03-0416:28:50【问题描述】:我在Vue中有一个多步骤表单,一旦我收集了所有信息,我就... 查看详情
如何在 google firebase 上托管我的 laravel 项目
】如何在googlefirebase上托管我的laravel项目【英文标题】:HowdoIhostmylaravelProjectongooglefirebase【发布时间】:2020-01-1013:29:18【问题描述】:我正在做一个项目,我使用Vue.js作为前端,使用laravel作为后端,使用mysql作为数据库。现在我... 查看详情
需要在 vue.js 前端和 Laravel 后端打印名称而不是 id
】需要在vue.js前端和Laravel后端打印名称而不是id【英文标题】:Needtoprintnameinsteadofidinvue.jsfrontendandLaravelbackend【发布时间】:2021-08-0213:38:11【问题描述】:这是我的CategoryController.php代码...publicfunctionname($id)$f=Category::findOrFail($id);$n... 查看详情
Laravel 使用 Socket.io 广播到私人频道
】Laravel使用Socket.io广播到私人频道【英文标题】:LaravelBroadcasttoaprivatechannelusingSocket.io【发布时间】:2018-03-0320:06:03【问题描述】:我正在尝试使用Laravel作为后端API客户端和SPA作为前端创建一个实时通知系统,我使用React作为前... 查看详情
Laravel + Vue JS:从数据库中获取/存储 Eloquent 模型的值
】Laravel+VueJS:从数据库中获取/存储Eloquent模型的值【英文标题】:Laravel+VueJS:Get/storevalueofEloquentModelfromDB【发布时间】:2017-08-2014:32:58【问题描述】:我使用vue.js2为前端和Laravel作为后端API构建了一个SPA。现在我尝试在用户登录... 查看详情
如何使用 Laravel 作为 Angular SPA 应用程序的后端?
】如何使用Laravel作为AngularSPA应用程序的后端?【英文标题】:HowtouseLaravelasthebackendforanAngularSPAapp?【发布时间】:2020-07-1421:20:18【问题描述】:我想用Angular(前端)和Laravel(后端)创建一个网站。连接这两者的最佳方式是什么... 查看详情