ChatGPT解决这个技术问题 Extra ChatGPT

Spring Security 中的“主体”是什么?

我对 Spring 和 Spring Security 真的很陌生。我正在阅读有关 Spring Security 的内容,它提出了主体的概念,它应该是当前登录的用户。但是,如果我们有多个当前登录用户怎么办?那么,我的问题是,春季安全的主要负责人究竟是什么?

例如,我已经阅读了本教程:

http://www.mkyong.com/spring-security/get-current-logged-in-username-in-spring-security/

他们似乎考虑到当前只有一个登录用户,但这种情况并不常见。

如何检索特定用户?以及如何区分正在执行请求的用户?


M
Marcel Stör

主体是当前登录的用户。但是,您通过绑定到当前线程的安全上下文来检索它,因此它也绑定到当前请求及其会话。

SecurityContextHolder.getContext() 通过 ThreadLocal 变量在内部获取当前的 SecurityContext 实现。因为请求绑定到单个线程,这将为您提供当前请求的上下文。

为简化起见,您可以说安全上下文在会话中,并且包含用户/主体和角色/权限。

如何检索特定用户?

你没有。所有 API 都旨在允许访问用户和当前请求的会话。让用户 A 成为 100 个当前经过身份验证的用户之一。如果 A 向您的服务器发出请求,它将分配一个线程来处理该请求。如果您随后执行 SecurityContextHolder.getContext().getAuthentication(),您将在此线程的上下文中执行此操作。默认情况下,您无法从该线程中访问由不同线程处理的用户 B 的上下文。

以及如何区分正在执行请求的用户?

您不必这样做,这就是 Servlet 容器为您所做的。


所以,我随之而来的问题是:spring 如何区分会话和用户?我想这需要对Spring有更深入的了解......
谢谢!不错的答案。我只有一个疑问,这来自于我在创建 Web 应用程序等方面的经验减少。但在此之前,让我试着解释一下我认为我知道的事情。据我所知,可以使用会话 ID 来增强 HTTP 请求。 Tomcat(或其他)可能使用此信息来区分用户和请求。 Principal 是当前上下文的当前登录用户。要了解有一个登录用户(委托人),可能必须与一个会话号相关联。
现在,假设我有两个名为 rootadmin 的登录用户,并且我有一个请求映射为 /users/{username} 的控制器。进一步假设 root 在客户端的 URL www.host.com:3000/users/root 上使用他的帐户进行游戏。此时,我猜已经为 root 生成了一个会话 ID。如果它现在尝试向 www.host.com:3000/users/admin 发出请求,例如通过手动更改 URL,并且管理员也已连接并从另一台计算机登录,在这种情况下会发生什么情况?
我想这不会被服务器允许,因为请求可能不包含正确的信息,即正确的会话 id?
@nbro 使用 RestController 只需添加 Principal arg like here,Spring 会自动填充它,您可以执行任何所需的安全检查。 (例如,如果 P 是“root”但试图查看“admins”页面会抛出 403 错误,但允许他尝试访问他的页面。)请参阅第 15.3.2.3 here 节了解您可以选择 Spring 将自动为您填充。
O
Ousama

Principal 的简要定义:

Principal 代表用户的身份。

它可以是在简单级别上具有用户名的 String 对象,也可以是复杂的 UserDetails 对象。


P
Panagiotis Bougioukos

Principal 只是 Java SE 6 的一个旧接口。

由于所有没有默认实现的接口,它简单地定义了一些需要由实现该接口的类实现的方法。

这些方法是

boolean  equals(Object another)
          Compares this principal to the specified object.

String   getName()
          Returns the name of this principal.

int      hashCode()
          Returns a hashcode for this principal.

String   toString()
          Returns a string representation of this principal.

正如 Java Doc 所述:

该接口表示主体的抽象概念,可用于表示任何实体,例如个人、公司和登录 ID。

简单来说,它只是用来让实现者必须以一种使实体在其他实体之间唯一区分的方式来实现这个接口。此外,getName() 必须返回一个值,通过该值唯一标识一个特定实体并且不会与其他实体发生冲突。因此,如果使用的 PrincipalUserDetails 类型,则 PrincipalgetName() 返回 UserDetailsUserName

如果我们看到 Spring 用于 AbstractAuthenticationToken.class 的实现:

public String getName() {
        if (this.getPrincipal() instanceof UserDetails) {
            return ((UserDetails)this.getPrincipal()).getUsername();
        } else if (this.getPrincipal() instanceof AuthenticatedPrincipal) {
            return ((AuthenticatedPrincipal)this.getPrincipal()).getName();
        } else if (this.getPrincipal() instanceof Principal) {
            return ((Principal)this.getPrincipal()).getName();
        } else {
            return this.getPrincipal() == null ? "" : this.getPrincipal().toString();
        }
    }

同样重要的是要提到:

abstract class AbstractAuthenticationToken implements Authentication

interface Authentication extends Principal

Principal 接口还确保实现者将实现 equals()hashCode(),这很有意义,因为代表组织或公司或个人的 Principal 实体必须有某种方式与其他实体进行比较。