В этой статье подробно рассматриваются файлы cookie, сеансы и токены.

Java
В этой статье подробно рассматриваются файлы cookie, сеансы и токены.

«Эта статья участвовала в мероприятии Haowen Convocation Order, щелкните, чтобы просмотреть:Двойные заявки на внутреннюю и внешнюю стороны, призовой фонд в 20 000 юаней ждет вас, чтобы бросить вызов!"

предисловие

Как JAVA-разработчик, я несколько раз раньше ходил на собеседования. Интервьюеры спрашивали меня, как я освоил JAVAWeb. В то время я не знал, что ответить. Что Web используется в повседневной разработке? Сегодня мы поговорим о трех вещах, которыми JAVAWeb должен овладеть больше всего.

путь развития

1. Давным-давно в Интернете в основном просто просматривали документы. Поскольку он просматривает, как сервер, нет необходимости записывать, кто какие документы просматривал в определенный период времени. Каждый запрос - это новый протокол HTTP, который является запросом Плюс ответы, тем более что мне не нужно помнить, кто только что сделал HTTP-запрос, каждый запрос совершенно новый для меня.

2. Но с появлением интерактивных веб-приложений, таких как сайты онлайн-покупок, сайты, на которых необходимо авторизоваться и т. д., сразу возникает проблема, то есть для управления сеансами необходимо помнить, кто вошел в систему на система и кто покупает для себя.Положить товары в машину означает, что я должен различать всех, что является большой проблемой, потому что HTTP-запросы не имеют состояния, поэтому решение, которое я придумал, состоит в том, чтобы отправить всем идентификатор сеанса (идентификатор сеанса ), грубо говоря Это случайная строка, и все получают ее по разному.Каждый раз, когда все отправляют мне HTTP-запрос, они отправляют эту строку вместе, чтобы я мог различать, кто есть кто.

3. Таким образом, все очень довольны, но сервер недоволен.Каждому нужно сохранить только свой идентификатор сессии, а серверу нужно сохранить идентификатор сессии каждого! Если серверов доступа слишком много, их будут тысячи или даже сотни тысяч.

Это огромные накладные расходы для сервера, которые сильно ограничивают масштабируемость сервера.Например, если я использую две машины для формирования кластера, а Xiao F входит в систему через машину A, идентификатор сеанса будет сохранен на машине A. Предположим, что если следующий запрос малого F перенаправить на машину B? Машина B не имеет идентификатора сеанса маленького F.

Иногда используется небольшая уловка: прилипание сеанса, то есть запрос F застревает на машине A, но это не работает, если машина A зависает, он должен перейти к машине B.

Затем мне пришлось реплицировать сеанс и перемещать идентификатор сеанса туда и обратно между двумя машинами Я был измотан.

image-20210712162145823

Позже появился трюк под названием Memcached: централизованно хранить идентификатор сеанса в одном месте, и все машины получают доступ к данным в этом месте, таким образом, нет необходимости копировать, но увеличивается вероятность единой точки отказа. , Если машина, отвечающая за сеанс, зависнет, всем придется снова войти в систему, и считается, что они будут заруганы до смерти.

image-20210712162600049

Я также пытался кластеризовать эту единую машину для повышения надежности, но несмотря ни на что, эта маленькая сессия стала для меня тяжелым бременем.

4 Итак, кто-то подумал, почему я должен сохранять эту отвратительную сессию, насколько хорошо позволять сохранять ее только каждому клиенту?

Но если я не сохраню эти идентификаторы сеансов, как я могу убедиться, что идентификатор сеанса, отправленный мне клиентом, действительно сгенерирован мной? Если мы не проверим, мы не узнаем, являются ли они законными пользователями.Те, у кого плохие намерения, могут подделать идентификатор сеанса и делать все, что захотят.

Ну да, ключевой момент - проверка!

Например, Xiao F зашел в систему, я отправляю ему токен (токен), который содержит идентификатор пользователя Xiao F, в следующий раз, когда Xiao F снова обращается ко мне через запрос Http, этот токен передается через заголовок Http. Просто приходи.

Но это принципиально не отличается от идентификатора сеанса, его может подделать любой, поэтому мне нужно придумать способ предотвратить подделку другими.

