Подтвердить что ты не робот

Обеспечивает ли SecurityContext # setAuthentication видимость?

Я использую spring безопасность в своем проекте.

У меня есть функция для изменения имени пользователя. Для достижения этой цели я использую следующий код

Authentication authentication = ...
SecurityContextHolder.getContext().setAuthentication(authentication);

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

 public class SecurityContextImpl implements SecurityContext {

    private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;

    // ~ Instance fields
    // ================================================================================================

    private Authentication authentication;

Должен ли я обернуть свой код собственной синхронизацией для достижения видимости?

P.S.

Я прочитал qaru.site/info/240966/...

В приложение, которое получает одновременные запросы за один сеанс, тот же экземпляр SecurityContext будет разделяться между потоками. Даже хотя ThreadLocal используется, это тот же самый экземпляр, который извлекается из HttpSession для каждого потока. Это имеет последствия если вы хотите временно изменить контекст, под которым находится поток Бег. Если вы просто используете SecurityContextHolder.getContext() и вызываете setAuthentication (anAuthentication) на возвращаемом объекте контекста, то объект аутентификации будет изменяться во всех параллельных потоках которые имеют один и тот же экземпляр SecurityContext. Вы можете настроить поведение SecurityContextPersistenceFilter для создания полностью новый SecurityContext для каждого запроса, предотвращая изменения в одном потоке от воздействия на другое. В качестве альтернативы вы можете создать новый экземпляр просто в точке, где вы временно меняете контекст. Метод SecurityContextHolder.createEmptyContext() всегда возвращает новый контекстный экземпляр.

но я не понимаю, как spring гарантирует видимость. Есть только что написано, что каждый поток в сеансе увидит изменения. но нет ответа, как быстро? и что более важно - механизм видимости не объясняется

4b9b3361

Ответ 1

Ваши сомнения оправданы, видимость не гарантирована . ThreadLocal не является потокобезопасным, когда все записи ThreadLocalMap сохраняют один и тот же объект.

Ссылка на документацию, Сохранение SecurityContext между запросами, предупреждает вас об этом факте и предлагает возможные решения для изменения контекста таким образом, чтобы предотвращает влияние на другие потоки.

Примером такого решения является механизм RunAs, который изменяет контекст во время фазы обратного вызова защищенного объекта.

Но, насколько я понимаю ваш вопрос, вам нужно изменить логин пользователя (то есть имя пользователя) "на лету". Если я прав, то проблема в том, что если вы установили измененный Authentication - другой поток мог бы прочитать старое значение. Чтобы избежать этого состояния гонки, вам нужно сделать логин, чтобы написать до того, как будет прочитано каждый последовательный вход в систему.

Authentication интерфейс имеет getPrincipal() метод, который возвращает Object, который является экземпляром UserDetails (в большинстве случаев). Этот объект обычно используется для получения имени пользователя для текущего (прошедшего проверку подлинности) пользователя.

Итак, если вы хотите изменить логин для аутентифицированного пользователя "на лету", вы можете изменить свойство username в этом объекте UserDetails.

Возможный способ сделать это поточно-безопасным способом - это обычная реализация UserDetails с свойством volatile String username (по умолчанию User имеет неизменяемое имя пользователя).

Вы также должны создать и подключить в свою конфигурацию реализацию UserDetailsService, которая будет использовать ваш пользовательский UserDetails.

Ответ 2

Если вы не создаете новые потоки или используете @Async, то ваш подход к настройке новой проверки подлинности в SecurityContext правильный. Если вы создаете новые потоки, то это происходит в двух случаях: новые потоки создаются до или после переключения проверки подлинности. Если ваши потоки создаются после установки нового объекта проверки подлинности, вам необходимо создать поток factory, который будет проверять подлинность, и скопировать объект аутентификации во все созданные им потоки. Если ваши потоки создаются ранее, взгляните на AuthenticationSuccessEvent и AuthenticationEventPublisher, они Spring официальный способ сигнализации событий auth, может быть какой-то полезный механизм для распространения изменений аутентификации во всех потоках, используемых в данный момент этим пользователем.

В одном из моих предыдущих приложений мне пришлось реализовать функциональность, в которой пользователи-администраторы могли выдавать себя за обычных пользователей, чтобы помочь им отлаживать проблемы с приложением, SecurityContextHolder.getContext().setAuthentication(authentication);, которые вы описали, и мы никогда не сталкивались с какими-либо проблемами с Spring запутывая, какой пользователь должен выполнять действия как. Приложение запускалось в кластере multi node с общим кешем сеанса, общим для всех узлов.

Ответ 3

Java имеет набор строгих и четко определенных правил, которые определяют согласованность памяти. Короче говоря, некоторые вещи гарантированно случаются - перед другими вещами. Вы можете прочитать об этом здесь. Один из способов достижения - прежде всего через ключевое слово volatile, а другое - через synchronized.

Как и в любой документации, если Spring явно не ясно, что она для вас гарантирует, вы не должны предполагать, что она гарантирует то, что вы хотите.

Короче говоря, вы должны обернуть свой код, который изменяет или использует переменную в блоке synchronized. Это гарантирует, что значение переменной не изменится, пока вы не закончите ее использование. Spring может обновлять переменные для вас, но это не гарантирует ни видимости перекрестных потоков, ни синхронизации, потому что только ваш код знает, когда и как вы используете эту переменную.

Обратите внимание, что вы должны использовать synchronized (x), где x является чем-то глобальным, постоянным, фиксированным и общим для всех потоков (то есть MyClass.class) - НЕ является самой переменной.