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

如何为spring boot RESTful web服务配置多级身份验证?

许俊雅
2023-03-14

我们正在使用Spring Boot构建一个RESTful webservice。我们希望有两级身份验证来保护endpoint。

首先,对于每个请求,我们希望检查请求头内部是否有指定的apiKey,如果没有,我们将拒绝该请求。如果请求有一个apiKey,我们将对某些请求使用用户名/密码登录进行下一个身份验证。有只需要apiKey身份验证的公共endpoint,和首先需要apiKey身份验证,然后需要用户名/密码身份验证才能访问它们的私有endpoint。

对于apiKey身份验证,我在这里复制了代码,我还可以找到许多关于用户名/密码身份验证的示例。

我的问题是:如何在WebSecurityConfigurerAdapter内部做Java配置,将它们结合在一起。

现在,我为这两个身份验证过滤器定义了扩展WebSecurityConfigurerAdapter的两个配置类,但是请求将只通过其中一个,这取决于我将哪个设置为@order(1)。

多谢了。

共有1个答案

孙嘉
2023-03-14

整个答案由一个运行中的Spring Boot应用程序支持,并通过单元测试来确认。

如果你觉得这个答案有帮助,请投票。

简单的回答是,您的安全配置可能如下所示

    http
        .sessionManagement()
            .disable()
        //application security
        .authorizeRequests()
            .anyRequest().hasAuthority("API_KEY")
            .and()
        .addFilterBefore(new ApiKeyFilter(), HeaderWriterFilter.class)
        .addFilterAfter(new UserCredentialsFilter(), ApiKeyFilter.class)
        .csrf().ignoringAntMatchers(
            "/api-key-only",
            "/dual-auth"
    )
        ;
        // @formatter:on
    }

}

>

  • 我使用WebSecurityConfigurerAdapter来满足ApiKey要求

    .authorizeRequests()
        .anyRequest().hasAuthority("API_KEY")
    

    我通过启用它来使用方法级安全性

    @EnableGlobalMethodSecurity(prePostEnabled=true)

        @PreAuthorize("hasAuthority('USER_CREDENTIALS')")
        public String twoLayersOfAuth() {
            //only logic here
        }
    

    ApiKey过滤器非常简单

    public class ApiKeyFilter extends OncePerRequestFilter {
        @Override
        protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
    
            final String authorization = request.getHeader("Authorization");
            final String prefix = "ApiKey ";
            if (hasText(authorization) && authorization.startsWith(prefix)) {
                String key = authorization.substring(prefix.length());
                if ("this-is-a-valid-key".equals(key)) {
                    RestAuthentication<SimpleGrantedAuthority> authentication = new RestAuthentication<>(
                        key,
                        Collections.singletonList(new SimpleGrantedAuthority("API_KEY"))
                    );
                    SecurityContextHolder.getContext().setAuthentication(authentication);
                }
            }
            filterChain.doFilter(request, response);
    
        }
    }
    

    第二层身份验证甚至很简单(它依赖于第一层的执行)

    public class UserCredentialsFilter extends OncePerRequestFilter {
        @Override
        protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
            final String userCredentials = request.getHeader("X-User-Credentials");
            Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
            if ("valid-user".equals(userCredentials) && authentication instanceof RestAuthentication) {
                RestAuthentication<SimpleGrantedAuthority> restAuthentication =
                    (RestAuthentication<SimpleGrantedAuthority>)authentication;
                restAuthentication.addAuthority(new SimpleGrantedAuthority("USER_CREDENTIALS"));
            }
            filterChain.doFilter(request, response);
    
        }
    }
    

    请注意:每个筛选器如何并不关心在没有身份验证或身份验证不足的情况下会发生什么。这些都帮你处理好了。筛选器只需验证正确的数据;

        mvc.perform(
            post("/api-key-only")
                .header("Authorization", "ApiKey this-is-a-valid-key")
                .header("X-User-Credentials", "valid-user")
        )
            .andExpect(status().isOk())
            .andExpect(authenticated()
                .withAuthorities(
                    asList(
                        new SimpleGrantedAuthority("API_KEY"),
                        new SimpleGrantedAuthority("USER_CREDENTIALS")
                    )
                )
            )
            .andExpect(content().string("API KEY ONLY"))
        ;
    
        mvc.perform(
            post("/dual-auth")
                .header("Authorization", "ApiKey this-is-a-valid-key")
        )
            .andExpect(status().is4xxClientError())
            .andExpect(authenticated()
                .withAuthorities(
                    asList(
                        new SimpleGrantedAuthority("API_KEY")
                    )
                )
            )
        ;
    

    当然,我们总有一条幸福的路

        mvc.perform(
            post("/dual-auth")
                .header("Authorization", "ApiKey this-is-a-valid-key")
                .header("X-User-Credentials", "valid-user")
        )
            .andExpect(status().isOk())
            .andExpect(content().string("DUAL AUTH"))
            .andExpect(authenticated()
                .withAuthorities(
                    asList(
                        new SimpleGrantedAuthority("API_KEY"),
                        new SimpleGrantedAuthority("USER_CREDENTIALS")
                    )
                )
            )
        ;
    

  •  类似资料:
    • 将配置服务器用户名和密码存储为环境变量(在客户端和服务器中)还是使用密钥库更好?密钥库密码无论如何都存储为环境变量,那么为什么实际使用密钥库呢?还是有更好的方法在SpringCloudConfig服务器中实现身份验证?

    • 我有一个需要以两种不同方式保护的API: 1) 对除1以外的所有请求URL使用JWT,该URL需要使用基本身份验证进行保护 2) 一个url的基本身份验证。 我已经为JWT和Basic Auth设置了安全配置。我的问题是,当我使用有效的用户名和密码请求基本的经过身份验证的URL时,它会成功地对我进行身份验证,并将数据存储在cassandra中。 然后,我希望必须通过/api/login为所有其他请

    • 我试图扩展用户身份验证示例,这里也给出了这个示例,以便多个用户可以登录到服务器。我还想为每个用户分配一个不同的主目录。到目前为止,我还没有找到Apache SSHD API提供的任何此类实用程序,因此我尝试了以下解决方案,使用Apache FTPServer提供的实用程序。 我试图做的是:

    • 我试图让我们的所有组织用户使用他们的LDAP域凭据登录到Jenkins。使用下面的选项,只有1个特定OU下的用户才能登录。我想提供多个OU进行搜索。 我们的Active Directory结构如下: ca->美国->用户->实际用户id ca->印度->用户->实际用户id 目前,只有属于美国OU的用户才能成功登录到该应用程序。我希望来自美国和印度的用户能够成功登录。我认为将用户搜索库和组搜索库都

    • 在斯威格中有一个.BasicAuth(“基本”)。我取消注释的描述(“基本HTTP授权”)行。 但是,当尝试使用其中一个endpoint时,单击“试用!”按钮时,系统会提示我登录。无论我在这个框中放了什么(用户名和密码框),我都无法运行或测试endpoint,因为我未经授权。我需要在 SwaggerConfig.cs 文件或其他位置执行哪些操作才能进行测试? 我使用的是.Net Framework

    • 我正在尝试配置一个具有多种身份验证机制(DB和LDAP)并使用Spring Security性作为其底层框架的应用程序。我正在使用java配置来设置Web和http安全性。我知道我们需要多个WebSecurityConfigurerAdapter实例来存储多个http元素(如基于xml的config中使用的);但是当我这样做时,应用程序只选择配置的第一个身份验证(数据库身份验证),并且从不使用第二