Затем сделайте подпись на данных.Например, я использую алгоритм HMAC-SHA256, добавляю ключ, который знаю только я, делаю подпись на данных и использую подпись и данные вместе как токен, потому что ключ неизвестно другим. , токен нельзя подделать.

image-20210712163843772

Я не сохраняю этот токен. Когда Xiao F отправит мне этот токен, я буду использовать тот же алгоритм HMAC-SHA256 и тот же ключ, чтобы снова вычислить подпись данных и сравнить ее с подписью в токене. то же самое, я знаю, что Xiao F уже вошел в систему и может напрямую получить идентификатор пользователя Xiao F. Если это не то же самое, часть данных, должно быть, была подделана, и я скажу отправителю: Извините, нет аутентификации.

image-20210712163519280

Данные в токене хранятся в виде открытого текста (хотя я буду использовать Base64 для кодирования, но он не зашифрован), и его все равно могут увидеть другие, поэтому я не могу хранить в нем конфиденциальную информацию, такую ​​как пароли.

Конечно, если токен человека украден кем-то другим, то я ничего не могу с этим поделать.Я также думаю, что вор является законным пользователем, что на самом деле то же самое, что идентификатор сеанса человека, украденный кем-то другим.

Таким образом, я не сохраняю идентификатор сеанса, я просто генерирую токен, затем проверяю токен, я использую вычислительное время ЦП, чтобы получить место для хранения сеанса!

Можно сказать, что бремя идентификатора сеанса снято, и можно сказать, что все просто.Мой кластер машин теперь можно легко масштабировать по горизонтали, а количество посещений пользователей увеличивается, просто добавляйте машины напрямую. Это безгосударственное чувство действительно хорошо!

Cookie

1. Что такое файлы cookie

Cookie в переводе на китайский язык означает «куки», который был предложен организацией W3C и впервые разработан сообществом Netscape. В настоящее время файлы cookie стали стандартом, и все основные браузеры, такие как IE, Netscape, Firefox, Opera и т. д., поддерживают файлы cookie.

Сервер не может узнать личность клиента только по сетевому соединению. Как это сделать? Просто выдайте клиентам пропуск, по одному на каждого человека, и каждый посетитель должен принести свой пропуск. Это позволяет серверу проверять личность клиента по паспорту. Вот как работают файлы cookie.

Cookie — это механизм сохранения клиентом информации о пользователе, который используется для записи некоторой информации о пользователе, а также способ реализации сеанса. Файлы cookie хранят ограниченный объем данных, и все они хранятся в клиентском браузере. Разные браузеры имеют разные размеры хранилища, но обычно не более 4 КБ. Таким образом, с помощью файлов cookie на самом деле можно сохранить только небольшой фрагмент текстовой информации (в формате «ключ-значение»).

2. Механизм cookie

Когда пользователь посещает веб-сайт и входит в систему в первый раз, установка и отправка файлов cookie проходят следующие 4 этапа:

  1. Клиент отправляет запрос на сервер;

  2. Сервер отправляет клиенту ответ HttpResponse, содержащий заголовок Set-Cookie;

  3. Клиент сохраняет файл cookie, и когда он отправляет запрос на сервер, запрос HttpRequest будет включать заголовок файла cookie;

  4. Сервер возвращает данные ответа.

image-20210709083823647

Чтобы изучить этот процесс, я написал следующий код для тестирования:

В методе doGet я создал новый объект Cookie и добавил его в объект HttpResponse.

@RestController
public class TestController {

    @GetMapping(value = "/doGet")
    public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        Cookie cookie = new Cookie("jiangwang",System.currentTimeMillis()+"");
        // 设置生命周期为MAX_VALUE
        cookie.setMaxAge(Integer.MAX_VALUE);
        resp.addCookie(cookie);
    }
}

Браузер вводит адрес для доступа, и результат показан на рисунке:

image-20210708111331506

Видно, что заголовок Set-Cookie включен в заголовки ответов, а заголовок Cookie включен в заголовки запросов. Имя и значение устанавливаются точно так же, как указано выше.

3. Атрибуты файлов cookie

Expires

Этот атрибут используется для установки срока действия файла cookie. maxAge в файле cookie используется для представления этого атрибута, а единицей измерения являются секунды. Это свойство читается и записывается в Cookie через getMaxAge() и setMaxAge(int maxAge). Есть 3 значения для maxAge, положительное, отрицательное и 0.

