web-dev-qa-db-ja.com

トークンベースの認証のセッション

私はPHP Lumenでログイン時にトークンを返すLumenでアプリを構築しています。これ以上先に進む方法がわかりません。

これらのトークンを使用してセッションを維持するにはどうすればよいですか?

具体的には、reactjsまたはVanilla HTML/CSS/jQueryを使用している場合、クライアント側にトークンを保存し、Webアプリの安全な部分に対して行うすべてのリクエストでトークンを送信するにはどうすればよいですか?

20
Flame of udun

私が通常やることは、トークンをローカルストレージに保持することです。これにより、ユーザーがサイトを離れてもトークンを保持できます。

localStorage.setItem('app-token', theTokenFromServer);

ユーザーがページをロードするたびに、私が最初にすることはトークンの存在を探すことです。

token = localStorage.getItem('app-token');

反応を使用する場合、グローバル状態でトークンを保持します(たとえば、reduxを使用します)。

function loadAppToken(token) {
  return {
    type: 'LOAD_TOKEN',
    payload: { token },
  };
}

Vanilla javascriptを使用すると、接続ユーティリティに保持します。次のようになります。

const token = localStorage.getItem('app-token');

export function request(config) {
   const { url, ...others } = config;

   return fetch(url, {
     ...others,
     credentials: 'include',
     headers: {
       'Authorization': `Bearer ${token}`
     },
   });
}

以前のコードと同様に、反応アプリにはまだフェッチユーティリティがありますが、オプションごとにトークンを送信し、リクエストごとにreduxミドルウェアで取得します。

11
Crysfel

