Je crée une application en PHP Lumen qui retourne un jeton lors de la connexion. Je ne sais pas comment procéder au-delà.
Comment suis-je censé maintenir une session en utilisant ces jetons?
Plus précisément, comment puis-je stocker les jetons 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?
Ce que je fais habituellement, c'est de conserver le jeton dans le stockage local, de cette façon, je peux conserver le jeton 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 rechercher l'existence du jeton.
token = localStorage.getItem('app-token');
Si j'utilise react, je garderais le jeton sur l'état global (en utilisant redux par exemple):
function loadAppToken(token) {
return {
type: 'LOAD_TOKEN',
payload: { token },
};
}
Avec Vanilla javascript, je le garderais sur mon utilitaire de connexion. Ce qui pourrait ressembler à 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 toujours un utilitaire de récupération dans une application React, similaire au code précédent, mais j'enverrais le jeton dans les options, en le plaçant dans un middleware redux pour chaque demande.
Supposons que vous souhaitiez créer une application avec.
Vous devez oublier les sessions lors de la construction des API REST.
Les API REST sont censées être sans état, elles ne doivent donc pas dépendre des sessions, elles doivent traiter les demandes avec uniquement les données fournies par le client.
Tout ce que le client veut faire, c'est seulement échanger des username
et password
contre un token.
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"
}
Comment notre API traitera-t-elle la demande d'authentification?
Il vérifiera si un utilisateur avec un nom d'utilisateur foo
et un mot de passe bar
est fondé et est actif dans la base de données
Il va générer un JWT (Json Web Token)
Il renverra une réponse contenant le JWT
Il s'agit d'une méthode d'authentification super 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, aucune session n'est définie ou quoi que ce soit.
Comment notre côté client traitera-t-il la réponse?
Le client pourrait utiliser un package comme superagent pour gérer les demandes et les réponses à notre API de cette façon, le processus sera simplifié comme suit:
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
});
Vous pouvez utiliser un package 3RD PT pour générer et valider JWT au lieu de l'écrire vous-même.
Regardez ceci package , vous pouvez voir comment cela se fait.
Et n'oubliez pas de toujours créer des signatures fortes. Je recommande d'utiliser RSA keys
Je ne fais pas de publicité ni ne soutiens ce projet, je l'ai trouvé utile de le partager ici. Je ne l'avais jamais utilisé, j'utilise quelque chose de similaire à cela sur mes projets NodeJS.
Il y a deux façons comme vous le savez déjà localStorage
& cookies
Pour moi, j'utilise des cookies, car:
Mais tout dépend de vous.
Désormais, chaque demande adressée au serveur doit inclure votre JWT.
Dans votre REST API, 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
}
Si vous utilisez des cookies pour enregistrer votre JWT, soyez prudent avec la définition des dates d'expiration.
La date d'expiration du cookie doit être égale à la date d'expiration de JWT.
Travaille actuellement sur le même type d'application en utilisant Lumen pour l'API. En suivant 3 étapes pour l'authentification basée sur les jetons dans Lumen avec JWT :
1. Créer un jeton et revenir après la connexion réussie
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 le middleware pour la vérification des jetons
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 jeton dans le stockage local 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 un jeton avec chaque demande dans les en-têtes
Demande avec en-têtes personnalisés
$.ajax({
url: 'foo/bar',
headers: { 'X-TOKEN': TokenData.Token ,'X-USER': TokenData.UserId}
});
En-têtes de chaque demande
$.ajaxSetup({
headers: { 'X-TOKEN': TokenData.Token ,'X-USER': TokenData.UserId}
});
J'espère que cela vous aidera.
Remarque: Ajoutez des vérifications et des validations de données lors de la lecture des données de localstorage
ou cookies
.
Vous pouvez le stocker dans le localStorage du navigateur, puis le définir dans l'en-tête pour chaque demande au serveur.
Pour le chiffrement et le déchiffrement, vous pouvez utiliser le modèle de chiffrement de laravel intégré
tilisez Illuminate\Support\Facades\Crypt;
Ce que nous faisons pour générer un jeton d'API, c'est prendre un tableau de champs obligatoires.
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);
Maintenant, envoyez à la partie frontale en réponse et enregistrez dans le stockage local ou dans un cookie tout ce qui n'est pas nécessaire ici vérifiera uniquement le serveur.
Maintenant, à chaque demande, passez ce jeton et côté serveur, créez un logiciel intermédiaire qui analysera vos données et si votre temps de jeton est inférieur, puis expirez, puis continuez sinon envoyez l'erreur 403 ou tout ce que vous voulez.
Comment analyser les données côté serveur
Créer un middleware en utilisant la commande: php artisan make: middleware ApiAuth est alors la partie de la poignée
//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);
}
J'espère que cela vous aidera :)
En fait, vous n'avez pas besoin de ReactJS ou VanillaJS. Juste du HTML pur et PHP en fait. Ce que je fais, c'est simplement le stocker comme cookie.
Tout d'abord, lorsque vous recevez le jeton de Lumen, enregistrez-le dans votre base de données utilisateur pour un utilisateur spécifique. Ensuite, définissez l'ID utilisateur et l'accès 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
Ensuite, voici à quoi ressemblerait votre page d'accueil. Il vérifie si le cookie accesstoken existe, s'il le fait, il vérifie à nouveau que le jeton correspond au jeton actuel dans la base de données utilisateur. S'il s'agit d'une correspondance, la page "connecté" s'affiche. Sinon, vous devez 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>
puis se déconnecter est simple. Le code ci-dessous supprime les accesstokens en les définissant comme expirés:
setcookie("accesstoken", "", time() - 3600);
setcookie("userid", "", time() - 3600);
header('Location: /youareloggedout.html');
Rappelez-vous, c'est la BASE d'un système fonctionnel de connexion/déconnexion. Si j'expliquais toutes les mesures de sécurité nécessaires, ce message serait encore plus long. Assurez-vous de faire vos recherches. Certains sujets pour commencer sont les instructions préparées et la prévention des attaques XSS. :)
J'ai récemment terminé un portail Web React où nous avons utilisé JWT pour lancer, maintenir et expirer la session de l'utilisateur.
Extraits de code
Supposons que vous ayez récupéré le jeton depuis l'appel d'API de connexion:
définir le jeton dans le stockage et l'état de la session (magasin redux)
window.sessionStorage.setItem('partyToken', token)
store.dispatch({type: 'profile/setToken', payload: { token }})
jeton de récupération à partir du stockage ou de l'état de la session (magasin redux)
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/actualiser le jeton après chaque appel d'API. Similaire pour la récupération, car vous avez besoin du jeton avant d'effectuer un appel API.
Je vais écrire une tâche rapide et les meilleures pratiques, car il existe de nombreuses façons de le faire avec du code.
demande de connexion, passez {email, mot de passe}.
En cas de succès, obtenez le jeton et stockez-le localement, le stockage local est préféré, mais le cookie est également possible.
à chaque chargement de page avec votre application React, vous devriez vérifier la fonction de ce jeton, il le déchiffrera et obtiendra les détails pour une utilisation ultérieure.
Je veux dire obtenir le nom d'utilisateur, l'ID utilisateur, etc. Plus important si vous voulez l'ajouter, c'est "l'expiration", si le jeton a expiré, vous redirigez l'utilisateur vers la page de connexion, OR vous peut demander à nouveau un nouveau jeton, cela dépend vraiment de votre application.
la déconnexion, est assez simple ... il suffit de retirer le jeton du côté client et de le rediriger vers la page de connexion.
Assurez-vous que pour les pages "authentifiées", vous vérifiez que le jeton existe, et encore plus 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