Если атрибут maxAge является положительным числом, это означает, что срок действия файла cookie автоматически истечет через maxAge секунд. Браузер сохранит файл cookie, maxAge которого является положительным числом, то есть запишет его в соответствующий файл cookie (место хранения каждого браузера несовместимо). Независимо от того, закрывает ли клиент браузер или компьютер, файл cookie по-прежнему действителен при входе на веб-сайт, если он не превышает maxAge секунд. Информация о файлах cookie в приведенном ниже коде будет работать всегда.

Cookie cookie = new Cookie("jiangwang",System.currentTimeMillis()+"");
// 设置生命周期为MAX_VALUE,永久有效
cookie.setMaxAge(Integer.MAX_VALUE);
resp.addCookie(cookie);

Отрицательное значение атрибута maxAge означает, что файл cookie является временным файлом cookie и не будет сохраняться, он действителен только в этом окне браузера или подокне, открытом этим окном, и файл cookie будет недействителен сразу после закрытия. браузер.

Cookie cookie = new Cookie("jiangwang",System.currentTimeMillis()+"");
// 设置生命周期为MAX_VALUE,永久有效
cookie.setMaxAge(-1);
resp.addCookie(cookie);

Когда maxAge равен 0, это означает, что файл cookie немедленно удаляется.

Cookie[] cookies = req.getCookies();
Cookie cookie = null;

// get Cookie
for (Cookie ck : cookies) {
    if ("jiangwang".equals(ck.getName())) {
        cookie = ck;
        break;
    }
}

if (null != cookie) {
    // 删除一个cookie
    cookie.setMaxAge(0);
    resp.addCookie(cookie);
}

Изменить или удалить файлы cookie

Операция Cookie, предоставляемая HttpServletResponse, имеет только один addCookie (файл cookie), поэтому, если вы хотите изменить файл cookie, вы можете использовать только файл cookie с тем же именем, чтобы перезаписать исходный файл cookie. Если вы хотите удалить файл cookie, вам нужно всего лишь создать новый файл cookie с тем же именем, установить для параметра maxAge значение 0 и перезаписать исходный файл cookie.

Для вновь созданного файла cookie атрибуты, отличные от значения и maxAge, такие как имя, путь и домен, должны соответствовать исходным, чтобы добиться эффекта модификации или удаления. В противном случае браузер будет рассматривать их как два разных файла cookie и не будет перезаписывать их.

Доменное имя куки

Файлы cookie не могут быть междоменными, а механизм защиты конфиденциальности запрещает веб-сайтам незаконно получать файлы cookie с других веб-сайтов.

При нормальных обстоятельствах два доменных имени второго уровня под одним и тем же доменным именем первого уровня не могут интерактивно использовать файлы cookie, напримерa1.jiangwang.comиa2.jiangwang.com, потому что два домена не идентичны. если ты хочешьjiangwnag.comДоменное имя второго уровня под именем может использовать этот файл cookie, и параметр домена файла cookie должен быть установлен как.jiangwang.com, использовать вот такa1.jiangwang.comиa2.jiangwang.comполучить доступ к тому же файлу cookie

Доменное имя первого уровня также называется доменным именем верхнего уровня, которое обычно состоит из строки + суффикса. Знакомые доменные имена первого уровня — baidu.com и qq.com. com, cn, net и т. д. являются общими суффиксами. Доменное имя второго уровня является производным от доменного имени первого уровня, например, доменное имя первого уровняabc.com,ноblog.abc.comиwww.abc.comОба являются производными доменными именами второго уровня.

Путь к файлам cookie

Атрибут пути определяет путь, по которому разрешен доступ к файлу cookie. Например, установите «/», чтобы разрешить использование файлов cookie на всех путях.

4. Применение

Наиболее типичным применением файлов cookie является определение того, вошел ли зарегистрированный пользователь на веб-сайт, и пользователю может быть предложено сохранить информацию о пользователе при входе на веб-сайт в следующий раз, чтобы упростить процедуру входа.Это функции файлов cookie. . Еще одним важным приложением является «корзина» и тому подобное. Пользователи могут выбирать разные элементы на разных страницах одного и того же веб-сайта в течение определенного периода времени, и эта информация будет записана в файлы cookie, чтобы ее можно было извлечь во время окончательного платежа.