APPをビルドしたいと仮定しましょう。

  1. ReactJS
  2. PHPを使用したREST API
  3. [〜#〜] jwt [〜#〜] を使用する

1.はじめに

REST APIを作成するときは、セッションを忘れる必要があります。

REST APIはステートレスであることを意図しているため、セッションに依存してはならず、クライアントから提供されたデータのみでリクエストを処理する必要があります。

2.認証

クライアントがやりたいのは、トークンのusernamepasswordを交換することだけです。

これはHTTPリクエストの例です

POST /api/v1/authentication HTTP/1.1
Host: localhost
Content-Type: application/json
{
    "username": "foo",
    "password": "bar"
}

応答は次のとおりです。

{
    "token": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}

3.リクエスト/レスポンスの詳細を見てみましょう

APIが認証リクエストを処理する方法

  1. ユーザー名fooとパスワードbarを持つユーザーが見つかり、DBでアクティブかどうかを確認します

  2. JWT(Json Web Token)を生成します

  3. JWTを含む応答を返します

これは、たとえば非常に単純な認証方法です。

public function authAction()
{
  /** Get your payload somehow */
  $request = $_POST;

  //Validate if username & password are given/

  $user = $this->model->auth($username, $password);

  if(!$user) {
    //throw error for not valid credentials
  }

  $jwt = $this->jwt->create($user);

  //return response with $jwt
}

ご覧のとおり、セッションは設定されていません。

クライアント側が応答を処理する方法は?

クライアントは superagent のようなパッケージを使用して、APIへのリクエストとレスポンスを処理します。これにより、プロセスは次のように簡略化されます。

  let data = {
    username: email,
    password: password
  };

  request
    .post('/api/v1/authentication')
    .set('Content-Type', 'application/json')
    .send(data)
    .end(function (error, response) {
      //response.body.token
    });

4.サーバー側でJWTを作成する

generateおよびvalidatingJWTに3RD PTパッケージを使用できます自分で書いてください。

これを見てください package 、それがどのように行われたかを見ることができます。

そして、常に強力な署名を作成することを忘れないでください。 RSA keysの使用をお勧めします

私はこのプロジェクトを宣伝したりサポートしたりしていません。ここで共有するのが便利だとわかりました。 NodeJSプロジェクトではこれを使用したことはなく、これに似たものを使用しています。

5.クライアント側でJWTを保存する

既に知っているように、これらは2つの方法ですlocalStoragecookies私にとっては、私はcookieを使用しています。

  1. それらはもう少し secure です。
  2. 有効期限は、カスタムロジックを実装せずに設定できます。
  3. 古いブラウザのサポート(非常に古いブラウザなので、それほど重要ではありません)。

しかし、それはすべてあなた次第です。

6. JWTを使用する

これからサーバーへのすべてのリクエストで、JWTを含める必要があります。

REST= APIで、JWTを検証し、ユーザーオブジェクトと交換するメソッドを記述する必要があります。

リクエストの例:

  let jwt = ...; //GET IT FROM LOCALSTORAGE OR COOKIE

  request
    .get('/api/v1/posts')
    .set('Content-Type', 'application/json')
    .set('Authorization', jwt)
    .end(function (error, response) {

    });

APIがこのリクエストを処理する方法

public function postsAction()
{
  $jwt = $this->headers->get('Authorization');

  if(!$this->jwt->validate($jwt)) {
    //throw unauthorized error
  }

  $user = $this->model->exchangeJWT($jwt);

  //Your logic here
}

7.有効期限とCookie

Cookieを使用してJWTを保存する場合は、有効期限の設定に注意してください。

Cookieの有効期限はJWTの有効期限と等しくなければなりません。

5
Codew

現在、Lumen for APIを使用して同じタイプのアプリケーションで作業しています。 Lumen with JWT のトークンベースの認証の3つの手順に従います:

1。トークンを作成し、ログイン成功後に戻る

public function login(Request $request) {
    $token = $this->jwt->attempt(['user_name' => $data['user_name'], 'password' => $data['password']]); //$token = $this->jwt->attempt($data); 
    if (!$token) {
        $response = array('success' => false, 'data' => null, 'detail' => array('message' => Messages::MSG_INVALID_USER, 'error' => array(Messages::MSG_INVALID_USER)));
        return response()->json($response);
    } else {
        $user = \Auth::setToken($token)->user();
        $data = array('token' => $token,'user_id' => $user->id);
        $response = array('success' => true, 'data' => $data, 'detail' => array('message' => Messages::MSG_SUCCESS, 'error' => null));
        return response()->json($response);
    }
}

2。トークン検証用のミドルウェアを定義する

public function handle($request, Closure $next, $guard = null) {
    try {
        $token = $request->header('X-TOKEN');
        $user_id = $request->header('X-USER');
        $user = \Auth::setToken($token)->user();
        if ($user && $user->id == $user_id) {
            return $next($request);
        } else {
            $response = array('success' => false, 'data' => null, 'detail' => array('message' => Messages::MSG_ERR_INVALID_TOKEN, 'error' => Messages::MSG_ERR_INVALID_TOKEN));
            return response()->json($response);
        }
    } catch (Exception $ex) {
        $response = array('success' => false, 'data' => null, 'detail' => array('message' => Messages::MSG_ERROR_500, 'error' => array($ex)));
        return response()->json($response);
    }
}

3。 localstorageまたはcookieにトークンを保存します

localStorage.setItem("Token", JSON.stringify(TokenData));
TokenData = JSON.parse(localStorage.getItem("Token"));

または

$.cookie('Token', JSON.stringify(TokenData), {expires: 1, path: '/'});
TokenData = JSON.parse($.cookie("Token"));

4。ヘッダーのすべてのリクエストでトークンを送信します

カスタムヘッダー付きのリクエスト

$.ajax({
    url: 'foo/bar',
    headers: { 'X-TOKEN': TokenData.Token ,'X-USER': TokenData.UserId}
});

すべてのリクエストへのヘッダー

$.ajaxSetup({
        headers: { 'X-TOKEN': TokenData.Token ,'X-USER': TokenData.UserId}
    });

それが役立つことを願っています。

注:localstorageまたはcookiesからデータを読み取りながら、いくつかのチェックとデータ検証を追加します。

3
Govind Samrow

ブラウザのlocalStorageに保存し、サーバーへの各リクエストのヘッダーに設定できます。

2

暗号化と復号化のために、構築されたlaravelの暗号モデルで使用できます

Illuminate\Support\Facades\Cryptを使用

APIトークンを生成するために行うことは、必須フィールドの配列を取ります。

データを作成しましょう

$data = [
    'user_id' => $user->id,
    'time_stemp' => \Carbon::now() // Carbon is laravel's time model(class) for managing times
    'expire_on' => \Carbon::now()->addDays(2); //here i'm setting token expires time for 2 days you can change any
];

$data = serialize($data);

次に、Cryptでデータを暗号化します

$accessToken = Crypt::encrypt($data);

応答としてフロントエンドに送信し、ローカルストレージまたはCookieに保存します。ここで時間の必要のないものはすべて、サーバーでのみチェックします。

これで、すべてのリクエストでそのトークンを渡し、サーバー側でデータを解析するミドルウェアを1つ作成し、トークン時間が短い場合は期限切れになり、それ以外の場合はエラー403または必要なものを送信します。

サーバー側でデータを解析する方法

コマンドを使用してミドルウェアを作成します:php artisan make:middleware ApiAuth then is handle part

//Accesstoken you passed in $headers or in $request param use whatever you like
$searilizerData = Crypt::decrypt($headers['AccessToken']);
$data = unserialize($searilizerData);
//check if expire_on is less then current server time
if($data['expire_on] <= \Curbon::now()){
   next(); // let them contuine and access data
} else {
      throw new Exception ("Your token has expired please regenerate your token",403);
}

これが役立つことを願っています:)

2
Binit Ghetiya

実際には、ReactJSやVanillaJSは必要ありません。純粋なHTMLとPHP実際。私がしているのは、それをCookieとして保存することです。

まず、Lumenからトークンを受け取ったら、特定のユーザーのユーザーデータベースにトークンを保存します。次に、ユーザーIDとaccesstokenをCookieとして設定します。Cookieは、次のコードを使用して一定時間後に有効期限が切れます。

setcookie('userid',$userid, time()+(3600 * 24 * 15),"/");
setcookie('accesstoken',$accesstoken, time()+(3600 * 24 * 15),"/");
header('Location: /home.php');
//You can change the 15 in setcookie() to amount of days the cookie will expire in.
//The "/" in setcookie is important, because it ensures the cookies will be available on every page the user visits on your website.
//The header function redirects to your home page after log in

次に、ホームページがどのように表示されるかを示します。 accesstoken cookieが存在するかどうかを確認し、存在する場合は、トークンがユーザーデータベース内の現在のトークンと一致することを二重に確認します。一致する場合は、「ログイン」ページが表示されます。そうでない場合は、ログインページを表示/リダイレクトする必要があります。

<?php
if (isset($_COOKIE['accesstoken']))
{
//connect to your user database and check that the cookie accesstoken matches
// if it doesn't match, deal with it appropriately, such as deleting all cookies then redirecting to login page.
}
?>
<!DOCTYPE HTML>
<html>
<head>
<title>Sup</title>
</head>
<body>
<?php if (isset($_COOKIE['accesstoken'])){ ?>

<h1>User logged in!</h1>
<h3>Do whatever you need to do if user is logged in</h3>

<?php } else { ?>

<h1>No accesstoken found</h1>
<h3>More than likely you will want to show login page here</h3>

<?php } ?>
</body>
</html>

ログアウトするのは簡単です。以下のコードは、アクセストークンを期限切れに設定して削除します。

setcookie("accesstoken", "", time() - 3600);
setcookie("userid", "", time() - 3600);
header('Location: /youareloggedout.html');

これは、機能的なログイン/ログアウトシステムの基本であることに注意してください。必要なすべてのセキュリティ対策を説明すると、この投稿はさらに長くなります。必ず調査を行ってください。始めるためのいくつかのトピックは、準備されたステートメントとXSS攻撃の防止です。 :)

