Charla sobre cookies, sesión y jwt - Nuggets
Al desarrollar la página de inicio de sesión en el front-end, a menudo nos encontramos con las cookie
palabras , session
y jwt
estas palabras. Aunque se usan a menudo, su comprensión es relativamente superficial. Este artículo trata de aclarar su esencia y la relación entre ellas.
cookie
Sabemos que HTTP
el protocolo no tiene estado, por lo que el front-end necesita pasar una información de estado para decirle al fondo quién inició la solicitud, cookie
es decir, esta información de estado, que es una pequeña porción de datos (no más de 4K) almacenada en el navegador Cuando HTTP
se realiza la primera solicitud, el navegador la agregará automáticamente al encabezado de la solicitud y la enviará al fondo.
cookie
varios atributos
Set-Cookie: sessionId=123; Path=/accounts; Expires=Wed, 13 Jan 2021 22:23:01 GMT; Secure; HttpOnly; SameSite=Strict
Domain & path
cookie
Alcance del control
domain
Es decir, el nombre de dominio,path
es decir, la ruta,cookie
solo se puede usar bajo el especificadodomain
ypath
.Cuando
domain
no se especifica, el valor predeterminado es el nombre de dominio actual (excluyendo los nombres de subdominio); cuandopath
no se especifica, el valor predeterminado es todas las rutas '/' debajo del nombre de dominio.En el ejemplo anterior, el primero
cookie:sessionId
no se especificadomain
y se especificaPath=/accounts
, por lo quea.com/accounts
se llevará solo cuando se solicite el recurso a continuacióncookie
.
Expires & Max-Age
cookie
Tiempo efectivo de control
Expires
yMax-Age
se usan para especificarcookie
el período de tiempo de espera, el primero se usa para especificar elcookie
período de tiempo de espera específico y el último se usa para especificarcookie
el intervalo de tiempo de espera;- Si no se especifica, se eliminará cuando el usuario cierre el navegador
cookie
;
Secure & HttpOnly & SameSite
cookie
Política de seguridad controlada
Secure
Especificacookie
quehttps
debe llevarse en la solicitud;HttpOnly
Está prohibido utilizarjs
la operación en la parte delanteracookie
ydocument.cookie
no se obtendrá el valorcookie
;SameSite
Determinar si el navegador lo lleva automáticamente al cruzar dominioscookie
;
Strict
más estricto. SiSameSite
el valor de esStrict
, el navegador bloquea completamente a tercerosCookie
. En resumen, si accede a los recursos de InfoQ desde la página de GeekTime y los de InfoQCookie
están configuradosSameSite = Strict
, estosCookie
no se enviarán al servidor de InfoQ . Solo cuando solicite los recursos de InfoQ del sitio de InfoQ, se traerán estas cookies.
Lax
Relativamente relajado. En el caso de sitios cruzados,Get
se llevarán a cabo ambos métodos de abrir un enlace desde un sitio de terceros y enviar un formulario desde un sitio de tercerosCookie
. Sin embargo, si el método se utiliza en un sitio de tercerosPost
, oimg、iframe
si la URL se carga a través de etiquetas como , estos escenarios no se llevarán a caboCookie
.Y si se utilizan
None
, los datos serán enviados en cualquier casoCookie
.
cookie
principio
Para entender cookie
el principio, en primer lugar, ¿quién puede establecerlo cookie
? Como se muestra abajo:
Se puede ver en la figura que cuando el usuario visita a.com
, hay un campo en el encabezado de respuesta devuelto por el fondo.Después set-cookie
de que el navegador lea el encabezado de respuesta y tenga este campo, guardará automáticamente la información contenida en este campo en el navegador En solicitudes posteriores, el navegador llevará automáticamente estos cookie
datos y los enviará al servidor.
Por lo tanto, cookie
lo configura el servidor y el navegador guarda automáticamente la información de este campo en la computadora del usuario .
cookie
domain
Solo se llevará automáticamente bajo la misma solicitud, y el navegador no llevará esta URL cuando acceda a recursos bajo una URL cookie
diferente .domain
domain
cookie
Si un usuario solicita la primera a.com
página, el navegador almacena a.com
la siguiente cookie
. Luego, el usuario abrió b.com
la página siguiente y el navegador también almacenó b.com
la página siguiente cookie
. Si el usuario envía una solicitud b.com
a la página en este momento, ¿ cuál a.com
llevará el navegador ?domain
cookie
La respuesta es a.com
, es decir, el nombre de dominio incluido en la solicitud no tiene cookie
nada que ver con el sitio web actualmente abierto, pero url
tiene algo que ver con la solicitud. Porque esta característica puede dar lugar a CSRF
ataques .
cookie
el uso de
1. Gestión de sesiones
Cuando el usuario visita el sitio web de compras (1-4) por primera vez, el sitio web
server
genera uno para el usuariosessionId
y lo incluye en la respuestaSet-Cookie: sessionId=123; Expires=Tue, 15 Jan 2021 21:47:38 GMT;
;El navegador recibe la respuesta del servidor, la obtiene de la respuesta
Set-Cookie
ysessionId=123
la almacena en el navegadorcookie
. Dado queSet-Cookie
el atributo Caduca se lleva en el navegador, el navegador tambiéncookie
establece el tiempo de caducidad para esto (si no hay ningúnExpires
atributo, el navegador lo tratarácookie
comosession cookie
un procesamiento, es decir, cuando el usuario cierre el navegador, secookie
eliminará) ;Cuando el usuario agrega un producto al carrito de compras, el navegador enviará la operación del carrito de compras a , y automáticamente lo incluirá en
server
la solicitud y obtendrá el usuario correspondiente (más adelante se explicará cómo encontrar al usuario correspondiente a través de sessionId=123) , y agregó un artículo a su carrito de compras;cookie
sessionId=123
server
sessionId=123
Luego, el usuario cierra el sitio de compras;
Unas horas más tarde, el usuario vuelve a abrir el sitio web de compras y visita el carrito de compras. El sitio web solicita los datos del carrito de compras del backend. El navegador busca en el área local y encuentra que el sitio web es válido. El navegador adjunta
cookie
automáticamentesessionId=123
la cookiecookie
a el encabezado de solicitud del sitio web ;sessionId=123
El servidor recibe la solicitud de consulta del carrito de compras y la obtiene del encabezado de la solicitud
sessionId=123
. El servidor busca en la memoriaid=123
ysession
encuentra los datos del producto del carrito de compras del usuario, y el servidor devuelve estos datos al front-end.El usuario ve el artículo agregado en el carrito de compras cuando visitó el sitio web por última vez y selecciona el artículo para completar el pago.
Lo anterior completa la gestión de la sesión.
2. Seguimiento de usuarios (publicidad)
El sitio web de compras está conectado a la plataforma de publicidad sdk (sdk de publicidad de Baidu) y se paga para habilitar la publicidad en la plataforma de publicidad;
Cuando un usuario visita un sitio web de compras, automáticamente solicitará recursos de la plataforma de publicidad (generalmente solicitando un gif de la plataforma de publicidad) ([2] en la figura). La plataforma de publicidad sdk genera una identificación única para el usuario: y pone en el
user123
encabezado de respuesta del recurso CarryInfocookie
(Set-Cookie:HMACCOUNT=user123; Path=/; Domain=hm.baidu.com; Expires=Sun, 18 Jan 2038 00:00:00 GMT
);El navegador almacenará
Set-Cookie
la información encookie
y tenga en cuenta que estecookie
atributodomain
es el nombre de dominio de la plataforma de publicidad, no el nombre de dominio del sitio web de compras Llamamos a este tipo de cookie de terceroscookie(third cookie)
;El usuario cierra el sitio web después de ver algunos productos en el sitio web de compras;
Después de un período de tiempo, el usuario navega por un sitio web de videos
youku.com
, y el sitio web de videos ha abierto un servicio de publicidad en la plataforma de publicidad y se ha conectado a la plataforma de publicidad sdk. Del mismo modo, el sitio web solicitará automáticamente recursos bajo la plataforma publicitaria ([7][8] en la figura), y el navegador llevará automáticamente las tres partes previamente almacenadascookie
(porque el dominio de esta cookie es el nombre de dominio de la plataforma publicitaria recursos).La plataforma de publicidad recibe
youku.com
la solicitud de recursos y encuentra que se lleva el encabezado de la solicitudcookie:HMACCOUNT=user123
. De esta manera, la plataforma publicitaria reconoce que este usuario es un usuario que ha visitado el sitio web de compras anteriormenteuser123
. Dado que el sitio web de compras ha abierto anuncios pagados, este usuario se identifica como un usuario exacto. Cuandoyouku.com
el material publicitario se extrae de la plataforma publicitaria , será llevado al sitio web de compras para la entrega de anuncios y mostrárselos a los usuarios.Cuando el usuario está
youku.com
viendo el video, se inserta un anuncio de 30 segundos al comienzo del video y el contenido del anuncio trata sobre las actividades preferenciales del sitio web de compras.Los usuarios están más interesados en las actividades impulsadas por este anuncio, y también son usuarios del sitio web de compras, por lo que tienen una base de confianza, por lo que el usuario hace clic en el anuncio, ingresa al sitio web de compras para recibir descuentos y comprar productos, y por lo tanto, recibe ingresos publicitarios de la plataforma publicitaria
youku.com
.
Esto completa cookie
el seguimiento del usuario.
session
que es exactamentesession
Como desarrollo front-end, no entendí qué era durante mucho tiempo session
, y no entendí completamente qué era hasta que aprendí poco a poco el desarrollo en segundo plano session
.
Del contenido del apartado anterior podemos saber que cookie
se guarda en el navegador, es decir el cliente, entonces session
¿dónde se guarda?
La respuesta es el lado del servidor .
Después de que el navegador lo envía cookie
al servidor, ¿cómo sabe el servidor qué usuario es? Es decir, ¿cómo se usa el servidor cookie
para la verificación de inicio de sesión?
set-cookie
El cliente envía una solicitud de inicio de sesión y el servidor verifica la contraseña y el nombre de usuario. Si son correctos, el servidor los configurará en el encabezado de respuesta key-value
como username=zhangsan
:
const getCookieExpires = () => {
const d = new Date()
d.setTime(d.getTime() + (24 * 60 * 60 * 1000))
return d.toGMTString()
}
res.setHeader('Set-Cookie', `username=zhangsan; path=/; httpOnly; expires=${getCookieExpires()}`)
Después de que el navegador obtenga el encabezado de respuesta, almacenará la información en el navegador cookie
, que se almacenará en la siguiente solicitud cookie
, y el servidor comenzará a analizar cookie
la información después de obtener el encabezado de la solicitud:
// 解析请求头中的cookie信息
req.cookie = {}
const cookieStr = req.headers.cookie || ''
cookieStr.split(';').forEach(item => {
if (!item) {
return
}
const arr = item.split('=')
req.cookie[arr[0]] = arr[1]
})
console.log('cookie', req.cookie)
Después del análisis, se realiza la verificación del usuario. La verificación es muy simple, solo para cookie
ver si hay alguno username
, y si hay una descripción, ya ha iniciado sesión.
Así es cookie
como se realiza la autenticación de usuario.
¡Es muy sencillo!
cookie
Hay un problema fatal en lo anterior : la exposición username
y otra información clave es muy peligrosa.
La solución es cookie
almacenar en el medio userid
, y el servidor almacena la información correspondiente username
, por lo que la información almacenada en el servidor username
es session
.
Ahora, debes saber qué es session
.
cómo almacenarsession
session
¿ Dónde se almacena?
Lo primero que viene a la mente es guardar en memoria (es decir, mantener un objeto globalmente y usar este objeto para guardar información del usuario).Los siguientes son los pasos para usar la memoria para implementar unasession
función de almacenamiento:
session
Mantener un objeto almacenado globalmenteSESSION_DATA
para que se pueda acceder a él durante todo el proceso;- Si no se incluye el encabezado de la solicitud
userid
, significa que se trata de una visita y se solicita al usuario que inicie sesión;- Si la solicitud tiene un encabezado
userid
,SESSION_DATA
obtenga la información del usuario correspondiente, si no, permita que el usuario inicie sesión;- Después de un inicio de sesión exitoso, escriba la información del usuario en
session
el objetoSESSION_DATA
;
// session数据 在全局维护一个session_data
const SESSION_DATA = {}
// 当用户发送请求的时候,解析session
// 解析session
let needSetCookie = false
let userId = req.cookie.userId
if (userId) {
if (!SESSION_DATA[userId]) {
SESSION_DATA[userId] = {}
}
} else {
userId = `${Date.now()}_${Math.random()}`
SESSION_DATA[userId] = {}
// 如果没有userid,说明是第一次登录,那么就去设置cookie
needSetCookie = true
}
// 把session挂载在req上
req.session = SESSION_DATA[userId]
// 在登录接口,登录成功后往session_data里面写数据
if (method === 'POST' && req.path === '/api/user/login') {
const { username, password } = req.body
const result = login(username, password)
return result.then(data => {
if (data.username) {
// 设置session数据
req.session.username = data.username
req.session.realname = data.realname
return new SuccessModel('登录成功')
}
return new ErrorModel('登录失败')
})
}
Si session
lo almacena en la memoria, encontrará dos problemas :
- Como es una variable global, está en la memoria, pero el tamaño de la memoria asignada a cada proceso es limitado, y cuando hay muchos usuarios, no podrá almacenarla .
- El sistema operativo asignará un espacio de memoria para cada proceso, como se muestra en la figura anterior, por ejemplo, de 0x1000 a 0x8000, la parte superior es la memoria de pila y la parte inferior es la memoria de montón.Nuestra sesión se coloca en la memoria de montón. Si hay muchos usuarios en ese momento, habrá más y más montones, llenando toda la memoria y todo el proceso colapsará.
- En línea se implementa mediante varios procesos y no se puede acceder a los procesos entre procesos
session
, por lo que el proceso B no puede acceder al proceso A.
Las computadoras de hoy en día son todas multinúcleo, por lo que para hacer un uso completo de los recursos de la computadora, una aplicación a menudo inicia múltiples procesos. Si cada proceso lo tiene , no se puede compartir session
entre procesos . session
Cuando ingresa por primera vez, presiona el del primer proceso session
, pero la segunda vez ingresa y presiona el del segundo proceso session
, pero session
no puede iniciar sesión sin su información. Esto es causado por el equilibrio de carga, que asigna qué proceso depende de qué proceso está relativamente inactivo.
Dado que session
no es posible guardarlo en la memoria, ¿es posible guardarlo en la base de datos? ej mysql
.
De hecho, también hay un problema. Tengo que verificar la legitimidad del usuario para cada solicitud. Si la verificación es exitosa, vaya al siguiente paso para obtener datos de la base de datos. Esto es equivalente a consultar la base de datos dos veces , lo que conduce a un mayor tiempo de solicitud .
Para resolver este problema, generalmente lo colocamos session
en redis
esta base de datos de memoria, y la velocidad de acceso a la memoria es mucho mayor que la velocidad de acceso al disco duro.
Por lo tanto, el modelo común actual de servicios web se muestra en la figura:
session
¿Por qué aplicar redis
?
1. Se accede a las sesiones con frecuencia y requieren un rendimiento extremadamente alto
session
Acceso frecuente, porque tenemos que verificar si iniciar sesión en cada solicitud, que es una operación previa al acceso, por lo que el acceso debe ser muy rápido, si el acceso es muy lento, las operaciones posteriores también se ralentizarán, lo que resultarásession
en un tiempo de solicitud más largo Long, por lo que los requisitos de rendimiento son extremadamente altos.2.
session
No considere el problema de la pérdida de datos debido a una falla de energía (daño de memoria)
session
Si se pierden los datos de apagado, vuelva a iniciar sesión (iniciar sesión nuevamente es escribir la información relacionada con el usuario, el usuario primero irá a redis para obtener los datos cuando visite la próxima vez, y luego procederá a la siguiente la lógica comercial después de obtener los datos) está bien. Después de iniciar sesión, puede solicitar otras interfaces parauserid
verificarloredis
. Si hay, significa que ya ha iniciado sesión.3. La cantidad de datos de la sesión no será demasiado grande (en comparación con los datos almacenados en mysql)
jwt
qué esjwt
json web token
( jwt
) es un token basado en JSON que se utiliza para declarar ciertos reclamos en la red. Por lo general, consta de tres partes: información del encabezado (header), cuerpo del mensaje (carga útil) y firma (signature).
- La información del encabezado especifica el
jwt
algoritmo de firma a utilizar.
header = {"alg":"HS256","typ":"JWT"}
HS256
Indica que se utilizó un HMAC-SHA256
para generar la firma.
- La carga útil del cuerpo del mensaje es la parte que realmente almacena la información que debe transmitirse. Por ejemplo, normalmente almacenaremos algunas ID de usuario, nombres de usuario y similares. Además, se incluyen algunos metadatos como editor, fecha de caducidad, etc.
payload = {"userName":"admin","iat":1422779638} //iat表示令牌生成的时间
- Firma: Firma
Header
yPayload
El token sin firmar se base64url
concatena a partir de la información del encabezado codificado y el cuerpo del mensaje (separados por "."), y la firma se calcula a través de la clave privada:
key = 'secretkey' // 秘钥保存在服务端,即使用户篡改了数据,因为不知道秘钥,生成的token也是无效的
unsignedToken = encodeBase64(header) + '.' + encodeBase64(payload)
signature = HMAC-SHA256(key, unsignedToken)
Finalmente, concatene labase64url
firma codificada (también separada por ".") al final del token sin firmar :jwt
token = encodeBase64(header) + '.' + encodeBase64(payload) + '.' + encodeBase64(signature)
# token看起来像这样: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2dnZWRJbkFzIjoiYWRtaW4iLCJpYXQiOjE0MjI3Nzk2Mzh9.gzSraSYS8EXBxLN_oWnFSRgCzcmJmMjLiuyu5CSpyHI
jwt
Darse cuenta del principio de inicio de sesión
-
Primero, el frontend envía su propio nombre de usuario y contraseña al backend a través de un formulario web.
-
Después de que el backend verifica el nombre de usuario y la contraseña con éxito, toma la identificación del usuario y otra información como
jwt Payload
(carga útil), realiza la codificación Base64 y la concatenación con el encabezado, y luego firma para formar una cadenatoken
comoaaa.bbb.ccc
esta.
-
El backend
token
devuelve la cadena como el resultado de retorno de un inicio de sesión exitoso en el frontend, y el frontend puede guardar el resultado devuelto unalocalStorage
ysessionStorage
otra vez, y eliminar el guardado al cerrar la sesióntoken
. -
El front-end lo envía al back-end en el encabezado de la solicitud para cada solicitud
token
. Actualmente hay dos formas:-
Una es
cookie
la forma de pasar , es decir, ponerlotoken
,cookie
y el navegador lo tomará automáticamente por nosotros cada vez, y no necesitamos configurarlo nosotros mismos. -
El segundo es ponerlo en el encabezado de la solicitud
header Authorization
, debemos configurar manualmente el encabezado de la solicitud nosotros mismos. Por lo general, después de obtenersetoken
, se almacenará ensessionStorage
olocalStorage
en, para quetoken
no desaparezca cuando se actualice la página.
-
Authorization: Bearer eyJhbGci*...<snip>...*yu5CSpyHI
- El backend verifica la existencia, como
token
la validez de la validación de existencia. Por ejemplo, comprobar si la firma es correcta, comprobar si el Token ha caducado, etc.
jwt
pros y contras
De la introducción anterior, podemos ver que el servidor pone toda la información del usuario token
en Internet y no necesita redis
guardarla session
, es decir, usar jwt
la mayor ventaja y eliminarla session
.
Pero esto traerá un gran problema, porque token
se almacena en el cliente y el servidor no puede invalidar el token emitido.Incluso si sabe que un token
token ha sido robado, no tiene forma de invalidarlo. Hasta token
que caduque (y absolutamente debe establecer un tiempo de caducidad), no hay mucho que pueda hacer. Por ello, es muy necesario fijartoken
el tiempo de caducidad .
Además, token
aunque se almacena en Local Storage
, para evitar CSRF
ataques, sigue siendo imposible evitarlos XSS
, y las secuencias de comandos entre dominios aún pueden robar Local Storage
datos en .
Resumir
Dado que http
el protocolo no tiene estado, para saber qué usuario inició la solicitud, nació y se cookie-session
almacenó cookie
en el cliente. Cada vez que se envía una solicitud, el navegador la enviará automáticamente al fondo. cookie
Después de que el fondo la obtenga de la solicitud encabezado, se enviará desde La información personal del usuario se encuentra redis
en él session
y se completa la confirmación del usuario.
Debido a que es problemático usarlo redis
y guardarlo , espero que la información del usuario se guarde directamente en el lado del cliente en lugar del lado del servidor, así nació .session
jwt
El servidor encripta la información del usuario con un algoritmo de encriptación y token
la envía al navegador. El navegador la almacena en él , la agrega al encabezado de la solicitud localstorage
para cada solicitud y la envía al fondo. El fondo verifica la corrección a través de la clave.localstorage
token
No importa si es cookie-session
, o si jwt
ambos tienen el propósito de completar la autenticación del usuario, cada uno tiene sus propias ventajas y desventajas, y se puede usar de manera flexible en diferentes escenarios.