Предыдущие статьи в основном были посвящены содержанию, связанному с аутентификацией личности. Сегодня мы в основном обсуждаем поддержание статуса аутентификации. Поскольку протокол HTTP не имеет состояния, после успешной аутентификации, чтобы позволить последующим запросам продолжать поддерживать статус аутентификации. Чтобы избежать необходимости повторно инициировать процесс аутентификации для каждого запроса, вам необходимо сохранить результаты аутентификации, а затем запросить и восстановить соответствующую аутентификацию при поступлении нового запроса. Обычно существует две схемы реализации. Одна — классическая схема сеанса cookie, которая получает доступ к информации аутентификации в атрибуте сеанса сервера. Преимущество заключается в том, что метод реализации относительно прост. Другой — схема токена. использует некоторые алгоритмы. Преимущество кодирования и декодирования аутентификационной информации заключается в том, что ее не нужно реализовывать, что эффективно снижает нагрузку на хранилище на стороне сервера. В этой статье в основном рассматривается Spring. Аутентификация на основе сеанса и общие механизмы управления в структуре безопасности.
Чтобы лучше понять, как работает сеанс, необходимо ознакомиться с некоторыми базовыми знаниями о сеансе. Давайте возьмем Tomcat в качестве примера, чтобы кратко представить, как сеанс поддерживается на стороне сервера.
Примечание. Показанный ниже сеанс представляет собой интерфейс, определенный в Tomcat.,И то, что мы обычно называем сеансом,это интерфейс HttpSession, определенный в jakarta.servlet.http (или java.servlet.http),В Tomcat у них есть общая реализация, добрая, как StandardSession.,через фасадный режим,В конце концов, фактическими операциями являются объекты StandardSession.
В Tomcat ManagerBase в основном отвечает за поддержание объекта сеанса. Исходный код выглядит следующим образом. Вы можете видеть, что объект сеанса фактически хранится в ConcurrentHashMap, а его ключ — sessionId в соответствии со значением JSESSIONID в файле cookie. по запросу вы можете запросить его через метод findSession к соответствующему объекту сеанса.
public abstract class ManagerBase extends LifecycleMBeanBase implements Manager {
...
protected Map<String,Session> sessions = new ConcurrentHashMap<>();
...
@Override
public Session findSession(String id) throws IOException {
if (id == null) {
return null;
}
return sessions.get(id);
}
@Override
public void add(Session session) {
sessions.put(session.getIdInternal(), session);
int size = getActiveSessions();
if (size > maxActive) {
synchronized (maxActiveUpdateLock) {
if (size > maxActive) {
maxActive = size;
}
}
}
}
@Override
public Session createSession(String sessionId) {
if ((maxActiveSessions >= 0) && (getActiveSessions() >= maxActiveSessions)) {
rejectedSessions++;
throw new TooManyActiveSessionsException(sm.getString("managerBase.createSession.ise"), maxActiveSessions);
}
// Recycle or create a Session instance
Session session = createEmptySession();
// Initialize the properties of the new session and return it
session.setNew(true);
session.setValid(true);
session.setCreationTime(System.currentTimeMillis());
session.setMaxInactiveInterval(getContext().getSessionTimeout() * 60);
String id = sessionId;
if (id == null) {
id = generateSessionId();
}
session.setId(id); // Внутри этого метода будет вызван метод добавления, указанный выше, для сохранения сеанса в ConcurrentHashMap.
SessionTiming timing = new SessionTiming(session.getCreationTime(), 0);
synchronized (sessionCreationTiming) {
sessionCreationTiming.add(timing);
sessionCreationTiming.poll();
}
return session;
}
}
кроме того,Метод createSession может генерировать новый объект сеанса.,Будет вызван метод session#setId,В это время сеанс будет сохранен в ConcurrentHashMap.,Этот метод в основном используется в сценариях, где HttpServleRequest получает текущий сеанс.,Вы можете еще раз взглянуть на реализацию HttpServleRequest в Tomcat добрыйRequest.,При вызове метода Request#doGetSession,Если объект сеанса в данный момент не найден,Будет вызван метод createSession,Создать новый сеанс,Затем создайте соответствующий файл cookie,Его имя по умолчанию — «JSESSIONID».,и добавлен в ответ,Наконец, возвращается объект сеанса.
@Override
public HttpSession getSession(boolean create) {
Session session = doGetSession(create);
if (session == null) {
return null;
}
return session.getSession(); // Фасадный режим: инкапсулируйте сам сеанс в объект StandardSessionFacade и возвращайте его. StandardSessionFacade — это реализация интерфейса HttpSession в Tomcatдобрый.
}
protected Session doGetSession(boolean create) {
Context context = getContext();
...
Manager manager = context.getManager();
...
if (requestedSessionId != null) { // Из файлов cookie SessionId, проанализированный из JSESSIONID, также может быть нулевым.
try {
session = manager.findSession(requestedSessionId); // Поиск ConcurrentHashMap в менеджере
} catch (IOException e) {
...
}
...
if (session != null) {
session.access(); // Фиксируйте количество посещений сеанса
return session;
}
}
// Create a new session if requested and the response is not committed
if (!create) {
return null;
}
...
String sessionId = getRequestedSessionId();
...
session = manager.createSession(sessionId); // Создать новый сеанс
// Creating a new session cookie based on that session
if (session != null && trackModesIncludesCookie) {
Cookie cookie =
ApplicationSessionCookieConfig.createSessionCookie(context, session.getIdInternal(), isSecure()); // Здесь будет создан файл cookie с именем «JSESSIONID».
response.addSessionCookieInternal(cookie);
}
if (session == null) {
return null;
}
session.access();
return session;
}
Выше приведена базовая реализация создания и чтения сеанса в tomcat. Из-за ограничений по объему другие методы, связанные с сеансом, здесь не представлены.
Во-первых, давайте кратко представим, как сохранять и читать объект SecurityContext с помощью механизма Session. Фактически, весь процесс в основном инкапсулируется инфраструктурой Spring Security, которая не требует слишком больших затрат на разработку для разработчиков. Весь процесс примерно представлен простой диаграммой последовательности:
Подводя итог, можно выделить в основном следующие этапы:
Давайте посмотрим на конкретные детали реализации.
Упоминалось выше для Сохранить контекст интерфейсаSecurityContextRepository безопасности, его добрая реализация по умолчанию — DelegatingSecurityContextRepository, а его метод сохранения делегируется двум объектам в конфигурации по умолчанию, а именно HttpSessionS. ecurityContextRepository и RequestAttributeSecurityContextRepository, где RequestAttributeSecurityContextRepository фактически не сохраняется, а просто сохраняет SecurityCo. ntext сохраняется в атрибуте запроса, поэтому в других последующих запросах объект SecurityContext не может быть получен. Он применим только к сценарию внутренней отправки, а HttpSessionSecurityContextRepository в основном отвечает за использование сеанса для достижения постоянства. относительно Относительно просто: сначала сгенерируйте сеанс методом request#getSession, затем запишите объект SecurityContext в атрибут сеанса (SPRING_SECURITY_CONTEXT) и, наконец, установите соответствующий Cookie в ответе и запишите его в клиент браузера. Ниже приведен исходный код метода HttpSessionSecurityContextRepository#saveContext, Весной. В новой версии Security, как правило, маловероятно, что объект SaveContextOnUpdateOrErrorResponseWrapper не является пустым (причина будет объяснена в разделе 2.3. Фактический метод сохранения объекта SecurityContext — setContextInSession).
@Override
public void saveContext(SecurityContext context, HttpServletRequest request, HttpServletResponse response) {
SaveContextOnUpdateOrErrorResponseWrapper responseWrapper = WebUtils.getNativeResponse(response,
SaveContextOnUpdateOrErrorResponseWrapper.class);
if (responseWrapper == null) {
saveContextInHttpSession(context, request); // Обычно этот метод выполняется
return;
}
responseWrapper.saveContext(context);
}
private void saveContextInHttpSession(SecurityContext context, HttpServletRequest request) {
if (isTransient(context) || isTransient(context.getAuthentication())) {
return;
}
SecurityContext emptyContext = generateNewContext();
if (emptyContext.equals(context)) {
HttpSession session = request.getSession(false);
removeContextFromSession(context, session);
}
else {
boolean createSession = this.allowSessionCreation;
HttpSession session = request.getSession(createSession);
setContextInSession(context, session);
}
}
private void setContextInSession(SecurityContext context, HttpSession session) {
if (session != null) {
session.setAttribute(this.springSecurityContextKey, context); //Сохраняем объект SecurityContext в атрибуте сеанса, где значением по умолчанию для SpringSecurityContextKey является «SPRING_SECURITY_CONTEXT_KEY»
if (this.logger.isDebugEnabled()) {
this.logger.debug(LogMessage.format("Stored %s to HttpSession [%s]", context, session));
}
}
}
Процесс загрузки SecurityContext в основном завершается в фильтре SecurityContextHolderFilter. Поскольку многим другим фильтрам необходимо полагаться на SecurityContext для получения аутентификационной информации при выполнении бизнес-логики, этот фильтр имеет более высокий приоритет во всей SecurityFilterChain. Исходный код выглядит следующим образом:
private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
if (request.getAttribute(FILTER_APPLIED) != null) {
chain.doFilter(request, response);
return;
}
request.setAttribute(FILTER_APPLIED, Boolean.TRUE);
Supplier<SecurityContext> deferredContext = this.securityContextRepository.loadDeferredContext(request);
try {
this.securityContextHolderStrategy.setDeferredContext(deferredContext);
chain.doFilter(request, response);
}
finally {
this.securityContextHolderStrategy.clearContext();
request.removeAttribute(FILTER_APPLIED);
}
}
Метод loadDeferredContext объекта SecurityContextRepository вызывается при загрузке. Этот метод возвращает объект отложенного доступа в режиме поставщика (просто понимаемый как возврат записи для доступа к объекту SecurityContext, которому требуется только доступ к Secur). ityContext, что может повысить определенную эффективность). Если процесс аутентификации не был инициирован ранее, здесь будет создан пустой SecurityContext. Если он был аутентифицирован, предыдущий процесс аутентификации будет получен из сохраненного экземпляра SecurityContext. . Ниже приведен исходный код метода HttpSessionSecurityContextRepository#loadDeferredContext.
public DeferredSecurityContext loadDeferredContext(HttpServletRequest request) {
Supplier<SecurityContext> supplier = () -> readSecurityContextFromSession(request.getSession(false));
return new SupplierDeferredSecurityContext(supplier, this.securityContextHolderStrategy);
}
private SecurityContext readSecurityContextFromSession(HttpSession httpSession) {
if (httpSession == null) {
this.logger.trace("No HttpSession currently exists");
return null;
}
// Session exists, so try to obtain a context from it.
Object contextFromSession = httpSession.getAttribute(this.springSecurityContextKey); //Получаем значение атрибута, соответствующее сессии, через SpringSecurityContextKey и получаем объект SecurityContext через доброе преобразование типа
...
// Everything OK. The only non-null return from this method.
return (SecurityContext) contextFromSession;
}
Фактически, до Spring Security 5.7 загрузка SecurityContext обрабатывалась не SecurityContextHolderFilter, а SecurityContextPersistenceFilter. Между ними есть большая разница. SecurityContextPersistenceFilter также отвечает за автоматическое сохранение объекта SecurityContext. Наконец, взгляните на его метод doFilter. блок кода, здесь метод saveContext SecurityContextRepository будет вызываться один раз.
private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws IOException, ServletException {
...
HttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, response);
SecurityContext contextBeforeChainExecution = this.repo.loadContext(holder);
try {
this.securityContextHolderStrategy.setContext(contextBeforeChainExecution);
...
chain.doFilter(holder.getRequest(), holder.getResponse());
}
finally {
SecurityContext contextAfterChainExecution = this.securityContextHolderStrategy.getContext();
// Crucial removal of SecurityContextHolder contents before anything else.
this.securityContextHolderStrategy.clearContext();
this.repo.saveContext(contextAfterChainExecution, holder.getRequest(), holder.getResponse()); // Больше не требуется автоматическое сохранение в SecurityContextHolderFilter. контекст безопасности
request.removeAttribute(FILTER_APPLIED);
this.logger.debug("Cleared SecurityContextHolder to complete request");
}
}
В старой версии каждый механизм аутентификации не использует напрямую SecurityContextRepository для сохранения объекта SecurityContext, созданного после аутентификации. Поэтому перед отправкой ответа, если SecurityContext изменяется после выполнения этого запроса, например, создается новый аутентифицированный объект аутентификации. set, то SecurityContext необходимо сохранить.
Например, фильтр RememberMeAuthenticationFilter используется для реализации механизма входа в систему «запомнить меня», то есть для определения статуса входа с помощью определенного файла cookie, что позволяет избежать повторной инициации запросов аутентификации в течение относительно длительного периода времени. Исходный код. Spring Security 5.6 выглядит следующим образом
private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws IOException, ServletException {
...
Authentication rememberMeAuth = this.rememberMeServices.autoLogin(request, response);
if (rememberMeAuth != null) {
// Attempt authentication via AuthenticationManager
try {
rememberMeAuth = this.authenticationManager.authenticate(rememberMeAuth);
// Store to SecurityContextHolder
SecurityContext context = SecurityContextHolder.createEmptyContext();
context.setAuthentication(rememberMeAuth);
SecurityContextHolder.setContext(context);
onSuccessfulAuthentication(request, response, rememberMeAuth);
// this.securityContextRepository.saveContext(context, request, response); Весной Security 5.7 и выше, добавлены сохранения контекст безопасностишаги ...
}
catch (AuthenticationException ex) {
...
}
}
chain.doFilter(request, response);
}
можно увидеть,После прохождения аутентификации RememberMe AuthenticationProvider,Создал новый SecurityContext.,Объект аутентификации установлен,Затем размещается SecurityContextHolder.,Но в конце концов это длилось недолго.,Итак, прежде чем будет отправлен окончательный ответ,Сохранение должно быть завершено единообразно с помощью SecurityContextPersistenceFilter.,Этот механизм кажется разумным,Позволяет избежать необходимости использовать SecurityContextRepository для каждого механизма аутентификации.,Так почему же в новой версии отсутствует фильтр SecurityContextPersistenceFilter? в официальной документации,Этому есть определенное объяснение: потому что происходят изменения в SecurityContext,Этот процесс отслеживания относительно сложен.,Вы можете проверить исходный код старой версии,Он использует различные оболочки HttpServletResponse добрый.,В различных методах Repsonse,Например, перенаправление sendRedirect,Исключения, такие как sendError, были скрыты.,Суждение, основанное на многих условиях,Чтобы определить, следует ли сохранять контекст безопасности,Это вызовет несколько операций чтения и записи HttpSession.,Но на самом деле большинство операций чтения и записи не являются необходимыми.,После выполнения каждого запроса,Вам предстоит пройти эти операции,По соображениям эффективности и производительности,В новой версии убрана логика автоматического сохранения.,Делает процесс доступа SecurityContext более легким.
public class SessionInformation implements Serializable {
...
private Date lastRequest; // Самое последнее время доступа. При каждом запросе, если текущий сеанс не истек, время будет обновляться.
private final Object principal; // Идентифицирует текущего пользователя, обычно имя пользователя
private final String sessionId; // SessionId, соответствующий HttpSession.
private boolean expired = false; // Определяет, истек ли текущий сеанс
...
}
Вот некоторые распространенные сценарии управления сеансами:
Управление параллелизмом сеансов. Наиболее распространенным сценарием является ограничение одной учетной записи, позволяющей нескольким клиентам входить в систему одновременно. То есть, когда второй клиент инициирует вход в систему и проходит аутентификацию, срок действия ранее проверенного сеанса будет установлен. и пользователь больше не сможет одновременно войти в систему. Вам придется выйти из системы.
Конфигурация также очень проста: элемент конфигурации sessionConcurrency в sessionMangement DSL настраивает maxinumSession на «1».
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
...
http.sessionManagement(session -> session.sessionConcurrency(concurrency -> concurrency.maximumSessions(1)));
...
return http.build();
}
Конкретную реализацию завершает реализация стратегии добрыйConcurrentSessionControlAuthenticationStrategy и фильтр ConcurrentSessionFilter.,Первый в ConcurrentSessionControlAuthenticationStrategy,Установите срок действия определенных сеансов, которые не соответствуют требованиям, на основании правил.,Продолжайте юридические заседания,Таким образом, очищая его в ConcurrentSessionFilter,Или обновите время обновления RefLastRequest.
Давайте сначала посмотрим на исходный код метода ConcurrentSessionControlAuthenticationStrategy#onAuthentication, где разрешенные сеансы — это максимальное количество сеансов, которое мы установили в конфигурации, а затем получим все сеансы текущего пользователя через sessionRegistry. ionInformation и подсчитать его количество. При превышении указанного максимального количества сеансов вызывается методallowableSessionsExceeded, который будет отсортирован по времени LastRequest. Наконец, для тех сеансов, которые превышают максимальное количество сеансов и имеют более раннее время, будет установлено время истечения.
public void onAuthentication(Authentication authentication, HttpServletRequest request,
HttpServletResponse response) {
int allowedSessions = getMaximumSessionsForThisUser(authentication);
if (allowedSessions == -1) { // Если параметр MaximumSessions равен -1, это означает, что количество сеансов не ограничено.
// We permit unlimited logins
return;
}
List<SessionInformation> sessions = this.sessionRegistry.getAllSessions(authentication.getPrincipal(), false);
int sessionCount = sessions.size();
if (sessionCount < allowedSessions) {
// They haven't got too many login sessions running at present
return;
}
if (sessionCount == allowedSessions) { // Если количество sessionInformations, принадлежащих пользователю, точно равно MaximumSessions, определите, включен ли текущий идентификатор сеанса в эти sessionInformations. Если нет, это означает, что сеанс необходимо признать недействительным.
HttpSession session = request.getSession(false);
if (session != null) {
// Only permit it though if this request is associated with one of the
// already registered sessions
for (SessionInformation si : sessions) {
if (si.getSessionId().equals(session.getId())) {
return;
}
}
}
// If the session is null, a new one will be created by the parent class,
// exceeding the allowed number
}
allowableSessionsExceeded(sessions, allowedSessions, this.sessionRegistry);
}
protected void allowableSessionsExceeded(List<SessionInformation> sessions, int allowableSessions,
SessionRegistry registry) throws SessionAuthenticationException {
if (this.exceptionIfMaximumExceeded || (sessions == null)) {
throw new SessionAuthenticationException(
this.messages.getMessage("ConcurrentSessionControlAuthenticationStrategy.exceededAllowed",
new Object[] { allowableSessions }, "Maximum sessions of {0} for this principal exceeded"));
}
// Determine least recently used sessions, and mark them for invalidation
sessions.sort(Comparator.comparing(SessionInformation::getLastRequest));
int maximumSessionsExceededBy = sessions.size() - allowableSessions + 1;
List<SessionInformation> sessionsToBeExpired = sessions.subList(0, maximumSessionsExceededBy);
for (SessionInformation session : sessionsToBeExpired) {
session.expireNow();
}
}
И в ConcurrentSessionFilter,Будет запрошен объект SessionInformation, соответствующий текущему идентификатору сеанса.,Определите, помечен ли он как просроченный,Если срок его действия не истек,Затем вызовите sessionRegistry#refreshLastRequest, чтобы обновить время.,Если срок его действия истек,затем вызовите логику выхода из системы,Включая признание недействительным объекта HttpSession.,Очистите объекты SecurityContext в SecurityContextRepository и т. д.,Поэтому его приоритет в SecurityFilterChain обычно стоит после SessionManagementFilter (ранняя версия) и Filter, соответствующих различным механизмам аутентификации (таким как реализация AbstractAuthenticationProcessingFilter добрый),Ниже приведен исходный код метода doFilter.
private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpSession session = request.getSession(false);
if (session != null) {
SessionInformation info = this.sessionRegistry.getSessionInformation(session.getId());
if (info != null) {
if (info.isExpired()) {
// Expired - abort processing
this.logger.debug(LogMessage
.of(() -> "Requested session ID " + request.getRequestedSessionId() + " has expired."));
doLogout(request, response); // sessionInformation помечается как истекший, выполняется логика выхода из системы и реализуется политика истечения срока действия.
this.sessionInformationExpiredStrategy
.onExpiredSessionDetected(new SessionInformationExpiredEvent(info, request, response)); //Реализацией по умолчанию является ResponseBodySessionInformationExpiredStrategy, которая записывает в Response текст, информирующий пользователя о том, что срок действия сеанса истек.
return;
}
// Non-expired - update last request date/time
this.sessionRegistry.refreshLastRequest(info.getSessionId()); // Для сеансов с истекшим сроком действия обновите время последнего запроса.
}
}
chain.doFilter(request, response);
}
Чтобы ConcurrentSessionControlAuthenticationStrategy быстро получала все сеансы под пользователем при очистке избыточных сеансов, необходимо регистрировать новую информацию о сеансе после прохождения каждой аутентификации. Поэтому RegisterSessionAuthenticationStrategy часто используется совместно с ConcurrentSessionControlAuthenticationStrategy, упомянутым в предыдущем разделе.
public class RegisterSessionAuthenticationStrategy implements SessionAuthenticationStrategy {
private final SessionRegistry sessionRegistry;
...
@Override
public void onAuthentication(Authentication authentication, HttpServletRequest request,
HttpServletResponse response) {
this.sessionRegistry.registerNewSession(request.getSession().getId(), authentication.getPrincipal());
}
}
public class SessionRegistryImpl implements SessionRegistry, ApplicationListener<AbstractSessionEvent> {
...
private final ConcurrentMap<Object, Set<String>> principals;
private final Map<String, SessionInformation> sessionIds;
...
@Override
public void registerNewSession(String sessionId, Object principal) {
...
if (getSessionInformation(sessionId) != null) {
removeSessionInformation(sessionId);
}
...
this.sessionIds.put(sessionId, new SessionInformation(principal, sessionId, new Date()));
this.principals.compute(principal, (key, sessionsUsedByPrincipal) -> {
if (sessionsUsedByPrincipal == null) {
sessionsUsedByPrincipal = new CopyOnWriteArraySet<>();
}
sessionsUsedByPrincipal.add(sessionId);
this.logger.trace(LogMessage.format("Sessions used by '%s' : %s", principal, sessionsUsedByPrincipal));
return sessionsUsedByPrincipal;
});
}
}
Если во время регистрации исходный sessionId имеет соответствующий объект SessionInformation, сначала очистите его, затем создайте новый объект SessionInformation, запишите связь между sessionId и объектом SessionInformation в карту sessionIds и запишите связь между субъектом и sessionId в карту. руководителей.
Так называемая атака с фиксированным сеансом в основном означает, что сеанс, используемый пользователем до входа в систему и после входа в систему, остается неизменным. Таким образом, злоумышленник может заранее подготовить сеанс, а затем побудить пользователя использовать этот сеанс для входа. in. Наконец, злоумышленник может успешно использовать этот сеанс, выдавая себя за этого пользователя, чтобы войти в систему.
Весной Безопасность предлагает 3 настраиваемые защиты. от фиксированных атак стратегии сеанса, а именно:changeSessionId, newSession иmigrSession (настроенные следующим образом), гдеchangeSessionId соответствует реализации ChangeSessionIdAuthenticationStrategy добрый, newSession иmigrSession, соответствующий реализации SessionFixationProtectionStrategy. Первый не требует создания нового сеанса, а последние два создают новый сеанс. Разница в том, что newSession не сохраняет значения атрибутов исходного сеанса. (только весна собственные атрибуты безопасности), а миграция Session перенесет исходные атрибуты сеанса.
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
...
http.sessionManagement(session -> session.sessionFixation(fixation -> fixation.changeSessionId()));
http.sessionManagement(session -> session.sessionFixation(fixation -> fixation.newSession()));
http.sessionManagement(session -> session.sessionFixation(fixation -> fixation.migrateSession()));
...
return http.build();
}
Конкретные детали двух реализаций доброго представлены ниже.
Реализация ChangeSessionIdAuthenticationStrategy является самой простой. Исходный код следующий: он использует метод HttpServletRequest#changeSessionId для присвоения нового sessionId текущему сеансу. ConcurrentHashMap, поддерживаемый ManagerBase, обновляется, старый sessionId удаляется, а новый sessionId добавляется в качестве ключа. Таким образом, старый sessionId не может использоваться для запроса объекта сеанса. Этот метод реализации является относительно легким, но он также является. полагается на поддержку базового контейнера, поэтому его можно использовать только в Servlet 3.1и более новые версии контейнера.,Это также реализация по умолчаниюдобрый。
public final class ChangeSessionIdAuthenticationStrategy extends AbstractSessionFixationProtectionStrategy {
@Override
HttpSession applySessionFixation(HttpServletRequest request) {
request.changeSessionId();
return request.getSession();
}
}
Реализация SessionFixationProtectionStrategy немного сложнее. Она эквивалентна работе по миграции сеанса: сначала атрибуты исходного сеанса временно сохраняются в Map (упоминалось выше, при выборе newSession только Spring). самоопределенные атрибуты безопасности), а затем аннулируют старый сеанс и создают новый сеанс. Наконец, все атрибуты, которые были временно сохранены в карте, переносятся. Этот метод реализации относительно тяжелый, поэтому он используется только в Servlet3. 0 и более ранние версии контейнера в качестве реализации по умолчанию.
final HttpSession applySessionFixation(HttpServletRequest request) {
HttpSession session = request.getSession();
String originalSessionId = session.getId();
this.logger.debug(LogMessage.of(() -> "Invalidating session with Id '" + originalSessionId + "' "
+ (this.migrateSessionAttributes ? "and" : "without") + " migrating attributes."));
Map<String, Object> attributesToMigrate = extractAttributes(session);
int maxInactiveIntervalToMigrate = session.getMaxInactiveInterval();
session.invalidate();
session = request.getSession(true); // we now have a new session
this.logger.debug(LogMessage.format("Started new session: %s", session.getId()));
transferAttributes(attributesToMigrate, session);
if (this.migrateSessionAttributes) {
session.setMaxInactiveInterval(maxInactiveIntervalToMigrate);
}
return session;
}
В этой статье представлена базовая реализация сеанса, процесс доступа к SecurityContext в сеансе и общие сценарии управления сеансом. Наконец, подводится итог: