Java 使用 spring boot 实现 2 路 SSL

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/33808603/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 15:03:51  来源:igfitidea点击:

Implementing 2 way SSL using spring boot

javasslspring-securityspring-boottomcat8

提问by Andrew Mc

I'm creating some restful web services and am using Spring-Boot to create an embedded tomcat container.

我正在创建一些宁静的 Web 服务,并且正在使用 Spring-Boot 创建一个嵌入式 tomcat 容器。

One of the requirements is that this implements 2 way SSL. I've been looking at the HttpSecurity object and can get it to only run the webservices over an SSL channel using this:-

要求之一是这实现了 2 路 SSL。我一直在查看 HttpSecurity 对象,并且可以使用以下方法让它仅通过 SSL 通道运行 web 服务:-

@Override
protected void configure(HttpSecurity http) throws Exception {

    System.out.println("CONFIGURED");

    http
        // ...
        .requiresChannel()
            .anyRequest().requiresSecure();
}

What I can't seem to find is a way of making the webservice only accessible to applications providing a valid client cert.

我似乎找不到一种方法,可以让提供有效客户端证书的应用程序访问 web 服务。

I have only a basic knowledge of SSL so even a general pointer in the right direction would be appreciated.

我只有 SSL 的基本知识,因此即使是正确方向的一般指针也将不胜感激。

The server this is being deployed onto will have a mix of applications - this is the only one that needs to be locked down with 2-way SSL. What I'm really looking for is a way of locking down a single application to only accept client certificates.

正在部署的服务器将包含多种应用程序 - 这是唯一需要使用 2-way SSL 锁定的应用程序。我真正在寻找的是一种将单个应用程序锁定为仅接受客户端证书的方法。

采纳答案by dur

You could configure clientAuth=want, see Apache Tomcat 8 Configuration Reference:

您可以配置clientAuth=want,请参阅Apache Tomcat 8 配置参考

Set to trueif you want the SSL stack to require a valid certificate chain from the client before accepting a connection. Set to wantif you want the SSL stack to request a client Certificate, but not fail if one isn't presented. A falsevalue (which is the default) will not require a certificate chain unless the client requests a resource protected by a security constraint that uses CLIENT-CERTauthentication.

设置为true,如果你想在SSL栈需要从客户端的有效证书链接受连接之前。want如果您希望 SSL 堆栈请求客户端证书,则设置为,但如果未提供,则不会失败。一false,除非客户端请求由安全限制使用保护的资源值(默认值)将不需要证书链CLIENT-CERT认证。

and then read the client certificate with Spring Security - X.509 Authentication:

然后使用Spring Security - X.509 Authentication读取客户端证书:

You can also use SSL with "mutual authentication"; the server will then request a valid certificate from the client as part of the SSL handshake. The server will authenticate the client by checking that its certificate is signed by an acceptable authority. If a valid certificate has been provided, it can be obtained through the servlet API in an application. Spring Security X.509 module extracts the certificate using a filter. It maps the certificate to an application user and loads that user's set of granted authorities for use with the standard Spring Security infrastructure.

您还可以将 SSL 与“相互身份验证”结合使用;然后,服务器将作为 SSL 握手的一部分从客户端请求有效证书。服务器将通过检查其证书是否由可接受的授权机构签署来验证客户端。如果已提供有效证书,则可以通过应用程序中的 servlet API 获取。Spring Security X.509 模块使用过滤器提取证书。它将证书映射到应用程序用户并加载该用户的一组授予权限以与标准 Spring Security 基础设施一起使用。

and

clientAuthcan also be set to wantif you still want SSL connections to succeed even if the client doesn't provide a certificate. Clients which don't present a certificate won't be able to access any objects secured by Spring Security unless you use a non-X.509 authentication mechanism, such as form authentication.

clientAuthwant即使客户端不提供证书,如果您仍然希望 SSL 连接成功,也可以设置为。不提供证书的客户端将无法访问 Spring Security 保护的任何对象,除非您使用非 X.509 身份验证机制,例如表单身份验证。

回答by veebee

I came across a similar problem, and thought I'd share the solution I came with.

我遇到了类似的问题,并认为我会分享我带来的解决方案。

First, you need to understand that the SSL certificate authentication will be handled on your web server's side (cfr. dur's explanation, with the “clientAuth=want” setting). Then, your web app must be configured in order to handle the provided (and allowed) certificate, map it to a user etc.

首先,您需要了解 SSL 证书身份验证将在您的 Web 服务器端处理(参见 dur 的解释,使用“clientAuth=want”设置)。然后,必须配置您的 Web 应用程序以处理提供的(和允许的)证书,将其映射到用户等。

The slight difference I have with you is that I'm packaging my spring boot application into a WAR archive, which is then deployed on an existing Tomcat application server.

我与您之间的细微差别是我将 Spring Boot 应用程序打包到一个 WAR 存档中,然后将其部署在现有的 Tomcat 应用程序服务器上。

