Java AuthenticationManager.authenticate上未更新Spring安全上下文

Java AuthenticationManager.authenticate上未更新Spring安全上下文,java,spring,spring-boot,authentication,spring-security,Java,Spring,Spring Boot,Authentication,Spring Security,我正在尝试为RESTAPI后端实现一个自定义身份验证过程。 一些实施细节: 我实现了自定义AuthenticationProvider,它成功地对用户进行身份验证并返回身份验证对象。 我注册了全新的AuthenticationManager,这样可以跳过一些spring boot自动配置: 在我的AuthController中,我有/login端点。其逻辑相当简单:它将DTO转换为自定义身份验证对象,并调用AuthenticationManager.authenticatemy\u自定义\u身份

我正在尝试为RESTAPI后端实现一个自定义身份验证过程。 一些实施细节:

我实现了自定义AuthenticationProvider,它成功地对用户进行身份验证并返回身份验证对象。 我注册了全新的AuthenticationManager,这样可以跳过一些spring boot自动配置: 在我的AuthController中,我有/login端点。其逻辑相当简单:它将DTO转换为自定义身份验证对象,并调用AuthenticationManager.authenticatemy\u自定义\u身份验证。 注意:我不会在代码中的任何地方直接操作SecurityContextHolder.getContext

问题是,安全上下文在成功身份验证后不会得到更新,当我尝试在spring侦听器后处理方法中访问SecurityContextHolder.getContext时,我发现它不是我在成功身份验证后从自定义AuthenticationProvider获得的身份验证对象

以下是一些问题:

直接访问SecurityContextHolder.getContext进行编辑是否正常?做这样的事感觉是错误的和令人讨厌的。 我认为AuthenticationManager自己处理安全上下文更新。我的意思是我们调用AuthenticationManager.authenticatemy\u custom\u身份验证,它会在成功身份验证时更新上下文。如果不是这样,我们应该手动保持安全上下文最新,为什么我们甚至需要AuthenticationManager? 如果我有错误,你能告诉我吗? 另外,如果有经典的RESTAPI基于spring的登录和安全性实现,那么最好看看这个,因为要让它正常工作,似乎还有很多事情要做

编辑 这里有一个例子来说明我的问题。

您有两个选择

1-在登录入口点更新SecurityContext

您可以在自定义登录服务中更新SecurityContext。 完成身份验证过程后,请更新SecurityContext

我将这个场景编码如下

2-SecurityContext在CustomAuthenticationProvider中自动更新

当CustomAuthenticationProvider返回UsernamePasswordAuthenticationToken时,SecurityContext将自动更新用户的save principals

我将这个场景编码如下

也可能有用。

您有两个选择

1-在登录入口点更新SecurityContext

您可以在自定义登录服务中更新SecurityContext。 完成身份验证过程后,请更新SecurityContext

我将这个场景编码如下

2-SecurityContext在CustomAuthenticationProvider中自动更新

当CustomAuthenticationProvider返回UsernamePasswordAuthenticationToken时,SecurityContext将自动更新用户的save principals

我将这个场景编码如下


也可能有用。

如果您想跳过Spring Security的自动配置,请添加一个带有@EnableWebSecurity的@Configuration类。不要注册空的AuthenticationManager

为您的自定义提供程序添加一个bean,并通过向AuthenticationManager注册该bean

这将配置AuthenticationManager以使用自定义的AuthenticationProvider。如果您使用的是最新版本的Spring Security,但不确定哪个版本包含此功能,只需配置自定义AuthenticationProvider即可,AuthenticationManagerBuilder将自动检测到该功能。我也看到了

SecurityContext由筛选器链设置,例如通过UsernamePasswordAuthenticationFilter,它将身份验证本身委托给AuthenticationManager

使现代化 项目中的关键部分是登录端点基本上绕过了SpringSecurity中的所有内容。Spring安全性的主要部分是在过滤器中实现的,如果您希望在web应用程序中进行身份验证,而不考虑需要在链中添加过滤器的机制

public CustomAuthenticationFilter extends AbstractAuthenticationProcessingFilter {

  private final ObjectMapper mapper;

  public CustomAuthenticationFilter(ObjectMapper mapper) {
    super(new AntPathRequestMatcher("/auth/login", "POST"));
    this.mapper=mapper;
  }

  public Authentication attemptAuthentication(HttpServletRequest request,
            HttpServletResponse response) throws AuthenticationException, IOException,
            ServletException {

      MyPrincipal principal = mapper.readValue(request.getInputStream(), MyPrincipal.class);
      MyAuthentication authentication = new MyAuthentication(principal);
      setDetails(request, authentication); //assuming you are extending AbstractAuthenticationToken
      return getAuthenticationManager().authenticate(authentication);

   }

    protected void setDetails(HttpServletRequest request,
            MyAuthentication authRequest) {
        authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
    }

}
在您的配置中,将此筛选器添加到Spring Security,并相应地配置筛选器

