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

MvC中Spring安全中每个路径/路由的不同AuthenticationManager

陈增
2023-03-14

由于在 StackOverflow 上已经有很多关于这个问题的问题,我首先要确保这不是重复和区分。

这是关于

    在 2 个不同的身份验证
  • 管理器中具有 2(或更多)不同的身份验证提供程序,以用于不同的路由。
  • 使用 Spring Security 5.5 而不是 3.x 中的方法
  • 使用基于非 XML 配置的方法

所以问题不在于:

  • 如何在身份验证管理器中包含多个身份验证以提供“替代身份验证”(大多数问题往往是这样)

假设有2个自定义authenticationprovider:CATApiTokenProvider和DOGApiTokenProvider。根据设计,我们不谈论auth/JWT/Basic/Form提供者,因为它们提供快捷方式。

现在我们有两个REST APIendpoint< code>/dog/endpoint和< code>/cat/endpoint。

今天如何使用Spring Security 5.5正确实现这一点:

  • 我们希望身份验证提供者CATApiTokenProvider只能对/cat/endpoint
  • 上的请求进行身份验证
  • 我们希望身份验证提供者DOGApiTokenProvider只能对/dog/endpoint上的请求进行身份验证

因此,不能使用/dog/endpoint上的cat令牌进行身份验证,也不能使用/cat/endpoint上的dog令牌进行身份认证。

我的想法/方法

a) 我理解,因为我有自定义的猫/狗过滤器,所以在创建bean时可以使用AuthenticationManagerResolver并将一个实例传递到过滤器中。这个解析器可能看起来像

  public AuthenticationManagerResolver<HttpServletRequest> resolver()
  {
    return request -> {
      if (request.getPathInfo().startsWith("/dog/")) {
        try {
          return ???;
        } catch (Exception exception) {
          log.error(exception);
        }
      }
      if (request.getPathInfo().startsWith("/cat/")) {
        try {
          return ???;
        } catch (Exception exception) {
          log.error(exception);
        }
      }
    };
  }

其中两个问题是:

  • 如何在这里返回不同的身份验证管理器?如何用每个CatAP和DogAP实例化2个不同的AM?目前我使用公共空格配置(身份验证管理器生成器身份验证),但据我所知,我只会配置“一个”身份验证管理器,我可以在那里添加DogAP和CatAP,但这会让1个AM有2个AP,所以当使用此AM时,我可以在catendpoint上使用狗令牌进行身份验证
  • 这真的是实现这一点的正确方法吗?我本来期望能够在SecurityConfigation级别上提供AM

b) 以某种方式实例化 2 个不同的身份验证管理器,然后使用安全配置将它们分配给不同的匹配器。

两个问题:

    < li >通过不同的供应商产生两种不同的am的正确方法是什么? < li >我不明白如何为规格添加AM
http.authorizeRequests()
      .antMatchers("/dog/**")
      .?

共有1个答案

闻人高卓
2023-03-14

您可以发布多个筛选器链,也可以使用身份验证管理器解析程序连接自己的身份验证筛选器

您可以使用< code > AuthenticationManagerResolver 返回不同的< code > AuthenticationManager 。从Spring Security 5.4.0开始,我们不再需要扩展< code > WebSecurityConfigurerAdapter 来配置我们的< code>SecurityFilterChain,您可以定义一个< code>SecurityFilterChain类型的bean。

