Эта статья была впервые опубликована вличный блог
предисловие
HTTP-протокол без сохранения состояния
Давным-давно Интернет был просто просмотром документов. Поскольку он просматривает, как сервер, нет необходимости записывать, какие документы были просмотрены за определенный период времени.Каждый запрос представляет собой новый HTTP-протокол, то есть запрос плюс ответ. Нет необходимости отслеживать, кто только что сделал HTTP-запрос, каждый запрос совершенно новый.
Как управлять сессиями
С появлением интерактивных веб-приложений, таких как веб-сайты онлайн-покупок, веб-сайты, требующие входа в систему и т. д., возникает непосредственная проблема: управлять звонками, запоминать, кто вошел в систему, кто поместил продукты в свою учетную запись. тележки для покупок, а также То есть я должен отделить каждый из них.
В этой статье в основном объясняется, как cookie, сеанс и токен управляют сеансами;
cookie
Файл cookie — это очень специфическая вещь, которая относится к типу данных, которые могут постоянно храниться в браузере. К серверу это не имеет никакого отношения, это просто функция хранения данных, реализованная браузером.
Файл cookie генерируется сервером и отправляется в браузер. Браузер сохраняет файл cookie в виде KV в текстовом файле в определенном каталоге. При следующем запросе того же веб-сайта файл cookie будет отправлен на сервер. Поскольку файлы cookie хранятся на стороне клиента, браузеры добавляют некоторые ограничения, чтобы гарантировать, что файлы cookie не могут быть использованы злонамеренно и не занимают слишком много места на диске. Таким образом, количество файлов cookie на домен ограничено.
Как установить
Настройки клиента
document.cookie = "name=xiaoming; age=12 "
- Клиент может установить следующие параметры файла cookie: срок действия, домен, путь, безопасный (только на веб-странице протокола https клиент может установить безопасный тип файла cookie, чтобы он вступил в силу), но параметр httpOnly не может быть установлен.
Set Cookie => Cookie автоматически добавляется в заголовк запроса => Сервер получает cookie
Настройки сервера
Независимо от того, запрашиваете ли вы файл ресурсов (например, html/js/css/image) или отправляете запрос ajax, сервер вернет ответ. И в заголовке ответа есть элемент, называемыйset-cookie
, который специально используется сервером для установки файлов cookie;
- Set-cookie может установить только один файл cookie, если вы хотите установить более одного, вам нужно добавить один и тот же номер.
set-cookie
- Сервер может установить все параметры для файлов cookie: истекает, домен, путь, безопасный, HttpOnly
Файлы cookie, SessionStorage, LocalStorage
HTML5 предоставляет два метода локального хранения: sessionStorage и localStorage;
session
что такое сессия
Сессия - это буквально сессия. Это похоже на разговор с кем-то, откуда вы знаете, что в это время с вами разговаривал Чжан Сан, а не Ли Си? Другая сторона должна иметь определенные характеристики (равные по длине), чтобы указать, что он Чжан Сан; Сеанс аналогичен, серверу нужно знать, кому отправляется текущий запрос. Чтобы провести это различие, сервер должен присвоить каждому клиенту разные «идентификаторы», а затем каждый раз, когда клиент отправляет запрос на сервер, он будет приносить этот «идентификатор», и сервер будет знать, что запрос откуда и кто. Что касается того, как клиент сохраняет этот «идентификатор личности», существует много способов.Для браузерного клиента все используют метод cookie.
Процесс (сеанс сервера + идентификатор сеанса клиента)
- 1. Пользователь отправляет имя пользователя и пароль на сервер
- 2. После аутентификации сервера в текущем сеансе (сеансе) сохраняются соответствующие данные, такие как роль пользователя, время входа в систему и т. д.;
- 3. Сервер возвращает
session_id
, напишитеcookie
- 4. Каждый последующий запрос от пользователя будет проходить через
cookie
, Будуsession_id
отправить обратно на сервер - 5. Сервер получает
session_id
, найти данные, сохраненные в предыдущий период, и, таким образом, узнать личность пользователя
существующие проблемы
Плохая масштабируемость
Конечно, нет никаких проблем с одной машиной. Если это кластер серверов или сервера, ориентированная на сервисную архитектуру, это требует совместного использования данных сеанса, и каждый сервер может прочитать сеанс.
Например, Сайт А и Сайт Б являются дочерними сервисами одной и той же компании. Теперь требуется, чтобы, пока пользователи входят на один из веб-сайтов, они автоматически входили в систему при посещении другого веб-сайта.Как этого можно добиться? Этот вопрос заключается в том, как реализовать единый вход
- Стратегия Nginx ip_hash, сервер использует прокси-сервер Nginx, и каждый запрос распределяется в соответствии с хэшем IP-адреса доступа, так что доступ к фоновому серверу осуществляется с того же IP-адреса, что позволяет избежать явления создания сеанса на сервере A и распределения его на сервер B во второй раз.
- Репликация сеанса: когда сеанс на любом сервере изменяется (добавляется, удаляется или изменяется), узел сериализует все содержимое сеанса и передает его всем другим узлам.
- Общий сеанс: централизованно храните идентификатор сеанса в одном месте, и все машины получают доступ к данным в этом месте. Преимущество этой схемы в том, что структура понятна, а недостаток в том, что объем инженерных работ относительно велик. Кроме того, в случае сбоя уровня сохраняемости возникает единственная точка отказа;
Другое решение заключается в том, что сервер просто не сохраняет данные сессии, все данные хранятся на стороне клиента, и каждый запрос отправляется обратно на сервер. Это решение представляет собой проверку на основе токенов, которая будет представлена позже;
Token
Обработать
- Пользователь отправляет запрос с именем пользователя и паролем
- Верификация программы
- Программа возвращает подписанный токен клиенту
- Клиент сохраняет токен и отправляет каждый запрос с каждым
- Сервер проверяет токен и возвращает данные
На самом деле технология этого метода давно реализована, и есть уже готовые стандарты, этот стандарт JWT;
JWT(JSON Web Token)
структура данных
Реальный JWT выглядит так:
Веб-токены JSON состоят из трех частей, разделенных точкой (.):
- Заголовок
- Полезная нагрузка
- Подпись
Поэтому JWT обычно представляют следующим образом:
xxxxx.yyyyy.zzzz
Заголовок
Заголовок — это объект JSON.
{
"alg": "HS256", // 表示签名的算法,默认是 HMAC SHA256(写成 HS256)
"typ": "JWT" // 表示Token的类型,JWT 令牌统一写为JWT
}
Полезная нагрузка
Часть Payload также является объектом JSON, который используется для хранения фактических данных, которые необходимо передать.
{
// 7个官方字段
"iss": "a.com", // issuer:签发人
"exp": "1d", // expiration time: 过期时间
"sub": "test", // subject: 主题
"aud": "xxx", // audience: 受众
"nbf": "xxx", // Not Before:生效时间
"iat": "xxx", // Issued At: 签发时间
"jti": "1111", // JWT ID:编号
// 可以定义私有字段
"name": "John Doe",
"admin": true
}
JWT по умолчанию не зашифрован и может быть прочитан кем угодно, поэтому не помещайте секретную информацию в этот раздел.
Подпись
Подпись — это подпись для первых двух частей, чтобы предотвратить подделку данных.
Во-первых, вам нужно указать секрет. Этот ключ известен только серверу и не может быть раскрыт пользователю. Затем используйте алгоритм подписи, указанный в заголовке (по умолчанию HMAC SHA256), чтобы сгенерировать подпись по следующей формуле.
HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
После вычисления подписи объедините три части заголовка, полезной нагрузки и подписи в строку и разделите каждую часть точкой (.), а затем верните ее пользователю.
JWT = Base64(Header) + "." + Base64(Payload) + "." + $Signature
Как обеспечить безопасность?
- Используйте HTTPS для отправки JWT; если не используете HTTPS, не записывайте секретные данные в JWT.
- Время истечения должно быть установлено в полезной нагрузке JWT.
Как использовать
Клиент получает возвращенный сервером JWT, который может быть сохранен в файле cookie или в локальном хранилище. После этого каждый раз, когда клиент связывается с сервером, он должен приносить этот JWT. Вы можете поместить его в куку и отправить автоматически, но это не может быть междоменным, поэтому лучше поместить его в поле Авторизация заголовка HTTP-запроса.
Authorization: Bearer <token>
Другой подход заключается в том, чтобы поместить JWT в тело данных запроса POST, когда он является междоменным.
Роль JWT
Первоначальная цель JWT — обеспечить авторизацию и аутентификацию, а также реализовать распределенную авторизацию веб-приложений без сохранения состояния. Примерно реализованный процесс выглядит следующим образом
- Клиент должен иметь имя пользователя/пароль и другое идентифицируемое содержимое, чтобы авторизовать сервер для получения информации JWT;
- Каждая служба несет содержимое токена для взаимодействия с веб-сервером, и сервер службы проверяет, является ли токен действительным токеном, выданным авторизацией, чтобы проверить, является ли текущий запрос службы законным.
Здесь следует отметить: не обязательно подавать заявку на Токен один раз для каждого запроса.Это необходимо отметить.Если это не по требованиям безопасности, не рекомендуется подавать каждый раз, потому что это увеличит время работы; для например, применять только при входе в систему, а затем использовать время истечения срока действия JWT или другие средства для обеспечения действительности JWT;
Токен доступа, токен обновления
Самым большим преимуществом JWT является то, что серверу больше не нужно хранить сеанс, так что можно легко расширить бизнес-аутентификацию сервера и аутентификацию. Это также самый большой недостаток JWT.Поскольку серверу не нужно хранить состояние сеанса, невозможно сбросить токен или изменить разрешения токена во время использования. Другими словами, после выдачи JWT он будет действовать до истечения срока его действия. Мы можем внести некоторые улучшения, основанные на проблемах, упомянутых выше.
Упомянутые выше токены — это все токены доступа, то есть токены, необходимые для доступа к интерфейсу ресурса, и есть еще один токен, токен обновления. В обычных условиях срок действия Refresh Token будет больше. Срок действия токена доступа относительно короткий.Когда срок действия токена доступа истекает из-за истечения срока действия, токен обновления можно использовать для получения нового токена.Если срок действия токена обновления также истекает, пользователь может только снова войти в систему. Токен обновления и время истечения срока действия хранятся в базе данных сервера и будут проверены только при применении нового токена доступа.Это не повлияет на время отклика бизнес-интерфейса, и его не нужно хранить в памяти, как Сессия справляется с большим количеством запросов.
Простой пример использования JWT
Подготовить
npm i --save koa koa-route koa-bodyparser @koa/cors jwt-simple
код сервера
const Koa = require("koa");
const app = new Koa();
const route = require('koa-route');
var bodyParser = require('koa-bodyparser');
const jwt = require('jwt-simple');
const cors = require('@koa/cors');
const secret = 'your_secret_string'; // 加密用的SECRET字符串,可随意更改
app.use(bodyParser()); // 处理post请求的参数
const login = ctx => {
const req = ctx.request.body;
const userName = req.userName;
const expires = Date.now() + 1000 * 60; // 为了方便测试,设置超时时间为一分钟后
const payload = {
iss: userName,
exp: expires
};
const Token = jwt.encode(payload, secret);
ctx.response.body = {
data: Token,
msg: '登陆成功'
};
}
const getUserName = ctx => {
const token = ctx.get('authorization').split(" ")[1];
const payload = jwt.decode(token, secret);
// 每次请求只判断Token是否过期,不重新去更新Token过期时间(更新不更新Token的过期时间主要看实际的应用场景)
if(Date.now() > payload.exp) {
ctx.response.body = {
errorMsg: 'Token已过期,请重新登录'
};
} else {
ctx.response.body = {
data: {
username: payload.iss,
},
msg: '获取用户名成功',
errorMsg: ''
};
}
}
app.use(cors());
app.use(route.post('/login', login));
app.use(route.get('/getUsername', getUserName));
app.listen(3200, () => {
console.log('启动成功');
});
код клиента
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>JWT-demo</title>
<style>
.login-wrap {
height: 100px;
width: 200px;
border: 1px solid #ccc;
padding: 20px;
margin-bottom: 20px;
}
</style>
</head>
<body>
<div class="login-wrap">
<input type="text" placeholder="用户名" class="userName">
<br>
<input type="password" placeholder="密码" class="password">
<br>
<br>
<button class="btn">登陆</button>
</div>
<button class="btn1">获取用户名</button>
<p class="username"></p>
</body>
<script>
var btn = document.querySelector('.btn');
btn.onclick = function () {
var userName = document.querySelector('.userName').value;
var password = document.querySelector('.password').value;
fetch('http://localhost:3200/login', {
method: 'POST',
body: `userName=${userName}&password=${password}`,
headers:{
'Content-Type': 'application/x-www-form-urlencoded'
},
mode: 'cors' // no-cors, cors, *same-origin
})
.then(function (response) {
return response.json();
})
.then(function (res) {
// 获取到Token,将Token放在localStorage
document.cookie = `token=${res.data}`;
localStorage.setItem('token', res.data);
localStorage.setItem('token_exp', new Date().getTime());
alert(res.msg);
})
.catch(err => {
message.error(`本地测试错误${err.message}`);
console.error('本地测试错误', err);
});
}
var btn1 = document.querySelector('.btn1');
btn1.onclick = function () {
var username = document.querySelector('.username');
const token = localStorage.getItem('token');
fetch('http://localhost:3200/getUsername', {
headers:{
'Authorization': 'Bearer ' + token
},
mode: 'cors' // no-cors, cors, *same-origin
})
.then(function (response) {
return response.json();
})
.then(function (res) {
console.log('返回用户信息结果', res);
if(res.errorMsg !== '') {
alert(res.errorMsg);
username.innerHTML = '';
} else {
username.innerHTML = `姓名:${res.data.username}`;
}
})
.catch(err => {
console.error(err);
});
}
</script>
</html>
запустить код
Адрес источникаВыше приведен очень простой пример: для истечения срока действия токена выполняется простая обработка, и многие граничные условия не обрабатываются, например обработка исключений;
разница
Разница между куки и сессиями
- Различные места хранения: данные cookie хранятся в браузере клиента, данные сеанса хранятся на сервере.
- Различные политики конфиденциальности: файлы cookie не очень безопасны. Другие могут анализировать файлы cookie, хранящиеся локально, и выполнять обман файлов cookie. Учитывая безопасность, следует использовать сеанс.
- Сессия будет сохранена на сервере в течение определенного периода времени. Когда количество посещений увеличится, это поднимет производительность вашего сервера.Чтобы снизить производительность сервера, вы должны использовать файлы cookie.
- Размер хранилища отличается: данные, сохраняемые одним файлом cookie, не могут превышать 4 КБ, а многие браузеры ограничивают сайт сохранением максимум 20 файлов cookie.
Общий совет: храните важную информацию, такую как данные для входа в систему, как сеанс, а другую информацию можно хранить в файлах cookie, если вам нужно ее сохранить.
Разница между токеном и сеансом
Сессия — это механизм хранения HTTP, который обеспечивает механизм сохраняемости для HTTP без сохранения состояния; Токен — это токен.Например, когда вы авторизуете (входите в систему) программу, это является основанием для того, чтобы судить, авторизовали ли вы программное обеспечение;
Сессия и токен не противоречат друг другу.Как токен аутентификации, безопасность лучше, чем у сеанса, потому что каждый запрос имеет подпись и может предотвратить мониторинг и повторные атаки, в то время как сеанс должен полагаться на канальный уровень для обеспечения безопасности связи. Как упоминалось выше, если вам нужно реализовать сеанс с отслеживанием состояния, вы все равно можете добавить сеанс, чтобы сохранить некоторое состояние на сервере.
Суммировать
Нет абсолютной разницы между файлом cookie, сеансом и токеном. Если реальный бизнес-сценарий и потребности объединены, чтобы решить, какой метод использовать для управления сеансом, конечно, можно использовать все три.
Ссылаться на
- jwt
- Полное понимание файлов cookie, сессий, токенов
- Начало работы с веб-токеном JSON
- Файл cookie, сеанс и токен (оригинал)
- 3 способа управления веб-сессиями
- Вы действительно понимаете файлы cookie и сеансы?
- Не используйте JWT для замены управления сеансами (см. выше): всестороннее понимание токенов, JWT, OAuth, SAML, SSO.
- Принцип и реализация access_token и refresh_token