Sessions dans l'authentification par token
je construis une application en PHP Lumen qui renvoie un token lors de la connexion. Je ne suis pas sûr de savoir comment aller au-delà de cela.
Comment puis-je maintenir une session en utilisant ces tokens?
plus précisément, comment stocker les tokens du côté client si j'utilise reactjs ou vanilla HTML/CSS/jQuery et les envoyer dans chaque demande que je fais pour la partie sécurisée de mon application web?
8 réponses
ce que je fais habituellement est de garder le token dans le stockage local, de cette façon je peux persister le token même si l'utilisateur quitte le site.
localStorage.setItem('app-token', theTokenFromServer);
chaque fois que l'utilisateur charge la page, la première chose que je fais est de chercher l'existence du token.
token = localStorage.getItem('app-token');
si j'utilisais react, je garderais le token dans l'état global (en utilisant redux par exemple):
function loadAppToken(token) {
return {
type: 'LOAD_TOKEN',
payload: { token },
};
}
avec javascript vanille je le garderais sur mon l'utilitaire de connexion. Ce qui pourrait ressembler à quelque chose comme ceci:
const token = localStorage.getItem('app-token');
export function request(config) {
const { url, ...others } = config;
return fetch(url, {
...others,
credentials: 'include',
headers: {
'Authorization': `Bearer ${token}`
},
});
}
j'aurais encore un utilitaire fetch dans une application react, similaire au code précédent, mais j'enverrais le token dans les options, en le mettant dans un middleware redux pour chaque requête.
travaille actuellement sur le même type d'application en utilisant lumen pour API. Les 3 étapes suivantes pour L'authentification par Token dans Lumen avec JWT :
1. Créer un Token et revenir après le succès de connexion
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. Définir middleware pour la vérification des tokens
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. Stocker le token dans localstorage ou dans les cookies
localStorage.setItem("Token", JSON.stringify(TokenData));
TokenData = JSON.parse(localStorage.getItem("Token"));
ou
$.cookie('Token', JSON.stringify(TokenData), {expires: 1, path: '/'});
TokenData = JSON.parse($.cookie("Token"));
4. Envoyer token avec chaque requête dans les en-têtes
Demande avec des en-têtes personnalisés
$.ajax({
url: 'foo/bar',
headers: { 'X-TOKEN': TokenData.Token ,'X-USER': TokenData.UserId}
});
en-Têtes à toutes les demandes
$.ajaxSetup({
headers: { 'X-TOKEN': TokenData.Token ,'X-USER': TokenData.UserId}
});
J'espère que ça aidera.
Note: ajouter des vérifications et des validations de données lecture des données de localstorage
ou cookies
.
supposons que vous voulez construire une APPLICATION avec.
- ReactJS
- REST API with PHP
- utilisant JWT
1. Introduction
vous devez oublier les sessions lors de la construction des API REST.
les API REST sont censées être apatrides, donc elles ne doivent pas dépendre des sessions, ils doivent traiter les demandes avec seulement les données fournies par le client.
2. Authentification
Tout ce que le client veut faire est d'échanger quelques username
et password
contre un jeton.
ceci est un exemple de requête HTTP
POST /api/v1/authentication HTTP/1.1
Host: localhost
Content-Type: application/json
{
"username": "foo",
"password": "bar"
}
et la réponse est:
{
"token": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}
3. allons plus de détails dans la demande /réponse
comment notre API traitera la demande d'authentification?
-
il va vérifier si un utilisateur avec un nom d'utilisateur
foo
& mot de passebar
est fondé et il est actif dans DB -
il générera un JWT (JSON Web Token)
-
Il sera de retour de réponse contenant le JWT
C'est une méthode d'auth très simple, par exemple.
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
}
comme vous le voyez, il n'y a pas de session ou quoi que ce soit.
comment notre client traitera-t-il la réponse?
le client pourrait utiliser un paquet comme superagent pour traiter les requêtes et les réponses à notre API de cette façon le processus sera simplifié à ceci:
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. Création de JWT côté serveur
vous pouvez utiliser un paquet PT pour générant et validant JWT au lieu de l'écrire vous-même.
Regardez ce package , vous pouvez voir comment il est fait.
et n'oubliez pas de toujours créer des signatures fortes.
Je recommande d'utiliser RSA keys
Je ne fais pas de publicité ou en soutenant ce projet, vous n'avez trouvé utile de le partager ici. Je ne l'avais jamais utilisé, j'utilise quelque chose de similaire sur mes projets NodeJS.
5. Sauver JWT sur le côté du client
ils sont deux façons comme vous le savez déjà localStorage
& cookies
Pour moi, j'utilise des cookies, parce que:
- Ils sont un peu plus sécurisé .
- date D'expiration peut être réglée sans implémenter une logique personnalisée.
- Vieux soutien de navigateur (très vieux navigateurs, donc ce n'est pas si important).
mais tout dépend de vous.
6. En utilisant JWT
à partir de maintenant sur chaque requête au serveur, vous devez inclure votre JWT.
dans votre API REST, vous devez écrire une méthode pour valider le JWT et l'échanger contre un objet utilisateur.
Exemple de demande:
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) {
});
comment L'API traitera cette demande
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. Date d'expiration & cookie
si vous utilisez un cookie pour enregistrer votre JWT, faites attention à la date d'expiration.
la date d'expiration du témoin doit être égale à la date d'expiration du JWT.
, Vous pouvez le stocker dans le navigateur localStorage, puis le mettre dans l'en-tête pour chaque requête au serveur.
vous n'avez pas besoin de ReactJS ou VanillaJS. Juste purement HTML et PHP en fait. Ce que je fais, c'est le stocker comme un biscuit.
tout d'abord, lorsque vous recevez le token de Lumen, enregistrez-le dans votre base de données utilisateur pour un utilisateur spécifique. Ensuite, définissez le nom d'utilisateur et accesstoken comme cookies qui expirent après un certain temps avec ce code:
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
vous trouverez ci-dessous l'apparence de votre page d'accueil. Il vérifie si accesstoken cookie existe, s'il existe, il vérifie que le jeton correspond à l'jeton dans la base de données utilisateur. Si c'est une correspondance, ça montre la page "connecté". Si ce n'est pas le cas, vous devriez afficher/rediriger vers la page de connexion.
<?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>
et puis se déconnecter est simple. Le code ci-dessous supprime accessstokens en les fixant à expiration:
setcookie("accesstoken", "", time() - 3600);
setcookie("userid", "", time() - 3600);
header('Location: /youareloggedout.html');
Souvenez-vous, c'est la base d'une fonctionnelle de connexion / déconnexion du système. Si j'expliquais toutes les mesures de sécurité nécessaires, ce poste serait encore plus long. Assurez-vous de faire vos recherches. Certains sujets pour vous lancer sont des déclarations préparées et la prévention des attaques XSS. :)
pour le chiffrement et le déchiffrement que vous pouvez utiliser dans le modèle de cryptage construit de laravel
utiliser Illuminate\Support\façades\Crypt;
ce que nous faisons pour générer un jeton APIs est de prendre un ensemble de champs requis.
créons des données
$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);
puis cryptez vos données avec Crypt
$accessToken = Crypt::encrypt($data);
envoyer pour front end in response et enregistrer dans le stockage local ou cookie tout ce qui n'est pas nécessaire pour le temps ici vérifiera sur le serveur seulement.
maintenant, dans chaque requête, passez ce token et du côté du serveur créez un logiciel intermédiaire qui analysera vos données et si votre temps de token est inférieur au temps d'expiration, puis allez de l'avant, sinon envoyez l'erreur 403 ou tout ce que vous voulez.
comment analyser les données du côté du serveur
créer middleware utilisant la commande : php artisan faire:middleware ApiAuth alors est de gérer la partie
//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);
}
Espérons que cela aidera :)
je vais écrire un bref résumé et les meilleures pratiques, car il y a plusieurs façons de le faire avec le code.
Backend
- (POST) de connexion de l'itinéraire {email, mot de passe} il crée un jeton. Vous pouvez utiliser JWT (JSON Web Token)) Le jeton sera retourné au client. À l'intérieur du token, vous pouvez stocker quelques détails de base: identifiant de l'utilisateur, nom d'utilisateur, Date d'expiration du jeton, type d'utilisateur, etc. https://jwt.io /
Client
-
demande de connexion, de passe {email, mot de passe}.
en cas de succès, récupérez le token et stockez-le localement, localstorage est préféré, mais cookie est également possible.
-
sur chaque page chargée avec votre application react, vous devriez avoir une vérification de la fonction pour ce token, il va le décrypter, et obtenir les détails pour une utilisation ultérieure.
je veux dire obtenir le nom d'utilisateur, id d'utilisateur etc. Plus important si vous voulez l'ajouter, est la "date d'expiration", si le jeton est expiré, vous rediriger l'utilisateur vers la page de connexion, OU vous pouvez re-demande d'un nouveau jeton, cela dépend vraiment de votre application.
-
déconnexion, c'est assez simple... retirez simplement le jeton de l'utilisateur et le rediriger vers la page de connexion.
-
assurez-vous que pour les pages "authentifiées", vous vérifiez que le token existe, et même en outre, vous pouvez vérifier le type d'utilisateur.
* * pour le décodage côté client de JWT, vous pouvez utiliser: https://www.npmjs.com/package/jwt-client
j'ai récemment terminé un portail web react où nous avons utilisé JWT pour lancer, maintenir et expirer la session de l'utilisateur.
- lors de la connexion, en envoyant des justificatifs d'identité à l'API de connexion. En cas de succès, récupérez le jeton de l'API back-end. Le Back-end maintient la génération et l'expiration des tokens.
- stocker le token dans l'état react (nous utilisons redux store) et dans le stockage de session (au cas où la page est rafraîchie, nous pouvons la récupérer à partir de la session stockage.)
- (optionnel) Démarrer un compteur par seconde dans le stockage de session (pour vérifier combien de temps l'utilisateur est inactif)
- après la connexion, chaque appel API nécessite que le token soit envoyé dans l'en-tête. Les appels API sont effectués en utilisant fetch. Si L'appel API est un succès, nous récupérons le token de back-end et nous le remplaçons par un token existant (stay fresh).
- tous les appels API sont 'fetch'ed via une fonction customFetch Générique. L'idée est de avoir un fetch générique pour voir si la réponse de fin est 401 (Accès refusé). S'il est 401, le token est expiré ou invalide (l'utilisateur essaie d'accéder à quelque chose sans se connecter). Dans ce cas, nous rejetons l'utilisateur du portail, retour à login/Page d'accueil (affichage de l'erreur que l'accès est refusé).
- (optionnel) si l'utilisateur est inactif pendant trop longtemps( vérification du deuxième compteur > 900 soit 15 min), nous montrons avertissement à l'utilisateur que la session est sur le point d'expirer, donne à l'utilisateur un choix de continuer. Si les clics de l'utilisateur continuent, nous appelons une API pour récupérer le profil de l'utilisateur à nouveau, en s'assurant que le token est toujours valide. Si L'API ne fonctionne pas, nous déconnectons l'utilisateur et le renvoyons à login/Page d'accueil. Le deuxième compteur renvoie à 1 juste avant que n'importe quel appel API soit fait (l'utilisateur est actif et fait quelque chose).
- Inutile de dire que, avant l'envoi d'utilisateur/page d'accueil par l'un des scénarios ci-dessus, nous effacer le stockage de session et rétablir l'état (redux stocker.)
- en cas de rafraîchissement, nous récupérons le token du stockage de session et envoyons les actions initiales pour construire à nouveau l'état (Redux store). Si l'une des actions (API) échoue, nous affichons le message à l'utilisateur que la session est expirée ou invalide et que vous devez vous connecter, ce qui renvoie l'utilisateur à la page de connexion/accueil.
fragments de Code
supposons que vous avez récupéré le token depuis l'appel de l'API de connexion:
jeu de jeton de session du stockage et de l'etat (redux store)
window.sessionStorage.setItem('partyToken', token)
store.dispatch({type: 'profile/setToken', payload: { token }})
jeton de récupération de session de stockage ou d'état (Redux store)
const token = window.sessionStorage.getItem('token')
const token = store.getState().profile && store.getState().profile.token
bien sûr, vous pouvez définir une fonction commune où vous pouvez définir/rafraîchir le token après chaque appel API. Similaire pour la récupération parce que vous avez besoin du token avant de faire appel à L'API.