【问题标题】:Spring Security: Why Authentication is extending Principal?Spring Security:为什么 Authentication 正在扩展 Principal?
【发布时间】:2013-07-18 15:09:55
【问题描述】:

Spring Security 假设 Authentication 是一个 Principal。

public interface Authentication extends Principal, Serializable {}

HttpServletRequest 有getUserPrincipal 方法,负责访问主体对象。

让我们考虑一下这种情况:

public interface RealPrincipal extends Principal {
   public Integer getId();
}

Common Module A 具有 Real Principal 接口和实现。

Module A 使用 Common Module A,Servlet Api,不依赖 Spring Security:

Module B 使用 Common Module A、Servlet Api 并配置 Spring Security。该模块负责安全性和 UserDetails 的实现。

Web A 使用模块 A 和模块 B。

为了使用请求方法,我最终得到了这样一个实现:

public ModelAndView someRequestHandler(Principal principal) {
   User activeUser = (User) ((Authentication) principal).getPrincipal();
   ...
}

这迫使我对模块 A 和其他模块依赖 Spring Security。我相信一个适当的 servlet api 抽象不应该依赖于 spring 安全性。 request.getUserPrincipal 应该返回真正的委托人。

请解释为什么 org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestWrapper 返回

身份验证而不是Real Principal

编辑:我已将通用模块 A 添加到我的场景中,并更新了模块 B 负责安全性。

【问题讨论】:

    标签: spring spring-security


    【解决方案1】:

    正如 Luke 所说,Spring Security 对 Principal 使用 Authentication,因为它实现了 Principal。它不使用 Authentication#getPrincipal() 因为它不能保证是一个主体(它是一个对象)。事实上,在大多数情况下,Spring Security 的 Authentication#getPrincipal() 会返回一个 User(不实现 Principal)、框架用户提供的自定义 UserDetails 或 String。

    如果您希望 Spring Security 处理此问题,您可能需要按照 Luke 的建议使用 HttpServletRequestWrapper 来实现此逻辑。例如,您可以执行以下操作:

    public RealPrincipalFilter extends OncePerRequestFilter {
    
        public void doFiter(HttpServletRequest request, HttpServletResponse response, FilterChain) {
            chain.doFilter(new RealPrincipalRequestWrapper(request), response);
        }
    
        private static final class RealPrincipalRequestWrapper 
              extends HttpServletRequestWrapper {
            public Principal getUserPrincipal() {
                Authentication auth = (Authentication) super.getPrincipal();
                return auth == null ? null : (RealPrincipal) auth.getPrincipal()
            }
        }
    }
    
    @Configuration
    @EnableWebSecurity
    public WebSecurityConfig extends WebSecurityConfigurerAdapter {
        public configure(HttpSecurity http) {
            http
                // ... other config ...
                .addFilterAfter(new RealPrincipalFilter(), SecurityContextHolderAwareRequestFilter.class);
        }
        ...
    }
    

    或者,请查看我对您的其他问题的回答,了解与 Spring MVC 集成的选项 - Injecting Custom Principal to Controllers by Spring Security

    【讨论】:

    • 感谢 Rob 的精彩回答。现在我明白了 Spring Security 不仅与 Web 应用程序有关,而且与独立应用程序有关。这意味着用户不需要是委托人。并且为了符合 Http Api,Authentication 实现了 Principal 接口。到目前为止一切顺利:) 这是我的问题:如果 Spring Security 自定义用户已经实现了 Principal 接口怎么办?这导致了这样一个我认为完美的实现:gist.github.com/cemo/6041868 你觉得呢?
    • 您建议的更改将是非被动的,因此充其量可以包含在 4.x 版本中。我不认为这是大多数人都需要的用例,所以我认为它也不会包含在 4.x 中。这并不是说你没有充分的理由去做你正在做的事情,而是说作为一个框架,我们需要尝试瞄准 90% 的用例,并为其他用例提供挂钩。
    【解决方案2】:

    简短的回答是AuthenticationPrincipal,因此它可以在需要一个的API(例如您提到的servlet API 方法)中使用。

    这在实践中意味着什么?不是很多。 Java 的Principal 接口只有一个方法getName,所以如果你想做的不仅仅是渲染用户名,你需要了解更多的实现。

    当您使用短语“real principal”和“proper servlet api abstraction”时,您可能应该考虑一下您的意思。例如,如果主体是“真实的”主体,您希望如何实现您的 someRequestHandler 方法?

    【讨论】:

    • 将 RealPrincipal 视为主体的子类。它具有所有必要的方法,例如用户名、电子邮件、密码等……我可以依赖它。没问题。但是依赖 Spring Security 并不是一个可行的解决方案。还有另一个问题。 Spring MVC 正在使用 request.getUserPrincipal 注入 Principal。如果我将 RealPrincipal 声明为控制器方法参数,则它不能注入。因为 Authentication 不是 RealPrincipal 而是 Principal。这会导致错误。
    • 另外请看我的另一个问题stackoverflow.com/questions/17741787/…
    • RealPrincipal 是什么?您希望它来自哪里?即谁提供它,您如何依赖它?
    • 我添加了 RealPrincipal 接口。它来自另一个模块,如 servlet-api。模块 B 提供了必要的实施。顺便说一句,这不是虚构的场景。我的情况和我上面写的完全一样。
    • 如果你想从getUserPrincipal方法返回RealPrincipal,那么最好的选择可能是在spring安全过滤器链之后添加一个你自己的单独过滤器,它使用HttpServletRequestWrapper来自定义方法。您还可以禁用内置包装器。
    猜你喜欢
    • 2019-01-17
    • 2021-10-05
    • 2014-03-04
    • 2017-11-21
    • 2015-08-27
    • 2020-02-07
    • 2020-05-31
    • 2020-12-02
    • 2018-09-30
    相关资源
    最近更新 更多