1
Rogi Solorzano

最近、JWTを使用してユーザーのセッションを開始、維持、および期限切れにする反応Webポータルを終了しました。

  1. ログイン時に、ユーザー資格情報をログインAPIに送信します。成功したら、バックエンドAPIからトークンを取得します。バックエンドは、トークンの生成と有効期限を維持します。
  2. トークンをリアクティブ状態(reduxストアを使用)およびセッションストレージに保存します(ページが更新された場合、セッションストレージから取得できます)。
  3. (オプション)セッションストレージで1秒あたりのカウンターを開始します(ユーザーがアイドル状態である時間を確認します)
  4. ログイン後、すべてのAPI呼び出しでは、ヘッダーでトークンを送信する必要があります。 API呼び出しは、フェッチを使用して行われます。 API呼び出しが成功した場合、バックエンドからトークンを取得し、既存のトークンに置き換えます(最新の状態を維持します)。
  5. すべてのAPI呼び出しは、汎用のcustomFetch関数を介して「フェッチ」されます。アイデアは、バックエンドの応答が401(アクセスが拒否された)かどうかを確認するための一般的なフェッチを持つことです。 401の場合、トークンは期限切れまたは無効です(ユーザーはログインせずに何かにアクセスしようとしています)。この場合、ユーザーをポータルから追い出し、ログイン/ホームページに戻ります(アクセスが拒否されたというエラーを表示します)。
  6. (オプション)ユーザーがあまりにも長い間アイドル状態になっている場合(2番目のカウンターが900を超えている、つまり15分)、セッションの有効期限が切れようとしているという警告をユーザーに表示し、ユーザーに続行の選択肢を提供します。ユーザーが続行をクリックすると、APIを呼び出してユーザーのプロファイルを再度取得し、トークンがまだ有効であることを確認します。 APIが成功しない場合、ユーザーをログアウトし、ログイン/ホームページに送り返します。 2番目のカウンターは、API呼び出しが行われる直前に1に戻ります(ユーザーはアクティブで、何かを実行しています)。
  7. 言うまでもなく、上記のシナリオのいずれかによってユーザーをログイン/ホームページに送信する前に、セッションストレージをクリアし、状態(reduxストア)をリセットします。
  8. 更新が発生した場合、セッションストレージからトークンを取得し、初期アクションをディスパッチして状態(reduxストア)を再度構築します。アクション(API)のいずれかが失敗した場合、セッションが期限切れまたは無効であるというメッセージをユーザーに表示します。ログインする必要があるため、ログイン/ホームページにユーザーを送り返します。

