【问题标题】:Spring Boot/Security - can I use X509 Certificate as an extra layer in authentication?Spring Boot/Security - 我可以使用 X509 证书作为身份验证的额外层吗?
【发布时间】:2021-05-04 18:31:30
【问题描述】:

我正在构建一个 Android 应用程序,它与受 Spring Security 保护的 REST API 进行通信。

由于 Android 应用程序是“公共的”并且没有密钥等是安全的,我想制造不同的障碍并使事情变得复杂以尽可能保护我的 API。

我想增加更多安全性的一种方法是确保调用我的 API 的人拥有证书。我不想在我的 API 信任库中创建数千个证书,所以我只想确保调用者拥有一个我隐藏在我的 Android 应用程序的密钥库中的证书。

在示例中,我发现 Spring Security 中的“普通”X509Certificate 身份验证似乎需要为每个用户提供唯一的证书,然后此证书替换 Basic auth 或 JWT auth。我想拥有单独的客户端 JWT 令牌,但要确保每次调用都会带来我的一个 Android 应用证书,以确保(更多)有人从我的 Android 应用调用我的 API。

这是可能的,还是不是它的用途?

当您创建 RestTemplate 时,您可以使用密钥库和信任库对其进行配置,因此它应该很容易。但至于保护我的 REST API 似乎更困难,因为我想要证书 + JWT 令牌或基本身份验证。

我没有为我的 securityconfig 使用 XML 配置。我改为扩展 WebSecurityConfigurerAdapter。如果这可以在 configure(HttpSecurity http) 方法中进行配置,那就太好了,但我想也许我可以在 OncePerRequestFilter 中以某种方式实现这一点?也许在我的 JwtAuthFilter 之前配置一个过滤器?

编辑: 在我发现的所有配置spring security的示例中,它们似乎总是使用证书作为身份验证。我只想进行配置,以便当有人调用 example.com/api/** 时,它会检查证书是否得到了我的自定义信任库的批准(这样我“知道”它可能是来自我的应用程序的调用)但是如果有人调用 example.com/website 它应该使用默认的 java 信任库。

如果有人调用 example.com/api/** 我希望我的服务器能够

  1. 如果证书在我的自定义信任库中未获批准,请检查证书并终止连接。
  2. 如果证书正常,则使用 Basic-/JWT-authentication 向用户身份验证建立 https(或者如果我在建立 https-connection 之前无法终止连接,则继续)。

【问题讨论】:

  • 如果您想问更多问题,请启动其他主题。这是一个关于证书的问题,堆栈溢出不是一个讨论论坛。
  • 我只是想弄清楚我的目的。您一直在谈论其他事情并声称不需要安全措施,因为该应用程序是公开的。那是不正确的
  • 你声称我所说的is ofc not true 将结束我们的讨论。祝你今天过得愉快。祝你好运。
  • 我的意思是,每扇门都可以关掉,但这并不意味着你离开家时不应该锁门。对我来说,你似乎建议我最好让门敞开,因为如果花费足够的时间和精力,他们无论如何都可以打破门。你的回答听起来有点粗鲁,但我仍然很感激你的意见,尽管大部分内容(在你删除之前)有点离题
  • 祝你有美好的一天,祝你好运

标签: android spring spring-security resttemplate spring-security-rest


【解决方案1】:

我想我明白了。这是我的配置方式,它似乎可以工作。

"/**" 端点是可以在没有任何特定证书的任何浏览器上工作的网站,但它需要管理员权限(您需要以管理员身份登录)。

"/api/**""/connect/**" 端点需要正确的证书、正确的 API 密钥和有效的基本或 JWT 令牌身份验证。

@Override
protected void configure(HttpSecurity http) throws Exception {
    
    http.authorizeRequests()
            .antMatchers("/**").hasRole("ADMIN")
        .and()
            .formLogin()
                .loginPage("/loginForm")
                .loginProcessingUrl("/authenticateTheUser")
                .permitAll()
        .and()
            .logout()
                .permitAll().and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.ALWAYS);
    
    http.requestMatchers()
            .antMatchers("/connect/**","/api/**")
        .and()
            .addFilterBefore(new APIKeyFilter(null), UsernamePasswordAuthenticationFilter.class)
            .addFilterBefore(new JwtAuthorizationFilter(), BasicAuthenticationFilter.class)
            .csrf().disable()
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
        .and()
            .httpBasic()
            .authenticationEntryPoint(authenticationEntryPoint)
        .and()
            .authorizeRequests()
            .antMatchers("/connect/**").hasAnyRole("MASTER,APPCALLER,NEGOTIATOR,MEMBER")
            .antMatchers("/api/**").hasAnyRole("MASTER,MEMBER,ANONYMOUS");
    
}

ApiKeyFilter 类是检查 api-key 并确保调用中使用的证书在我的服务器信任库中获得批准的类。 api-key 检查是我必须配置的全部,扩展的 X509AuthenticationFilter 将自动检查请求证书。我的 ApiKeyFilter 如下所示:

    public class APIKeyFilter extends X509AuthenticationFilter {
    
    private String principalRequestHeader = "x-api-key";
    
    private String apiKey = "XXXX";
    
    public APIKeyFilter(String principalRequestHeader) {
        if (principalRequestHeader != null) {
            this.principalRequestHeader = principalRequestHeader;
        }
        setAuthenticationManager(new AuthenticationManager() {
            @Override
            public Authentication authenticate(Authentication authentication) throws AuthenticationException {
                if(authentication.getPrincipal() == null) {
                    throw new BadCredentialsException("Access Denied.");
                }
                String rApiKey = (String) authentication.getPrincipal();
                if (authentication.getPrincipal() != null && apiKey.equals(rApiKey)) {
                    return authentication;
                } else {
                    throw new BadCredentialsException("Access Denied.");
                }
            }
        });
    }
    
    @Override
    protected Object getPreAuthenticatedPrincipal(HttpServletRequest request) {
        return request.getHeader(principalRequestHeader);
    }
    
    @Override
    protected Object getPreAuthenticatedCredentials(HttpServletRequest request) {
        X509Certificate[] certificates = (X509Certificate[]) request.getAttribute("javax.servlet.request.X509Certificate");
        if (certificates != null && certificates.length > 0) {
            return certificates[0].getSubjectDN();
        }
        return super.getPreAuthenticatedCredentials(request);
    } 
}

Cred 访问了这些帮助我整理内容的资源:

Spring Boot - require api key AND x509, but not for all endpoints

spring security http antMatcher with multiple paths

【讨论】:

    猜你喜欢
    • 2015-11-22
    • 2011-04-24
    • 1970-01-01
    • 2020-07-02
    • 2019-01-03
    • 2020-12-05
    • 2022-08-16
    • 2011-08-21
    • 2016-03-26
    相关资源
    最近更新 更多