@Bean
public CustomAuthenticationFilter customAuthenticationFilter(ObjectMapper mapper) {
    CustomAuthenticationFilter filter = new CustomAuthenticationFilter(mapper);
    filter.setAuthenticationManager(authenticationManagerBean());
    return filter;
}

// To prevent registering the filter in the default filter chain!
@Bean
public FilterRegistrationbean customAuthenticationFilterRegistration() {
    FilterRegistrationBean filterReg = new FilterRegistrationBean(customAuthenticationFilter());
    filterReg.setEnabled(false);
    return filterReg;
}

@Override
public void configure(HttpSecurity http) {

    http.addFilterBefore(customerAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
    // other config here
}

现在,您的身份验证是Spring安全部分的一部分

如果要跳过Spring Security的自动配置,请添加带有@EnableWebSecurity的@Configuration类。不要注册空的AuthenticationManager

为您的自定义提供程序添加一个bean,并通过向AuthenticationManager注册该bean

这将配置AuthenticationManager以使用自定义的AuthenticationProvider。如果您使用的是最新版本的Spring Security,但不确定哪个版本包含此功能,只需配置自定义AuthenticationProvider即可,AuthenticationManagerBuilder将自动检测到该功能。我也看到了

SecurityContext由过滤器链设置,例如通过U sernamePasswordAuthenticationFilter,它将身份验证本身委托给AuthenticationManager

使现代化 项目中的关键部分是登录端点基本上绕过了SpringSecurity中的所有内容。Spring安全性的主要部分是在过滤器中实现的,如果您希望在web应用程序中进行身份验证,而不考虑需要在链中添加过滤器的机制

public CustomAuthenticationFilter extends AbstractAuthenticationProcessingFilter {

  private final ObjectMapper mapper;

  public CustomAuthenticationFilter(ObjectMapper mapper) {
    super(new AntPathRequestMatcher("/auth/login", "POST"));
    this.mapper=mapper;
  }

  public Authentication attemptAuthentication(HttpServletRequest request,
            HttpServletResponse response) throws AuthenticationException, IOException,
            ServletException {

      MyPrincipal principal = mapper.readValue(request.getInputStream(), MyPrincipal.class);
      MyAuthentication authentication = new MyAuthentication(principal);
      setDetails(request, authentication); //assuming you are extending AbstractAuthenticationToken
      return getAuthenticationManager().authenticate(authentication);

   }

    protected void setDetails(HttpServletRequest request,
            MyAuthentication authRequest) {
        authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
    }

}
在您的配置中,将此筛选器添加到Spring Security,并相应地配置筛选器

@Bean
public CustomAuthenticationFilter customAuthenticationFilter(ObjectMapper mapper) {
    CustomAuthenticationFilter filter = new CustomAuthenticationFilter(mapper);
    filter.setAuthenticationManager(authenticationManagerBean());
    return filter;
}

// To prevent registering the filter in the default filter chain!
@Bean
public FilterRegistrationbean customAuthenticationFilterRegistration() {
    FilterRegistrationBean filterReg = new FilterRegistrationBean(customAuthenticationFilter());
    filterReg.setEnabled(false);
    return filterReg;
}

@Override
public void configure(HttpSecurity http) {

    http.addFilterBefore(customerAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
    // other config here
}


现在,您的身份验证是Spring安全部分的一部分

你能把你的customAuthenticationProvider代码放在这里吗?@MehrdadHosseinNejad嘿,不幸的是我不能在这里发布它,因为它是私人代码。我向你保证这里面没有什么特别的。它只返回isAuthenticated==true的对象,并填充了principle对象。为什么要创建一个新的空身份验证管理器?只需向现有Authenticationmanager注册自定义AuthenticationProvider。那么它应该会起作用。AuthenticationManager将设置上下文。@M.Deinum Hm,如果您可以共享一段如何执行此操作的代码片段,那就太好了。我尝试了很多解决方案,但没有成功。你能把你的customAuthenticationProvider代码放在这里吗?@MehrdadHosseinNejad嘿,不幸的是,我无法在这里发布,因为它是私人代码。我向你保证这里面没有什么特别的。它只返回isAuthenticated==true的对象,并填充了principle对象。为什么要创建一个新的空身份验证管理器?只需向现有Authenticationmanager注册自定义AuthenticationProvider。那么它应该会起作用。AuthenticationManager将设置上下文。@M.Deinum Hm,如果您可以共享一段如何执行此操作的代码片段,那就太好了。我尝试了很多解决方案,但没有成功。谢谢你的回答。我尝试了这种方法,看起来它正在工作,但手动更新安全上下文感觉非常错误。。。让我们等待更多的人来看看这个。如果没有更好的解决办法,我会接受你的回答。谢谢谢谢,我认为当您手动使用自定义提供程序时,这意味着您必须自己在提供程序中更新安全上下文。这部分是有意义的。但是,根据自定义AuthenticationProvider返回的身份验证对象,AuthenticationManager似乎有足够的信息自己完成这项工作。虽然我可能对这一切的运作方式有错误的理解。似乎是我再次阅读spring文档的时候了:DEdited,当CustomAuthenticationProvider的过程完成并生成UsernamePasswordAuthenticationToken时,SecurityContex会自动更新,因此无需在此级别手动更新SecurityContext谢谢您的回答。我尝试了这种方法,看起来它正在工作,但手动更新安全上下文感觉非常错误。。。让我们等待更多的人来看看这个。如果没有更好的解决办法,我会接受你的回答。谢谢谢谢,我认为当您手动使用自定义提供程序时,这意味着您必须自己在提供程序中更新安全上下文。这部分是有意义的。但是,根据自定义AuthenticationProvider返回的身份验证对象,AuthenticationManager似乎有足够的信息自己完成这项工作。虽然我可能对这一切的运作方式有错误的理解。似乎是我再次阅读spring文档的时候了:DEdited,当CustomAuthenticationProvider的过程完成并生成UsernamePasswordAuthenticationToken时,SecurityContex会自动更新,因此,不需要在此级别手动更新SecurityContext非常感谢您的响应,但它似乎无法以这种方式工作。由于开发者被迫从spring boot 2.0.0开始提供自己的AuthenticationManager。我是这样做的,但在我的提供者完成其工作后,安全上下文仍然没有更新。你真的尝试过吗?这仅与使用自动配置时有关。由于@EnableWebSecurity,因此未使用此选项。因此,正常的Spring安全配置选项适用于链接的Spring安全文档中提到的。如果你能知道这有什么问题,那就太好了。如果过滤器处理登录,你不应该继续处理,只需返回状态200或你想做的任何事情。不要仅仅因为需要而创建其他端点,让过滤器完成所有这些。您可以通过添加一个AuthenticationSuccessHandler来实现这一点。这也会起作用,但默认的successfulAuthentication做的更多,而不仅仅是调用成功处理程序触发事件,如果配置了,请记住我,等等。非常感谢您的响应,但它似乎不起作用。由于开发者被迫从spring boot 2.0.0开始提供自己的AuthenticationManager。我是这样做的,如本线程所示,但安全上下文在我发布后仍然没有更新
供应商做了它的工作。你真的试过了吗?这仅与使用自动配置时有关。由于@EnableWebSecurity,因此未使用此选项。因此,正常的Spring安全配置选项适用于链接的Spring安全文档中提到的。如果你能知道这有什么问题,那就太好了。如果过滤器处理登录,你不应该继续处理,只需返回状态200或你想做的任何事情。不要仅仅因为需要而创建其他端点,让过滤器完成所有这些。您可以通过添加一个AuthenticationSuccessHandler来实现这一点。这也会起作用,但默认的successfulAuthentication做的更多,而不仅仅是调用成功处理程序触发事件,如果配置了,请记住我,等等。,
public CustomAuthenticationFilter extends AbstractAuthenticationProcessingFilter {

  private final ObjectMapper mapper;

  public CustomAuthenticationFilter(ObjectMapper mapper) {
    super(new AntPathRequestMatcher("/auth/login", "POST"));
    this.mapper=mapper;
  }

  public Authentication attemptAuthentication(HttpServletRequest request,
            HttpServletResponse response) throws AuthenticationException, IOException,
            ServletException {

      MyPrincipal principal = mapper.readValue(request.getInputStream(), MyPrincipal.class);
      MyAuthentication authentication = new MyAuthentication(principal);
      setDetails(request, authentication); //assuming you are extending AbstractAuthenticationToken
      return getAuthenticationManager().authenticate(authentication);

   }

    protected void setDetails(HttpServletRequest request,
            MyAuthentication authRequest) {
        authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
    }

}
@Bean
public CustomAuthenticationFilter customAuthenticationFilter(ObjectMapper mapper) {
    CustomAuthenticationFilter filter = new CustomAuthenticationFilter(mapper);
    filter.setAuthenticationManager(authenticationManagerBean());
    return filter;
}

// To prevent registering the filter in the default filter chain!
@Bean
public FilterRegistrationbean customAuthenticationFilterRegistration() {
    FilterRegistrationBean filterReg = new FilterRegistrationBean(customAuthenticationFilter());
    filterReg.setEnabled(false);
    return filterReg;
}

@Override
public void configure(HttpSecurity http) {

    http.addFilterBefore(customerAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
    // other config here
}