我将详细介绍如何连接您自己的身份验证过滤器

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain apiSecurity(HttpSecurity http) throws Exception {
        http.authorizeHttpRequests((authz) -> authz
                .anyRequest().authenticated());
        http.addFilterBefore(apiAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
        return http.build();
    }

    private AuthenticationFilter apiAuthenticationFilter() {
        AuthenticationFilter authenticationFilter = new AuthenticationFilter(new ApiAuthenticationManagerResolver(), new BasicAuthenticationConverter());
        authenticationFilter.setSuccessHandler((request, response, authentication) -> {});
        return authenticationFilter;
    }

    public static class ApiAuthenticationManagerResolver implements AuthenticationManagerResolver<HttpServletRequest> {

        private final Map<RequestMatcher, AuthenticationManager> managers = Map.of(
                new AntPathRequestMatcher("/dog/**"), new DogAuthenticationProvider()::authenticate,
                new AntPathRequestMatcher("/cat/**"), new CatAuthenticationProvider()::authenticate
        );

        @Override
        public AuthenticationManager resolve(HttpServletRequest request) {
            for (Map.Entry<RequestMatcher, AuthenticationManager> entry : managers.entrySet()) {
                if (entry.getKey().matches(request)) {
                    return entry.getValue();
                }
            }
            throw new IllegalArgumentException("Unable to resolve AuthenticationManager");
        }
    }

    public static class DogAuthenticationProvider implements AuthenticationProvider {

        @Override
        public Authentication authenticate(Authentication authentication) throws AuthenticationException {
            if (authentication.getName().endsWith("_dog")) {
                return new UsernamePasswordAuthenticationToken(authentication.getName(), authentication.getCredentials(),
                        AuthorityUtils.createAuthorityList("ROLE_DOG"));
            }
            throw new BadCredentialsException("Username should end with _dog");
        }

        @Override
        public boolean supports(Class<?> authentication) {
            return true;
        }

    }

    public static class CatAuthenticationProvider implements AuthenticationProvider {

        @Override
        public Authentication authenticate(Authentication authentication) throws AuthenticationException {
            if (authentication.getName().endsWith("_cat")) {
                return new UsernamePasswordAuthenticationToken(authentication.getName(), authentication.getCredentials(),
                        AuthorityUtils.createAuthorityList("ROLE_CAT"));
            }
            throw new BadCredentialsException("Username should end with _cat");
        }

        @Override
        public boolean supports(Class<?> authentication) {
            return true;
        }

    }
}

在上面的示例中,我们有两个身份验证提供者,一个用于猫,另一个用于狗。它们是通过AntPath请求匹配器解析的,该匹配器用于/狗/**/cat/*endpoint,在ApiAuth

然后,将ApiAut

您还可以为每个endpoint定义两个不同的过滤器链,如下所示:

@Bean
public SecurityFilterChain dogApiSecurity(HttpSecurity http) throws Exception {
    http.requestMatchers((matchers) -> matchers
                .antMatchers("/dog/**"));
    http.authorizeRequests((authz) -> authz
            .anyRequest().authenticated());
    http.httpBasic();
    http.authenticationProvider(new DogAuthenticationProvider());
    return http.build();
}

@Bean
public SecurityFilterChain catApiSecurity(HttpSecurity http) throws Exception {
    http.requestMatchers((matchers) -> matchers
                .antMatchers("/cat/**"));
    http.authorizeRequests((authz) -> authz
            .anyRequest().authenticated());
    http.httpBasic();
    http.authenticationProvider(new CatAuthenticationProvider());
    return http.build();
}

在定义多个过滤器链时,排序很重要,请在这些场景中使用@Order注释。

当你执行http.requestMatcher(new AntPathRequestMatcher(“/endpoint/**”))时;你告诉Spring Security只在请求与该路径匹配时才调用过滤器链。

Spring Security的存储库中还有一个票证,用于提供接受Map的AuthenticationManagerResolver实现。

 类似资料:
  • 我正在尝试使用http://localhost:3000/login?Id=1这样的查询参数来实现React路由器,我只能在我的登录路由中实现它,如果我将路径作为http://localhost:3000/然后重定向,但是,我想在整个应用程序中实现。如果我在其他路由上实现,它会匹配路由。这就是我的的样子,有人能指导我如何实现包括查询参数在内的所有路由路径吗?

  • 我有一个XML文件,如下所示 我的XSL看起来像这样 然后,我尝试达到节点内元素的每个值 它没有显示值!我认为这是一种正确的方式。首先,我有for each select=“//警报,然后我有<代码> 请帮忙。

  • 我正在尝试在我的URL中传递参数,但我的阅读有问题。我正在使用react-router v4。 网址:http://localhost:3000/reset?token=123我试着这样读: 但这打印空对象。奇怪的是,我试图将问号更改为其他字符,看起来它解决了我的问题,但我还是想保留问号。 网址:http://localhost:3000/reset_token=123 这已经工作了< code

  • 在以下React应用程序中,有两个路由URLhttp://myapp 正确布线到布局构件。但是,URLhttp://myapp/login 也路由到布局组件,而不是登录。如果我将path=“/login”更改为“/sign”,它将正确路由到登录组件。 React路由器中的“/login”路径将其路由到路由是否有特殊之处?或者我设置这个路由的方式有错误吗?

  • 问题内容: 我正在尝试使用单独的路线,但在我的React App中添加/编辑表单的组件相同,如下所示: 现在,在manageClient组件中,我解析查询参数(我在编辑路由中传递带有客户端ID的查询字符串),并根据传递的查询参数有条件地进行渲染。 问题在于这不会再次重新安装整个组件。假设打开了一个编辑页面,并且用户单击添加组件,URL发生了更改,但是该组件没有重新加载,因此保留在编辑页面上。 有办