SecurityContext#setAuthentication是否保证可见性?

gst*_*low 22 java multithreading visibility spring-security thread-safety

我在项目中使用spring security.

我有更改登录功能.为实现这一目标,我使用以下代码

Authentication authentication = ...
SecurityContextHolder.getContext().setAuthentication(authentication);
Run Code Online (Sandbox Code Playgroud)

但是现在我正在详细讨论这段代码并且看到认证字段不是volatile因此不能保证可见性:

 public class SecurityContextImpl implements SecurityContext {

    private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;

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

    private Authentication authentication;
Run Code Online (Sandbox Code Playgroud)

我应该用自己的同步来包装我的代码以实现可见性吗?

PS

我已阅读/sf/answers/2154707901/

在单个会话中接收并发请求的应用程序中,将在线程之间共享相同的SecurityContext实例.即使正在使用ThreadLocal,它也是从HttpSession为每个线程检索的相同实例.如果您希望临时更改运行线程的上下文,则会产生影响.如果您只使用SecurityContextHolder.getContext(),并对返回的上下文对象调用setAuthentication(anAuthentication),则Authentication对象将在共享同一SecurityContext实例的所有并发线程中更改.您可以自定义SecurityContextPersistenceFilter的行为,为每个请求创建一个全新的SecurityContext,防止一个线程中的更改影响另一个线程.或者,您可以在临时更改上下文的位置创建新实例.SecurityContextHolder.createEmptyContext()方法始终返回新的上下文实例.

但我不明白春天如何保证能见度.刚写过会话中的每个线程都会看到变化.但没有答案有多快?更重要的是 - 没有解释可见性机制

Ana*_*mov 8

您的疑虑是合理的,可见性不能保证.当所有ThreadLocalMap的条目存储相同的对象时,ThreadLocal不是线程安全的.

引用文档部分,在请求之间存储SecurityContext,警告您有关该事实并提出可能的解决方案以某种方式更改上下文,从而防止对其他线程的影响.

此类解决方案的一个示例是RunAs机制,它在安全对象回调阶段更改上下文.

但是,据我了解你的问题,你需要"动态"更改用户的登录名(即用户名).如果我是对的,那么问题是,当你设置修改时Authentication- 另一个线程可以读取旧值.为了避免这种竞争条件,您需要每次顺序登录读取之前进行登录写入.

Authenticationinterface有一个getPrincipal()方法,它返回Object一个UserDetails实例(在大多数情况下).此对象通常用于获取当前(经过身份验证的)用户的用户名.

因此,如果要"动态"更改已通过身份验证的用户的登录,则可以修改usernameUserDetails对象中的属性.

以线程安全的方式实现它的可能方法是UserDetails使用volatile String username属性的自定义实现(默认User实现具有不可变的用户名).

您还应该创建一个UserDetailsService实现并将其连接到您的配置中,该实现将使用您的自定义UserDetails.