Session

1. Что такое сессия

В WEB-разработке сервер может создать объект сеанса (session object) для каждого пользовательского браузера.Примечание: браузер имеет объект сеанса исключительно (по умолчанию). Поэтому, когда пользовательские данные необходимо сохранить, серверная программа может записывать пользовательские данные в сеанс исключительно для браузера пользователя.Когда пользователь использует браузер для доступа к другим программам, другие программы могут извлекать данные пользователя из сеанса пользователя. , Пользовательские услуги.

2. Принцип реализации сеанса

После того, как сервер создаст сеанс, он запишет номер идентификатора сеанса обратно клиенту в виде файла cookie. Таким образом, пока браузер клиента не закрыт, при доступе к серверу он принесет идентификатор сеанса. номер на сервер.Когда обнаруживается, что клиентский браузер поставляется с идентификатором сеанса, он будет использовать соответствующий сеанс в памяти для его обслуживания. Это можно продемонстрировать с помощью следующего кода:

@RestController
public class TestController {

    @GetMapping(value = "/doGet")
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        response.setCharacterEncoding("UTF-8");
        response.setContentType("text/html;charset=UTF-8");
        //使用request对象的getSession()获取session,如果session不存在则创建一个
        HttpSession session = request.getSession();
        //将数据存储到session中
        session.setAttribute("mayun", "马云");
        //获取session的Id
        String sessionId = session.getId();
        //判断session是不是新创建的
        if (session.isNew()) {
            response.getWriter().print("session创建成功,session的id是:"+sessionId);
        }else {
            response.getWriter().print("服务器已经存在该session了,session的id是:"+sessionId);
        }
    }

}

При первом доступе сервер создаст новый сеанс и отправит идентификатор сеанса браузеру клиента в виде файла cookie, как показано на следующем рисунке:

image-20210708145107823

Когда вы снова запрашиваете сервер, вы можете видеть, что когда браузер снова запрашивает сервер, он передает серверу идентификатор сеанса, хранящийся в файле cookie, как показано на следующем рисунке:

image-20210708145337564

3.Создание и уничтожение сеанса

При первом вызове метода request.getSession() в программе будет создана новая сессия.Вы можете использовать метод isNew(), чтобы определить, создана ли сессия заново.

//使用request对象的getSession()获取session,如果session不存在则创建一个
HttpSession session = request.getSession();
//获取session的Id
String sessionId = session.getId();
//判断session是不是新创建的
if (session.isNew()) {
    response.getWriter().print("session创建成功,session的id是:"+sessionId);
}else {
    response.getWriter().print("服务器已经存在session,session的id是:"+sessionId);
}

Объект сеанса не используется в течение 30 минут по умолчанию, сервер автоматически уничтожит сеанс, или вы можете вручную настроить время истечения сеанса, например:

session.setMaxInactiveInterval(10*60);//10分钟后session失效

Когда вам нужно вручную установить инвалидацию сессии в программе, вы можете вызвать ее вручнуюsession.invalidateметод, уничтожьте сеанс.

HttpSession session = request.getSession();
//手工调用session.invalidate方法,摧毁session
session.invalidate();

Вопрос интервью: Когда браузер закрыт, сессия уничтожается? неправильный.

После создания сеанса, пока пользователь продолжает доступ, сервер будет обновлять время последнего доступа сеанса и поддерживать сеанс. Чтобы предотвратить переполнение памяти, сервер будет удалять из памяти сеансы, которые не были активны в течение длительного времени. Это время является тайм-аутом сеанса. Если к серверу не было доступа по истечении периода ожидания, сессия будет автоматически аннулирована.

Token

1. Что такое токен

Токен означает «токен», который представляет собой строку строк, сгенерированных сервером в качестве идентификатора для клиента, чтобы сделать запрос.

При первом входе пользователя в систему сервер генерирует токен и возвращает токен клиенту, в дальнейшем клиенту нужно будет приносить этот токен только для запроса данных, без необходимости заново приводить логин и пароль .