コードスニペット

ログインAPI呼び出しからトークンを取得したと仮定します。

セッションストレージと状態にトークンを設定します(reduxストア)

window.sessionStorage.setItem('partyToken', token)
store.dispatch({type: 'profile/setToken', payload: { token }})

セッションストレージまたは状態からのトークンの取得(reduxストア)

const token = window.sessionStorage.getItem('token')
const token = store.getState().profile && store.getState().profile.token

もちろん、API呼び出しごとにトークンを設定/更新できる共通の関数を定義できます。 API呼び出しを行う前にトークンが必要になるため、取得についても同様です。

0
Umesh

コードでそれを行うには多くの方法があるので、簡単な作業とベストプラクティスを書き留めます。

バックエンド

  • (POST)ログインルート{メール、パスワード}トークンを作成します。 JWT(Json Web Token)を使用できます。トークンはクライアントに返されます。トークン内に、ユーザーID、ユーザー名、トークンの有効期限、ユーザータイプなどの基本的な詳細を保存できます。 https://jwt.io/

クライアント

  • ログインリクエスト、{メール、パスワード}を渡します。

    成功したら、トークンを取得してローカルに保存します。localstorageが推奨されますが、Cookieも使用できます。

  • 反応アプリでページをロードするたびに、そのトークンの機能チェックを行う必要があります。トークンは復号化され、さらに使用するために詳細を取得します。

    ユーザー名、ユーザーIDなどを取得することを意味します。追加する場合は「有効期限」が重要です。トークンの有効期限が切れた場合、ユーザーをログインページにリダイレクトしますOR you新しいトークンを再リクエストできます。アプリによって異なります。

  • ログアウトは非常に簡単です...単にクライアント側からトークンを削除し、ログインページにリダイレクトします。

  • 「認証された」ページでは、トークンが存在することを確認し、さらにユーザータイプを確認できることを確認してください。

** JWTのクライアント側のデコードには、次を使用できます。 https://www.npmjs.com/package/jwt-client

0
Tzook Bar Noy