Dan*_*eij 7 php jwt laravel single-page-application vue.js
我使用Laravel(5.5)作为后端,使用Vue(2.5)构建单页应用程序.一切正常,除了在退出后再次直接登录.在这种情况下,对/ api/user的调用(以检索用户的帐户信息并再次验证用户的身份)失败,401未经授权(即使登录成功).作为回应,用户直接退回到登录屏幕(我自己写了这个措施作为对401响应的反应).
注销是什么,用ctrl/cmd + R刷新页面,然后再次登录.一个页面刷新解决我的问题,这一事实让我有理由相信,我不处理X-CSRF-TOKEN的刷新正确,也可以忘掉那Laravel使用某些cookie(如描述在这里).
这是在用户单击登录按钮后执行的登录表单代码的片段.
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);
});
});
},
Run Code Online (Sandbox Code Playgroud)
该authenticate()方法是一个vuex动作,它调用laravel端的登录端点.
/ refreshTokens端点只是调用此Laravel控制器函数,该函数返回当前登录用户的CSRF令牌:
public function getCsrfToken(){
return ['csrfToken' => csrf_token()];
}
Run Code Online (Sandbox Code Playgroud)
在重新获取令牌之后,用户被重定向到主页面(或提供的另一页面),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,但是之后直接对/ 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();
}
}
Run Code Online (Sandbox Code Playgroud)
终于修好了!
通过直接在 LoginControllers 方法中返回 UserResource authenticated,它不是有效的 Laravel 响应(但我猜原始 JSON 数据?),所以可能没有附加像 cookie 这样的东西。我必须在资源上附加对 response() 的调用,现在一切似乎都工作正常(尽管我需要进行更广泛的测试)。
所以:
protected function authenticated(\Illuminate\Http\Request $request, $user)
{
...
return (new UserResource($user))->additional(
['permissions' => $user->getUIPermissions()]
);
}
Run Code Online (Sandbox Code Playgroud)
变成
protected function authenticated(\Illuminate\Http\Request $request, $user)
{
...
return (new UserResource($user))->additional(
['permissions' => $user->getUIPermissions()]
)->response(); // Add response to Resource
}
Run Code Online (Sandbox Code Playgroud)
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);
}
});
Run Code Online (Sandbox Code Playgroud)
我的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) );
});
},
Run Code Online (Sandbox Code Playgroud)
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();
}
Run Code Online (Sandbox Code Playgroud)