Состав простого токена: uid (уникальный идентификатор пользователя), time (отметка времени текущего времени), sign (подпись, первые несколько цифр токена — шестнадцатеричная строка определенной длины, сжатая алгоритмом хеширования. утечка токенов).

2. Принцип токена

  1. Пользователь отправляет запрос с именем пользователя и паролем
  2. проверка программы
  3. Программа возвращает токен клиенту
  4. Клиент хранит токен и переносит токен каждый раз, когда отправляется запрос.
  5. Сервер проверяет токен и возвращает данные

image-20210708162203287

3. Использование токена

Пример интеграции Spring Boot и Jwt

image-20210714062557183

Зависимости проекта pom.xml

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.47</version>
</dependency>

    <dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>3.10.3</version>
</dependency>

пользовательская аннотация

//需要登录才能进行操作的注解LoginToken
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface LoginToken {
    boolean required() default true;
}
//用来跳过验证的PassToken
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface PassToken {
    boolean required() default true;
}

Класс сущности пользователя и служба запросов

public class User {
    private String userID;
    private String userName;
    private String passWord;

    public String getUserID() {
        return userID;
    }

    public void setUserID(String userID) {
        this.userID = userID;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getPassWord() {
        return passWord;
    }

    public void setPassWord(String passWord) {
        this.passWord = passWord;
    }
}
@Service
public class UserService {
 
    public User getUser(String userid, String password){
        if ("admin".equals(userid) && "admin".equals(password)){
            User user=new User();
            user.setUserID("admin");
            user.setUserName("admin");
            user.setPassWord("admin");
            return user;
        }
        else{
            return null;
        }
    }
 
    public User getUser(String userid){
        if ("admin".equals(userid)){
            User user=new User();
            user.setUserID("admin");
            user.setUserName("admin");
            user.setPassWord("admin");
            return user;
        }
        else{
            return null;
        }
    }
}

Генерация токена

@Service
public class TokenService {
    /**
     * 过期时间10分钟
     */
    private static final long EXPIRE_TIME = 10 * 60 * 1000;
 
    public String getToken(User user) {
        Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);
        String token="";
        token= JWT.create().withAudience(user.getUserID()) // 将 user id 保存到 token 里面
            .withExpiresAt(date) //十分钟后token过期
            .sign(Algorithm.HMAC256(user.getPassWord())); // 以 password 作为 token 的密钥
        return token;
    }
}

Перехватчик перехватывает токен

package com.jw.interceptor;
 
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.jw.annotation.LoginToken;
import com.jw.annotation.PassToken;
import com.jw.entity.User;
import com.jw.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
 
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
 

public class JwtInterceptor implements HandlerInterceptor{
 
    @Autowired
    private UserService userService;
 
    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception {
        String token = httpServletRequest.getHeader("token");// 从 http 请求头中取出 token
        // 如果不是映射到方法直接通过
        if(!(object instanceof HandlerMethod)){
            return true;
        }
        HandlerMethod handlerMethod=(HandlerMethod)object;
        Method method=handlerMethod.getMethod();
        //检查是否有passtoken注释,有则跳过认证
        if (method.isAnnotationPresent(PassToken.class)) {
            PassToken passToken = method.getAnnotation(PassToken.class);
            if (passToken.required()) {
                return true;
            }
        }
        //检查有没有需要用户权限的注解
        if (method.isAnnotationPresent(LoginToken.class)) {
            LoginToken loginToken = method.getAnnotation(LoginToken.class);
            if (loginToken.required()) {
                // 执行认证
                if (token == null) {
                    throw new RuntimeException("无token,请重新登录");
                }
                // 获取 token 中的 user id
                String userId;
                try {
                    userId = JWT.decode(token).getAudience().get(0);
                } catch (JWTDecodeException j) {
                    throw new RuntimeException("401");
                }
                User user = userService.getUser(userId);
                if (user == null) {
                    throw new RuntimeException("用户不存在,请重新登录");
                }
                // 验证 token
                JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getPassWord())).build();
                try {
                    jwtVerifier.verify(token);
                } catch (JWTVerificationException e) {
                    throw new RuntimeException("401");
                }
                return true;
            }
        }
        return true;
    }
 
    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
 
    }
    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
 
    }
}

зарегистрировать перехватчик

package com.jw.config;
 
