¿Qué es JWT? Este artículo lo llevará a comprender JWT, presentar la aplicación de JWT en el nodo y las ventajas y desventajas de JWT. ¡Espero que sea útil para todos!
JWT es la abreviatura de JSON Web Token , que es una solución de autenticación en el entorno de aplicaciones de red. En el mecanismo de autenticación tradicional, no son más que los siguientes pasos:
1. El usuario envía la contraseña de la cuenta al servidor; 2. Después de que el servidor verifique la contraseña de la cuenta, guardará cierta información relacionada con el usuario, roles de usuario o tiempo de vencimiento, etc. en la sesión actual; 3. El servidor le da al usuario un ID de sesión y lo escribe en la cookie del usuario o el cliente lo guarda localmente; 4. Cada vez que el usuario solicita un servicio, necesita traer este session_id, quizás a través de cookies u otros métodos; 5. Una vez que el servidor lo recibe, regresa a la base de datos para consultar el ID de sesión actual y verificar si el usuario tiene permiso;
Una ventaja de este modelo es que el servidor puede cancelar los permisos del usuario en cualquier momento y puede ir a la base de datos para modificar o eliminar la información de la sesión del usuario actual. Pero también hay una desventaja: si se trata de un clúster de servidores, todas las máquinas deben compartir la información de la sesión para garantizar que cada servidor pueda obtener la misma información de almacenamiento de la sesión. Aunque estos problemas pueden resolverse, la cantidad de trabajo es enorme.
La ventaja de la solución JWT es que esta información no se guarda. Los datos del token se guardan en el cliente. Cada vez que se acepta una solicitud, solo es necesario verificarla.
Hablemos brevemente sobre el principio de JWT. De hecho, cuando el cliente envía una solicitud de autenticación, el servidor generará un objeto JSON después de autenticar al usuario, que probablemente incluya información como "quién es usted, qué hace, etc. ., tiempo de vencimiento ", lo importante es que debe haber un tiempo de vencimiento; el formato general es:
{ nombre de usuario: "ladrón de cadenas", rol: "Granjero de códigos mundial", endTime: "20 de mayo de 2022" }
Pero no se le pasará de una manera tan superficial. Se firmará y transmitirá mediante un algoritmo de firma reversible basado en el algoritmo de firma especificado y cierta información sobre la carga útil que envió. Usaré una imagen para representar el formato general. :
Como se puede ver en la imagen, la información devuelta se divide aproximadamente en tres partes. El lado izquierdo es el resultado después de la firma, que es el resultado devuelto al cliente. El lado derecho también es el código fuente decodificado. separados por "puntos", respectivamente. Existe una correspondencia uno a uno entre los tres colores rojo, violeta y cian:
La primera parte roja es el encabezado. El encabezado especifica principalmente el método. El algoritmo de firma en la imagen ( HS256 predeterminado ) es HMAC con SHA-256. Solo se comparte una clave entre las dos partes, tipo The. el identificador de campo es de tipo JWT;
La segunda parte violeta, la carga útil, es un objeto JSON, que son los datos reales que se transmitirán. Hay siete campos oficiales que se pueden usar:
iss (emisor): emisor
exp (tiempo de vencimiento): tiempo de vencimiento
sub (asunto):asunto
aud (audiencia): audiencia
nbf (no antes): tiempo efectivo
iat (Emitido en): momento de emisión
jti (ID JWT): número
Además de estos campos, también puede crear algunos campos personalizados. Dado que JWT no está cifrado de forma predeterminada, trate de tener cuidado de no utilizar algunos datos confidenciales al usarlo.
La tercera parte es la Signature
. Esta parte es una clave secreta que usted especifica y existe solo en el servidor, y luego utiliza el algoritmo especificado en el encabezado para firmar mediante el siguiente método de firma.
Experimentemos el uso específico a continuación:
Paso 1: Necesitamos construir un proyecto nodejs; inicializar un proyecto a través de npm init -y
luego necesitamos instalar dependencias, incluidas express
, jsonwebtoken
y nodemon
:
$ npm expreso jsonwebtoken nodemon
Luego agregue el comando nodemon app.js
en el campo scripts
en package.json
:
"guiones": { "inicio": "nodemon app.js" },
Paso 2: inicialice la aplicación del nodo y cree el archivo app.js en el directorio raíz;
// aplicación.js const expreso = requerir("expreso"); aplicación constante = expresar(); aplicación.use(express.json()); aplicación.listen(3000, () => { console.log(3000 + "escuchando..." // Escucha el puerto 3000});
Paso 3: Introduzca jsonwebtoken
y cree la clave privada de la interfaz y el servidor;
// aplicación.js //... const jwt = requerir("jsonwebtoken"); const jwtKey = "~!@#$%^&*()+,"; //...
jwtKey
aquí es nuestra clave privada personalizada que solo se guarda en el servidor. Después de eso, comenzamos a escribir una interfaz /login para iniciar sesión, creamos una base de datos de simulación local para la verificación y la realizamos a través del método jwt.sign
. Verificar firma:
// aplicación.js base de datos constante = { nombre de usuario: "nombre de usuario", contraseña: "contraseña", }; aplicación.post("/iniciar sesión", (req, res) => { const {nombre de usuario, contraseña} = req.body; if (nombre de usuario === base de datos.nombre de usuario && contraseña === base de datos.contraseña) { jwt.signo( { nombre de usuario, }, clave jwt, { caducaEn: "30S", }, (_, token) => { res.json({ nombre de usuario, mensaje: "Inicio de sesión exitoso", simbólico, }); } ); } });
En el código anterior, creamos database
para simular la creación de una cuenta local y una base de datos de contraseñas para verificar el inicio de sesión, luego establecimos una interfaz post
/login
. Después de verificar que la cuenta y la contraseña coincidan completamente, las importamos a través de jsonwebtoken
paquete Utilice el método sign
debajo del objeto jwt
para firmar. Este método tiene tres firmas de interfaz:
signo de función de exportación ( carga útil: cadena | objeto de búfer, secretOrPrivateKey: Secreto, ¿opciones?: Opciones de signo, ): cadena; signo de función de exportación ( carga útil: cadena | objeto de búfer, secretOrPrivateKey: Secreto, devolución de llamada: SignCallback, ): vacío; signo de función de exportación ( carga útil: cadena | objeto de búfer, secretOrPrivateKey: Secreto, opciones: Opciones de firma, devolución de llamada: SignCallback, ): vacío;
El método de sobrecarga de funciones se utiliza aquí para implementar la interfaz. Aquí implementaremos la última firma de la interfaz. El primer object
puede ser un tipo de objeto personalizado, un tipo de Buffer
o directamente un tipo string
. y personaliza algunos campos, porque JWT también firmará estos datos al firmar. Sin embargo, vale la pena señalar que debe intentar no utilizar datos confidenciales aquí porque JWT no está cifrado de forma predeterminada. no ha sido manipulado y el proceso de verificación de la firma se llama verificación .
Por supuesto, también puedes cifrar el Token original y luego transmitirlo;
El segundo parámetro: es la clave secreta que guardamos en el servidor para firmar. Generalmente en el modo cliente-servidor, JWS usa el algoritmo HS256 proporcionado por JWA más una clave. Es posible que varios servicios necesiten verificar JWT. Si la clave se guarda en cada servicio, la seguridad se reducirá considerablemente. Debe saber que una vez que se filtra la clave, cualquiera puede falsificar JWT.
El tercer parámetro: es la opción de firma SignOptions
, la firma de la interfaz:
exportar interfaz SignOptions { algoritmo?: Algoritmo | indefinido; keyid?: cadena | indefinido; caduca en?: cadena | número | indefinido; /** expresado en segundos o una cadena que describe un lapso de tiempo [zeit/ms](https://github.com/zeit/ms.js Por ejemplo: 60, "2 días", "10h", "7d". */ ¿noAntes?: cadena | número | indefinido; audiencia?: cadena | cadena[] | indefinido; asunto?: cadena | indefinido; emisor?: cadena | indefinido; jwtid?: cadena | indefinido; mutatePayload?: booleano | indefinido; ¿noTimestamp?: booleano | indefinido; encabezado?: JwtHeader | indefinido; codificación?: cadena | indefinido; }
Aquí usamos el campo expiresIn
para especificar el tiempo de vencimiento. Consulte este documento para conocer los métodos de uso.
El cuarto parámetro es una devolución de llamada. El segundo parámetro de la devolución de llamada es token
que generamos a través de la firma. Finalmente, este token
se devuelve al front-end para que pueda almacenarse localmente en el front-end y llevarse al servidor para su verificación. en cada solicitud.
A continuación, verifiquemos esta interfaz: instalé el complemento de cliente REST en vscode, luego creé un archivo request.http
en el directorio raíz y escribí la información solicitada en el archivo:
PUBLICAR http://localhost:3000/login tipo de contenido: aplicación/json { "nombre de usuario": "nombre de usuario", "contraseña": "contraseña" }
Luego ejecute el comando npm run start
en la línea de comando para iniciar el servicio y luego haga clic en el botón Send Request
sobre el archivo requset.http
para enviar la solicitud:
Después de que la solicitud sea exitosa, verá un mensaje de respuesta como este:
El campo token
es token
generado por nuestro JWT;
Verifiquemos si este token
es válido. Estamos escribiendo una interfaz después de iniciar sesión:
app.get("/después de iniciar sesión", (req, res) => { const {encabezados} = solicitud; token const = encabezados["autorización"].split(" ")[1]; //Coloca el token en el campo de autorización del encabezado jwt.verify(token, jwtKey, (err, payload) => { si (err) devuelve res.sendStatus(403); res.json({ mensaje: "Autenticación exitosa", carga útil }); }); });
En este código, token
generado previamente a través de JWT se obtiene obteniendo token
en el campo authorization
en el encabezado de la solicitud. Luego verifique si el token
es válido llamando jwt.verify
. Este método tiene tres parámetros:
// Hay cuatro firmas de interfaz, puede verificar la documentación usted mismo función de exportación verificar ( token: cadena, //Token que debe ser verificado secretOrPublicKey: Secreto | GetPublicKeyOrSecret, // ¿La clave de firma definida en la devolución de llamada del servidor?: VerifyCallback<JwtPayload string>, // Devolución de llamada para obtener el resultado de la información de verificación): void;
A continuación, copiamos token
al que acabamos de responder en el encabezado de la solicitud:
### OBTENER http://localhost:3000/afterlogin autorización: Portador eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InVzZXJuYW1lIiwiaWF0IjoxNjUyNzg5NzA3LCJleHAiOjE2NTI3ODk3Mzd9.s9fk3YLhxTUcpUgCfIK4xQ N58 Hk_XEP5y9GM9A8jBbY
La autenticación de portador anterior es el método de autenticación estándar en el protocolo http.
También haga clic en Send Request
y cuando vea la respuesta en la imagen a continuación, significa que la respuesta fue exitosa:
De hecho, los anteriores son algunos usos simples de JWT. A continuación, hablemos de las ventajas y desventajas del propio JWT.
El espacio de almacenamiento ocupado por JWT en realidad no es pequeño. Si necesitamos firmar demasiada información, es probable que el token exceda el límite de longitud de la cookie. Por ejemplo, compare estas dos imágenes:
Obviamente, a medida que aumenta la cantidad de información en la carga útil, la longitud del token también aumentará;
Seguridad: de hecho, si token
ocupa demasiado espacio, el espacio de almacenamiento máximo Cookie
es de solo 4 kb. La interfaz se puede almacenar en un almacenamiento local como localStorage
, pero causará un problema si no se coloca. en las cookies, la seguridad se reducirá considerablemente. Existe el riesgo de obtenerlas a través del script js, lo que significa que cualquier hacker puede hacer cualquier cosa con ellas;
Puntualidad inflexible. De hecho, un cierto aspecto de JWT es que el token
del usuario no necesita almacenarse de forma persistente. En cambio, token
se verifica de manera efectiva mediante la verificación del servidor. Como acaba de ver, la firma también incluye el tiempo de vencimiento. Si se cambia el tiempo de vencimiento, token
será manipulado. Dado que no hay forma de almacenar y cambiar manualmente la fecha de vencimiento, es difícil eliminar el token
inmediatamente. Si el usuario inicia sesión dos veces y se generan dos token
, entonces. En principio, se generarán dos token
, todos son válidos;
Lo anterior se refiere principalmente a algunos puntos:
El principio de JWT es principalmente utilizar la clave privada del servidor para comunicarse con token
generado por la firma JSON;
También presenta la composición de los datos internos de JWT, que utiliza el encabezado para especificar el algoritmo y el tipo de firma, la carga útil para transmitir datos JSON y la firma para realizar el algoritmo de firma en los datos y evitar la manipulación;
Se proporciona una introducción detallada sobre cómo usar JWT a través de nodejs, realizar la firma de datos mediante el método sign
y realizar la verificación de firma mediante el método verify
;
También se introducen algunas desventajas de JWT:
Una es que el espacio de almacenamiento aumenta a medida que aumenta la cantidad de datos de firma;
Luego está la seguridad, si el espacio de almacenamiento es demasiado grande, no se almacenará en Cookie
con un nivel de seguridad relativamente alto, lo que hará que el script se obtenga a voluntad;
Luego está la puntualidad, que no puede controlar de manera flexible la puntualidad de token
;
Este es el código fuente de demostración de nodejs anterior como referencia;