当前位置: 首页 > 知识库问答 >
问题:

SecurityContext#setAuthentication能保证可见性吗?

白修谨
2023-03-14

我在我的项目中使用spring security。

我有更改登录的功能。为了实现这个目标,我使用以下代码

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

但现在我正在详细研究这段代码,并看到身份验证字段不是易变的,因此不保证可见性:

 html" target="_blank">public class SecurityContextImpl implements SecurityContext {

    private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;

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

    private Authentication authentication;

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

我读过https://stackoverflow.com/a/30781541/2674303

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

但我不明白Spring是如何保证能见度的。刚刚有消息称,会话中的每个线程都将看到更改。但没有答案:多快?更重要的是,没有解释可见性机制

共有3个答案

陶原
2023-03-14

Java有一套严格且定义良好的规则来规定内存的一致性。简而言之,有些事情肯定会先于其他事情发生。你可以在这里读到。一种实现方法是通过关键字volatile,另一种是通过synchronized

与任何文档一样,如果Spring没有明确说明它为您保证了什么,那么您不应该假设它保证了您想要的东西。

简而言之,您应该在synchronized块中包装更改或使用变量的代码。这将保证变量的值在使用完毕之前不会改变。Spring可能会为您更新变量,但它既不能保证跨线程的可见性,也不能保证同步,因为只有您的代码知道您何时以及如何使用该变量。

注意你应该使用同步(x),其中x是全局的、恒定的、固定的和所有线程之间共享的东西(即MyClass.class)-而不是变量本身。

蒋高杰
2023-03-14

如果您没有创建新线程或使用@Async,那么您在SecurityContext上设置新身份验证的方法是正确的。如果您确实创建了新线程,那么它分为两个用例:新线程是在您切换身份验证之前或之后创建的。如果您的线程是在设置了新的身份验证对象之后创建的,那么您必须创建一个身份验证感知的线程工厂,并将Authentication对象复制到它创建的任何线程。如果您的线程是之前创建的,那么请查看AuthenticationSetessEventAuthenticationEventPublisher,它们是Spring发送身份验证事件的官方方式,可能有一些有用的机制可以在该用户当前使用的所有线程之间传播身份验证更改。

在我以前的一个应用程序中,我必须实现管理员用户可以模拟普通用户的功能,以便帮助他们调试应用程序的问题,SecurityContextHolder.getContext(). setAuthentication(身份验证);你描述的被使用了,我们从来没有遇到过Spring混淆它应该作为哪个用户执行操作的任何问题。该应用程序在多节点集群上运行,所有节点共享一个公共会话缓存。

龙兴学
2023-03-14

你的怀疑是有道理的,但不能保证能见度。当所有ThreadLocalMap的条目存储同一个对象时,ThreadLocal不是线程安全的。

参考文档部分在请求之间存储SecurityContext,它警告您这一事实,并提出可能的解决方案,以某种方式更改上下文,防止对其他线程产生影响。

这种解决方案的一个例子是RunAs机制,它在安全对象回调阶段更改上下文。

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

Authentication接口具有getPrincipal()方法,该方法返回一个对象,这是一个UserDetails实例(在大多数情况下)。此对象通常用于获取当前(已验证)用户的用户名。

因此,如果您想“动态”更改经过身份验证的用户的登录名,您可以在这个UserDetails对象中修改username属性。

以线程安全的方式实现它的可能方法是使用易失性字符串用户名属性(默认User实现具有不可变的用户名)。

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

 类似资料:
  • 读了很多关于易失性、原子性和可见性的文章后,有一个问题仍然存在。以下跨线程工作,当更新/读取“B”时,“A”始终可见: 原子变量是独立的对象,这同样适用吗?下面的操作会起作用吗? 如果答案是否定的,那么扩展AtomicInteger类并在其中包含“a”就可以了,因为AtomicInteger包装了一个volatile。

  • 为了保障组件的性能, 我们有的时候会从组件渲染的角度出发. 更干净的render函数? 这个概念可能会有点让人疑惑. 其实在这里干净是指我们在shouldComponentUpdate这个生命周期函数里面去做浅比较, 从而避免不必要的渲染. 关于上面的干净渲染, 现有的一些实现包括React.PureComponent, PureRenderMixin, recompose/pure 等等. 第一

  • 当<code>JPanel</code>设置为不可见时,它是否仍然“可触摸”?在我的框架上有一个<code>JPanel</code>,面板上有按钮。如果我将面板设置为不可见,如果我按下按钮所在的位置(如果它可见),该按钮是否仍然有效? 我要求更好地理解,而不是实际上试图实现上面所说的内容。

  • 名为visibility的属性允许您隐藏视图中的元素。 您可以将此属性与JavaScript一起使用,以创建非常复杂的菜单和非常复杂的网页布局。 您可以选择使用visibility属性来隐藏仅在用户需要查看时显示的错误消息,或隐藏测验的答案,直到用户选择一个选项。 NOTE - 请记住,源代码仍将包含隐藏段落中的内容,因此您不应使用此代码隐藏敏感信息,如信用卡详细信息或密码。 visibility

  • 公共的/私有的 你的书本可以是 公共的,也可以是 私有的。公共的书本对所有人可见,但是只有合作者才可以更改它。私有书本只对合作者可见。 你可以将公共的书本变为私有,也可以将私有书本变成公共的。 收费书本 收费书本只可以是 公共的。 主页/浏览页面 主页和浏览页面仅仅会展示已经成功构建的书本。建议给书本设置一张封面图片。

  • 我想让谷歌的某个人提供一些关于云Bigtable服务所提供的耐久性和可用性保证的指导方针。 以下是我到目前为止的理解: > 最小群集需要3个节点这一事实表明,至少在一个区域内,数据是高度持久的,并且可以复制到3个节点。 没有关于跨区域的Bigtable集群是否独立的信息。如果我要跨多个区域设置集群,而一个区域关闭了,我们能期望其他区域中的集群继续工作吗?或者,是否存在一些潜在的单点故障,甚至可能跨