import com.jw.interceptor.JwtInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
 

@Configuration
public class InterceptorConfig implements WebMvcConfigurer{
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(jwtInterceptor())
            .addPathPatterns("/**");    // 拦截所有请求,通过判断是否有 @LoginRequired 注解 决定是否需要登录
 
        //注册TestInterceptor拦截器
//        InterceptorRegistration registration = registry.addInterceptor(jwtInterceptor());
//        registration.addPathPatterns("/**");                      //添加拦截路径
//        registration.excludePathPatterns(                         //添加不拦截路径
//            "/**/*.html",            //html静态资源
//            "/**/*.js",              //js静态资源
//            "/**/*.css",             //css静态资源
//            "/**/*.woff",
//            "/**/*.ttf",
//            "/swagger-ui.html"
//        );
    }
    @Bean
    public JwtInterceptor jwtInterceptor() {
        return new JwtInterceptor();
    }
}

ЛогинКонтроллер

@RestController
public class LoginController {
 
    @Autowired
    private UserService userService;
    @Autowired
    private TokenService tokenService;
 
    @PostMapping("login")
    public Object login(String username, String password){
        JSONObject jsonObject=new JSONObject();
        User user=userService.getUser(username, password);
        if(user==null){
            jsonObject.put("message","登录失败!");
            return jsonObject;
        }else {
            String token = tokenService.getToken(user);
            jsonObject.put("token", token);
            jsonObject.put("user", user);
            return jsonObject;
        }
    }
 
    @LoginToken
    @GetMapping("/getMessage")
    public String getMessage(){
        return "你已通过验证";
    }
}

Настроить глобальный захват исключений

@RestControllerAdvice
public class GlobalExceptionHandler {
    @ResponseBody
    @ExceptionHandler(Exception.class)
    public Object handleException(Exception e) {
        String msg = e.getMessage();
        if (msg == null || msg.equals("")) {
            msg = "服务器出错";
        }
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("code", 1000);
        jsonObject.put("message", msg);
        return jsonObject;
    }
}

почтальон тест

получить токен

image-20210714063353922

Вход без токена

image-20210714063447601

Войти с токеном

image-20210714063557098

неверный токен для входа

image-20210714063830525

image-20210714064150202

4. Преимущества и недостатки токена

преимущество:

  1. Поддержка междоменного доступа: Cookie не разрешает междоменный доступ, поддержка токенов;
  2. Без сохранения состояния: токен не имеет состояния, сессия имеет состояние;
  3. Разделение: нет необходимости привязываться к определенной схеме аутентификации. Токены могут быть сгенерированы где угодно, если вы можете сделать вызов генерации токена при вызове вашего API;
  4. Больше подходит для мобильных приложений: файлы cookie не поддерживают доступ к мобильному телефону;
  5. Производительность: в процессе сетевой передачи производительность выше;
  6. На основе стандартизации: ваш API может использовать стандартизированный веб-токен JSON (JWT). Этот стандарт уже существует во многих внутренних библиотеках (.NET, Ruby, Java, Python, PHP) и поддерживается несколькими компаниями (например, Firebase, Google, Microsoft). ).

недостаток:

  1. Занимайте пропускную способность, которая обычно больше, чем session_id, потребляйте больше трафика и сжимайте больше пропускной способности. Если на вашем веб-сайте 100 000 браузеров в месяц, это означает десятки мегабайт дополнительного трафика. Это не звучит как много, но это много денег с течением времени. На самом деле многие люди будут хранить больше информации в JWT;
  2. Выйти из системы на стороне сервера невозможно, поэтому проблему угона долго решить сложно;
  3. Проблемы с производительностью, одним из преимуществ JWT являются криптографические подписи, благодаря этой функции получатель может убедиться, что JWT действителен и доверен. Но в большинстве приложений веб-аутентификации JWT хранится в файле cookie, что означает наличие двух уровней подписей. Звучит круто, но преимущества нет, и за это вы платите вдвое больше ресурсов ЦП за проверку подписи. Это не идеально для веб-приложений с жесткими требованиями к производительности, особенно для однопоточных сред.

конец

Я кодер, которого бьют, и я пытаюсь двигаться вперед. Если статья была вам полезна, не забудьте поставить лайк и подписаться, спасибо!