2017-10-31 16 views
6

私はLaravel(5.5)をバックエンドとして使用してVue(2.5)を使用してシングルページアプリケーションを構築しています。ログアウトした後に直接ログインする以外はすべてうまく動作します。この場合、/ api/user(ユーザーのアカウント情報を取得してもう一度ユーザーのIDを確認する)への呼び出しは、401が許可されていないと失敗します(ログインに成功したとしても)。応答として、ユーザーはログイン画面に直接戻されます(私はこの対策を自分で401応答への反応として書きました)。バックエンドにLaravelを使用したVue.js SPAの認証トークンのリフレッシュ

ログアウトするには、ctrl/cmd + Rでページを更新してから、もう一度ログインしてください。ページリフレッシュが私の問題を解決するという事実は、私がX-CSRF-TOKENのリフレッシュを正しく処理していないと信じる理由を与えるか、またはLaravelが使用する特定のクッキーを忘れている可能性があります(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()方法はlaravel側のログインエンドポイントを呼び出すvuexアクションです。トークンが再フェッチされた後、ユーザは、(メインページにリダイレクトされ

public function getCsrfToken(){ 
    return ['csrfToken' => csrf_token()]; 
} 

/refreshTokensエンドポイントは単純に現在ログインしているユーザーのCSRFトークンを返し、このLaravelコントローラ機能を呼び出しますまたは別のページが指定されている場合) とthis.$router.replace(this.$route.query.redirect || '/');があり、そこには現在ログインしているユーザーのデータをチェックするために関数が呼び出されます。

この作業を行うために必要な対策はありますか?見落としていますか?

ありがとうございました!


EDIT:すべての役立つ提案した後、2017年11月7日

、私はいくつかの情報を追加したいと思います。私はLaravel側でPassportを使って認証しており、CreateFreshApiTokenミドルウェアが使用されています。

アプリで設定したCookie、特にPassportがJavaScriptアプリケーションからのAPIリクエストを認証するために使用する暗号化されたJWTを保持すると言われているlaravel_tokenがあります。ログアウトすると、laravel_tokenのクッキーが削除されます。その後すぐに再びログインすると(Axisを使ってAJAXの投稿要求を送信する)、新しいlaravel_tokenは設定されていないので、ユーザーを認証しません。私はLaravelがログインPOSTリクエストでクッキーを設定していないが、その後に/ 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

'ログアウト'機能はどのように実装されていますか? – vtosh

+0

ポストリクエストでlaravelのログアウト機能を呼び出します。単に 'axios.post( '/ logout')'; –

+0

ログアウト/ログインのAjaxリクエストが前後に進むのを見ると、新しいトークンが生成され、/ refreshtokensによって返され、その後の(そして失敗した)サーバーへの呼び出しで使用されるトークンが表示されます。 –

答えて

1

最後にそれを修正!

LoginControllers authenticatedメソッドでUserResourceを直接返すことで、有効なLaravelレスポンスではありません(しかし、生のJSONデータと思われますか?)ので、おそらくクッキーのようなものは添付されません。リソース上でresponse()を呼び出す必要がありましたが、今ではすべてがうまくいくように思えます(さらに広範なテストを行う必要があります)。だから、

:また、laravel_tokenは、POSTリクエストによって設定されていない https://laravel.com/docs/5.5/eloquent-resources#resource-responses

protected function authenticated(\Illuminate\Http\Request $request, $user) 
{ 
    ... 

    return (new UserResource($user))->additional(
     ['permissions' => $user->getUIPermissions()] 
    ); 
} 

このセクションに、帰属のLaravelのドキュメントのための
protected function authenticated(\Illuminate\Http\Request $request, $user) 
{ 
    ... 

    return (new UserResource($user))->additional(
     ['permissions' => $user->getUIPermissions()] 
    )->response(); // Add response to Resource 
} 

万歳となりまた、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への追加コールを行うために、バックエンドの/ loginルートの応答にcsrf_tokenを添付しました。

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

あなたは認証のためのAPIを使用している、私が処理するためにPassportJWT Authenticationを使用することをお勧めしていることを考慮すると認証トークン。

+0

ありがとうございます。私はパスポートを使用しています。それは自動的にhttps://laravel.com/docs/5.5/passport#consuming-your-api-with-javascriptによるJWT認証の世話をするべきであることを言う。これは、ログアウトのログインの問題を除いてうまくいきます。私はhttps://laravel.com/docs/5.5/csrf#csrf-x-csrf-tokenに応じて各応答と共に返されるCookieデータを正しく処理していないという考えを持っています –

+0

ああ、申し訳ありませんでした。私はあなたの構成とセットアップを調べて、それがすべて正しいことを確認します。 – CUGreen

+0

私はすでに行っていましたが、何か不具合が見つかりませんでした。私の手順は正しいと思うが、パズルの一部(クッキーデータを扱うようなもの)が欠けている。私はこの問題にぶつかる最初の人だとは思わないので、誰かが間違いを見つけてくれることを願う。 –

1

あなたはpassport consuming-your-api

web => [..., 
    \Laravel\Passport\Http\Middleware\CreateFreshApiToken::class, 
], 

これはrequest_cookies

として、すべてのリクエストヘッダに右 csrftoken()を添付添付ウェブミドルウェアでパスポートCreateFreshApiTokenミドルウェアを使用する必要があります
+0

提案してくれてありがとうございましたが、それはすでに存在していました。私はCreateFreshApiTokenミドルウェア –

関連する問題