My Tomcat's server.xml configuration file defines an HTTPS connector as follows:

我的 Tomcat 的 server.xml 配置文件定义了一个 HTTPS 连接器,如下所示:

<Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol"
    maxThreads="150" SSLEnabled="true" scheme="https" secure="true"
    keystoreFile="/opt/tomcat/conf/key-stores/ssl-keystore.jks"
    keystorePass=“some-complex-password“
    clientAuth="want" sslProtocol="TLS"
    truststoreFile="/opt/tomcat/conf/trust-stores/ssl-truststore.jks"
    truststorePass=“some-other-complex-password” />

Small comment to avoid any confusion: keystoreFile contains the certificate/private key pair used for SSL (only), while truststoreFile contains the allowed CA certificates for client SSL authentication (note that you could also add the client certificates directly into that trust-store).

避免混淆的小注释:keystoreFile 包含用于 SSL 的证书/私钥对(仅),而 truststoreFile 包含用于客户端 SSL 身份验证的允许 CA 证书(请注意,您也可以将客户端证书直接添加到该信任库中) .

If you're using an embedded tomcat container with your spring boot application, you should be able to configure these settings in your application's properties file, using the following property key/values:

如果您在 Spring Boot 应用程序中使用嵌入式 tomcat 容器,您应该能够使用以下属性键/值在应用程序的属性文件中配置这些设置:

server.ssl.key-store=/opt/tomcat/conf/key-stores/ssl-keystore.jks
server.ssl.key-store-password=some-complex-password
server.ssl.trust-store=/opt/tomcat/conf/trust-stores/ssl-truststore.jks
server.ssl.trust-store-password=some-other-complex-password
server.ssl.client-auth=want

Then, on my web app, I declare a specific SSL configuration as follows:

然后,在我的 Web 应用程序中,我声明了一个特定的 SSL 配置,如下所示:

@Configuration
@EnableWebSecurity
//In order to use @PreAuthorise() annotations later on...
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SSLAuthConfiguration extends WebSecurityConfigurerAdapter {

    @Value("${allowed.user}")
    private String ALLOWED_USER;

    @Value("${server.ssl.client.regex}")
    private String CN_REGEX;

    @Autowired
    private UserDetailsService userDetailsService;

    @Override
    protected void configure (final HttpSecurity http) throws Exception {
        http
            .csrf().disable()
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            .and()
                .authorizeRequests()
                .antMatchers("/url-path-to-protect").authenticated() //Specify the URL path(s) requiring authentication...
            .and()
                .x509() //... and that x509 authentication is enabled
                    .subjectPrincipalRegex(CN_REGEX)
                    .userDetailsService(userDetailsService);
    }

    @Autowired
    //Simplified case, where the application has only one user...
    public void configureGlobal (final AuthenticationManagerBuilder auth) throws Exception {
        //... whose username is defined in the application's properties.
        auth
            .inMemoryAuthentication()
                .withUser(ALLOWED_USER).password("").roles("SSL_USER");
    }

}

I then need to declare the UserDetailsService bean (e.g. in my Application's main class):

然后我需要声明 UserDetailsS​​ervice bean(例如在我的应用程序的主类中):

@Value("${allowed.user}")
private String ALLOWED_USER;

@Bean
public UserDetailsService userDetailsService () {

    return new UserDetailsService() {

        @Override
        public UserDetails loadUserByUsername(final String username) throws UsernameNotFoundException {
            if (username.equals(ALLOWED_USER)) {
                final User user = new User(username, "", AuthorityUtils.createAuthorityList("ROLE_SSL_USER"));
                return user;
            }
            return null;
        }
    };
}

And that's it! I can then add @PreAuthorize(“hasRole(‘ROLE_SSL_USER')”) annotations to the methods that I want to secure.

就是这样!然后,我可以将 @PreAuthorize(“hasRole('ROLE_SSL_USER')”) 注释添加到我想要保护的方法中。

To sum things up a bit, the authentication flow will be as follows:

总结一下,认证流程如下:

  1. User provides SSL certificate ;
  2. Tomcat validates against its trust-store ;
  3. The custom WebSecurityConfigurerAdapter retrieves a “username” from the certificate's CN ;
  4. The application authenticates the user associated to the retrieved username ;
  5. At method level, if annotated with @PreAuthorize("hasRole('SSL_USER')"), the application will check whether the user has the required role.
  1. 用户提供SSL证书;
  2. Tomcat 根据它的 trust-store 进行验证;
  3. 自定义 WebSecurityConfigurerAdapter 从证书的 CN 检索“用户名”;
  4. 应用程序验证与检索到的用户名相关联的用户;
  5. 在方法级别,如果使用@PreAuthorize("hasRole('SSL_USER')") 进行注释,应用程序将检查用户是否具有所需的角色。