我使用Laravel(5.5)作为后端,使用Vue(2.5)构建单页面应用程序。一切正常,除了在退出后再次直接登录。在这种情况下,对/ api / user的调用(以检索用户的帐户信息并再次验证用户的身份)失败,401未经授权(即使登录成功)。作为回应,用户直接退回到登录屏幕(我自己写了这个措施作为对401响应的反应)。
注销是什么,用ctrl / cmd + R刷新页面,然后再次登录。页面刷新修复了我的问题这一事实让我有理由相信我没有正确处理X-CSRF-TOKEN的刷新,或者可能忘记了Laravel使用的某些cookie(如here所述)。
这是在用户点击登录按钮后执行的登录表单代码的片段。
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日
在提出所有有用的建议之后,我想补充一些信息。我正在使用Passport在Laravel端进行身份验证,并且CreateFreshApiToken中间件已经到位。
我一直在查看我的应用设置的Cookie,特别是laravel_token
,据说持有加密的JWT,Passport将用它来验证来自JavaScript应用程序的API请求。注销时,会删除laravel_token cookie。之后直接再次登录(使用axios发送AJAX帖子请求)没有设置新的laravel_token
,这就是为什么它不对用户进行身份验证的原因。我知道Laravel没有在登录POST请求上设置cookie,但是之后直接应该设置cookie,对/ refreshTokens(没有保护)的GET请求。但是,这似乎并没有发生。
我已经尝试增加/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();
}
}
答案 0 :(得分:4)
考虑到您使用api进行身份验证,我建议您使用Passport或JWT Authentication来处理身份验证令牌。
答案 1 :(得分:2)
最后解决了!
通过在LoginControllers authenticated
方法中直接返回UserResource,它不是一个有效的Laravel响应(但我猜原始的JSON数据?)所以可能没有附加像cookies这样的东西。我不得不在资源上附加对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
}
Hurray为Laravel文档介绍了一个部分: https://laravel.com/docs/5.5/eloquent-resources#resource-responses
此外,laravel_token不是由POST请求设置登录的,而对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();
}
答案 2 :(得分:1)
您应该在Web中间件中使用Passports CreateFreshApiToken中间件passport consuming-your-api
web => [...,
\Laravel\Passport\Http\Middleware\CreateFreshApiToken::class,
],
此附件将权限csrftoken()
附加到您的所有请求标头为request_cookies