安らかなWebサービスをいくつか作成し、Spring-Bootを使用して埋め込みTomcatコンテナを作成しています。
要件の1つは、これが2方向SSLを実装することです。私はHttpSecurityオブジェクトを見てきましたが、これを使用してSSLチャネル上でのみWebサービスを実行することができます:
@Override
protected void configure(HttpSecurity http) throws Exception {
System.out.println("CONFIGURED");
http
// ...
.requiresChannel()
.anyRequest().requiresSecure();
}
私が見つけられないように見えるのは、有効なクライアント証明書を提供するアプリケーションのみがWebサービスにアクセスできるようにする方法です。
私はSSLの基本的な知識しか持っていないので、正しい方向への一般的なポインタでさえ感謝されます。
これがデプロイされるサーバーには、アプリケーションが混在します。これは、双方向SSLでロックダウンする必要がある唯一のアプリケーションです。私が本当に探しているのは、単一のアプリケーションをロックダウンして、クライアント証明書のみを受け入れる方法です。
clientAuth=want
を構成できます。 Apache Tomcat 8構成リファレンス を参照してください。
SSLスタックが接続を受け入れる前にクライアントからの有効な証明書チェーンを必要とする場合は、
true
に設定します。 SSLスタックがクライアント証明書を要求する場合はwant
に設定しますが、提示されない場合は失敗しません。false
値(デフォルト)は、CLIENT-CERT
認証を使用するセキュリティ制約で保護されたリソースをクライアントが要求しない限り、証明書チェーンを必要としません。
そして、クライアント証明書を Spring Security-X.509 Authentication で読み取ります:
「相互認証」でSSLを使用することもできます。サーバーは、SSLハンドシェイクの一部としてクライアントに有効な証明書を要求します。サーバーは、証明書が受け入れ可能な機関によって署名されていることを確認することにより、クライアントを認証します。有効な証明書が提供されている場合、アプリケーションのサーブレットAPIを介して取得できます。 Spring Security X.509モジュールは、フィルターを使用して証明書を抽出します。証明書をアプリケーションユーザーにマップし、そのユーザーの付与された権限のセットをロードして、標準のSpring Securityインフラストラクチャで使用します。
そして
クライアントが証明書を提供しなくてもSSL接続を成功させる場合は、
clientAuth
をwant
に設定することもできます。証明書を提示しないクライアントは、フォーム認証などの非X.509認証メカニズムを使用しない限り、Spring Securityで保護されたオブジェクトにアクセスできません。
私は同様の問題に遭遇し、私が一緒に来た解決策を共有すると思いました。
まず、SSL証明書認証がウェブサーバー側で処理されることを理解する必要があります(cfr。durの説明、「clientAuth = want」設定を使用)。次に、提供された(および許可された)証明書を処理し、それをユーザーにマッピングするなどのために、Webアプリを構成する必要があります。
あなたとのわずかな違いは、スプリングブートアプリケーションをWARアーカイブにパッケージ化し、それを既存のTomcatアプリケーションサーバーにデプロイすることです。
私の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” />
混乱を避けるための小さなコメント:keystoreFileにはSSL(のみ)に使用される証明書/秘密キーのペアが含まれ、truststoreFileにはクライアントSSL認証に許可されたCA証明書が含まれます(クライアント証明書をそのトラストストアに直接追加することもできます) 。
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
次に、私の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");
}
}
次に、UserDetailsService 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;
}
};
}
以上です!次に、保護したいメソッドに@PreAuthorize(“ hasRole( ‘ROLE_SSL_USER″)”)注釈を追加できます。
少しまとめると、認証フローは次のようになります。