Spring Security Kerberos Integration With MiniKdc – Spring Security Kerberos与MiniKdc的集成

最后修改: 2019年 4月 14日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this tutorial, we’ll provide an overview of Spring Security Kerberos.

在本教程中,我们将提供Spring Security Kerberos的概述。

We’ll write a Kerberos client in Java that authorizes itself to access our Kerberized service. And we’ll run our own embedded Key Distribution Center to perform full, end-to-end Kerberos authentication. All that, without any external infrastructure required thanks to Spring Security Kerberos.

我们将用 Java 写一个 Kerberos 客户端,授权自己访问我们的 Kerberized 服务。我们将运行我们自己的嵌入式密钥分发中心,以执行完整的、端到端的 Kerberos 认证。所有这些,都是由于Spring Security Kerberos,而不需要任何外部基础设施。

2. Kerberos and Its Benefits

2.Kerberos及其好处

Kerberos is a network authentication protocol that MIT created in the 1980s, specifically useful for centralizing authentication on a network.

Kerberos是麻省理工学院在20世纪80年代创建的一个网络认证协议,特别适用于网络上的集中认证。

In 1987, MIT released it to the Open Source community and it’s still under active development. In 2005, it was canonized as an IETF standard under RFC 4120.

1987年,MIT将其发布给开源社区,目前仍在积极开发中。2005年,它在RFC 4120下被规范为IETF标准。

Usually, Kerberos is used in corporate environments.  In there, it secures the environment in such a way that the user doesn’t have to authenticate to each service separately. This architectural solution is known as Single Sign-on.

通常,Kerberos被用在企业环境中。 在那里,它以这样一种方式保护环境,即用户不必分别对每个服务进行认证。这种架构解决方案被称为单点登录

Simply put, Kerberos is a ticketing system. A user authenticates once and receives a Ticket-granting Ticket (TGT). Then, the network infrastructure exchanges that TGT for Service Tickets. These service tickets allow the user to interact with infrastructure services, so long as the TGT is valid, which is usually for a couple of hours.

简单地说,Kerberos 是一个票证系统。一个用户认证一次收到一张票据授予票(TGT)。然后,网络基础设施将该TGT交换为服务票。这些服务票允许用户与基础设施服务互动,只要TGT有效,通常是几个小时。

So, it’s great that the user only signs in one time. But there’s a security benefit, too: In such an environment, the user’s password is never sent over the network. Instead, Kerberos uses it as a factor to generate another secret key that’s gonna be used to message encryption and decryption.

因此,用户只需登录一次,这很好。但也有一个安全方面的好处。在这样的环境中,用户的密码从未通过网络发送。相反,Kerberos使用它作为生成另一个秘密密钥的因素,该密钥将被用于消息加密和解密。

Another benefit is that we can manage users from a central place, say one that’s backed by LDAP. Therefore, if we disable an account in our centralized database for a given user, then we’ll revoke his access in our infrastructure. Thus, the administrators don’t have to revoke the access separately in each service.

另一个好处是,我们可以从一个中央地方管理用户,比如说,一个由LDAP支持的地方。因此,如果我们在集中式数据库中禁用某个用户的账户,那么我们将在我们的基础设施中撤销他的访问权限。因此,管理员不必在每个服务中分别撤销访问权。

Introduction to SPNEGO/Kerberos Authentication in Spring provides an in-depth overview of the technology.

Spring中的SPNEGO/Kerberos认证介绍提供了该技术的深入概述。

3. Kerberized Environment

3.Kerberized环境

So, let’s create an environment for authenticating with the Kerberos protocol. The environment will consist of three separate applications that will run simultaneously.

因此,让我们创建一个用Kerberos协议进行验证的环境。这个环境将由三个独立的应用程序组成,它们将同时运行。

First, we’ll have a Key Distribution Center that will act as the authentication point. Next, we’ll write a Client and a Service Application that we’ll configure to use Kerberos protocol.

首先,我们将有一个密钥分发中心,它将充当认证点。接下来,我们将编写一个客户端和一个服务应用程序,我们将配置为使用Kerberos协议。

Now, running Kerberos requires a bit of installation and configuration. However, we’ll leverage Spring Security Kerberos, so we’ll run the Key Distribution Center programmatically, in embedded mode. Also, the MiniKdc shown below is useful in case of integration testing with Kerberized infrastructure.

现在,运行Kerberos需要进行一些安装和配置。然而,我们将利用Spring Security Kerberos,因此我们将以编程方式,在嵌入式模式下运行密钥分发中心。另外,下面显示的MiniKdc在与Kerberized基础设施进行集成测试时非常有用。

3.1. Running a Key Distribution Center

3.1.运行一个密钥分发中心

First, we’ll launch our Key Distribution Center, that will issue the TGTs for us:

首先,我们将启动我们的钥匙分配中心,它将为我们发行TGT。

String[] config = MiniKdcConfigBuilder.builder()
  .workDir(prepareWorkDir())
  .principals("client/localhost", "HTTP/localhost")
  .confDir("minikdc-krb5.conf")
  .keytabName("example.keytab")
  .build();

MiniKdc.main(config);

Basically, we’ve given MiniKdc a set of principals and a configuration file; additionally, we’ve told MiniKdc what to call the keytab it generates.

基本上,我们已经给了MiniKdc一组原则和一个配置文件;此外,我们还告诉MiniKdc应该如何称呼它生成的keytab

MiniKdc will generate a krb5.conf file that we’ll supply to our client and service applications. This file contains the information where to find our KDC – the host and port for a given realm.

MiniKdc 将生成一个 krb5.conf 文件,我们将把它提供给我们的客户和服务应用程序。这个文件包含了如何找到我们的 KDC 的信息 – 特定领域的主机和端口。

MiniKdc.main starts the KDC and should output something like:

MiniKdc.main启动KDC,应该会有类似的输出。

Standalone MiniKdc Running
---------------------------------------------------
  Realm           : EXAMPLE.COM
  Running at      : localhost:localhost
  krb5conf        : .\spring-security-sso\spring-security-sso-kerberos\krb-test-workdir\krb5.conf

  created keytab  : .\spring-security-sso\spring-security-sso-kerberos\krb-test-workdir\example.keytab
  with principals : [client/localhost, HTTP/localhost]

3.2. Client Application

3.2 客户端应用

Our client will be a Spring Boot application that’s using a RestTemplate to make calls to external a REST API.

我们的客户将是一个Spring Boot应用程序,它使用RestTemplate来调用外部的REST API。

But, we’re going to use KerberosRestTemplate instead. It’ll need the keytab and the client’s principal:

但是,我们要使用KerberosRestTemplate来代替。它需要keytab和客户的委托人。

@Configuration
public class KerberosConfig {

    @Value("${app.user-principal:client/localhost}")
    private String principal;

    @Value("${app.keytab-location}")
    private String keytabLocation;

    @Bean
    public RestTemplate restTemplate() {
        return new KerberosRestTemplate(keytabLocation, principal);
    }
}

And that’s it! KerberosRestTemplate negotiates the client side of the Kerberos protocol for us.

就这样了!KerberosRestTemplate为我们协商 Kerberos 协议的客户端。

So, let’s create a quick class that will query some data from a Kerberized service, hosted at the endpoint app.access-url:

因此,让我们创建一个快速类,它将从Kerberized服务中查询一些数据,托管在端点app.access-url

@Service
class SampleClient {

    @Value("${app.access-url}")
    private String endpoint;

    private RestTemplate restTemplate;

    // constructor, getter, setter

    String getData() {
        return restTemplate.getForObject(endpoint, String.class);
    }
}

So, let’s create our Service Application now so that this class has something to call!

因此,让我们现在创建我们的服务应用程序,这样这个类就有东西可以调用了

3.3. Service Application

3.3.服务应用

We’ll use Spring Security, configuring it with the appropriate Kerberos-specific beans.

我们将使用Spring Security,用适当的Kerberos专用Bean来配置它。

Also, note that the service will have its principal and use the keytab, too:

另外,请注意,服务也会有它的本金并使用钥匙串。

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends AbstractHttpConfigurer<WebSecurityConfig, HttpSecurity> {

    @Value("${app.service-principal}")
    private String servicePrincipal;

    @Value("${app.keytab-location}")
    private String keytabLocation;

    public static WebSecurityConfig securityConfig() {
        return new WebSecurityConfig();
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {
        AuthenticationManager authenticationManager = 
        http.getSharedObject(AuthenticationManager.class);
        http.addFilterBefore(spnegoAuthenticationProcessingFilter(authenticationManager),
           BasicAuthenticationFilter.class);
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.exceptionHandling()
            .authenticationEntryPoint(spnegoEntryPoint())
            .and()
            .authorizeRequests()
            .antMatchers("/", "/home")
            .permitAll()
            .anyRequest()
            .authenticated()
            .and()
            .formLogin()
            .loginPage("/login")
            .permitAll()
            .and()
            .logout()
            .permitAll()
            .and()
            .apply(securityConfig());
        return http.build();
    }

    @Bean
    public AuthenticationManager authManager(HttpSecurity http) throws Exception {
        return http.getSharedObject(AuthenticationManagerBuilder.class)
            .authenticationProvider(kerberosAuthenticationProvider())
            .authenticationProvider(kerberosServiceAuthenticationProvider())
            .build();
    }

    @Bean
    public KerberosAuthenticationProvider kerberosAuthenticationProvider() {
	KerberosAuthenticationProvider provider = new KerberosAuthenticationProvider();
	// provider configuration
	return provider;
    }

    @Bean
    public SpnegoEntryPoint spnegoEntryPoint() {
	return new SpnegoEntryPoint("/login");
    }

    @Bean
    public SpnegoAuthenticationProcessingFilter spnegoAuthenticationProcessingFilter(
		AuthenticationManager authenticationManager) {
	SpnegoAuthenticationProcessingFilter filter = new SpnegoAuthenticationProcessingFilter();
	// filter configuration
	return filter;
    }

    @Bean
    public KerberosServiceAuthenticationProvider kerberosServiceAuthenticationProvider() {
	KerberosServiceAuthenticationProvider provider = new KerberosServiceAuthenticationProvider();
	// auth provider configuration 
	return provider;
    }

    @Bean
    public SunJaasKerberosTicketValidator sunJaasKerberosTicketValidator() {
	SunJaasKerberosTicketValidator ticketValidator = new SunJaasKerberosTicketValidator();
	<span class="hljs-comment">// validator configuration</span>
	return ticketValidator;
    }

}

The intro article contains all the implementation above, so we’re omitting the full methods here for brevity.

intro文章包含了上述所有的实现,所以为了简洁起见,我们在此省略了全部的方法。

Note that we’ve configured Spring Security for SPNEGO authentication. This way, we’ll be able to authenticate through the HTTP protocol, though we can also achieve SPNEGO authentication with core Java.

请注意,我们已经为SPNEGO 认证配置了 Spring Security。这样,我们就可以通过HTTP协议进行认证,尽管我们也可以通过核心Java实现SPNEGO认证

4. Testing

4.测试

Now, we’ll run an integration test to show that our client successfully retrieves data from an external server over the Kerberos protocol. To run this test, we need to have our infrastructure running, so MiniKdc and our Service Application both must be started.

现在,我们将运行一个集成测试,以显示我们的客户端通过Kerberos协议成功地从外部服务器检索数据。为了运行这个测试,我们需要运行我们的基础设施,所以MiniKdc和我们的服务应用程序都必须启动。

Basically, we’ll use our SampleClient from the Client Application to make a request to our Service Application. Let’s test it out:

基本上,我们将使用客户端应用程序中的SampleClient来向我们的服务应用程序发出一个请求。让我们来测试一下。

@Autowired
private SampleClient sampleClient;

@Test
public void givenKerberizedRestTemplate_whenServiceCall_thenSuccess() {
    assertEquals("data from kerberized server", sampleClient.getData());
}

Note that we can also prove that the KerberizedRestTemplate is important by hitting the service without it:

请注意,我们也可以通过在没有服务的情况下冲击服务来证明KerberizedRestTemplate的重要性。

@Test
public void givenRestTemplate_whenServiceCall_thenFail() {
    sampleClient.setRestTemplate(new RestTemplate());
    assertThrows(RestClientException.class, sampleClient::getData);
}

As a side note, there’s a chance our second test could re-use the ticket already stored in the credential cache. This would happen due to the automatic SPNEGO negotiation used in HttpUrlConnection.

顺便提一下,我们的第二个测试有可能重复使用已经存储在credential cache中的票。这将发生在HttpUrlConnection中使用的自动SPNEGO协商上。

As a result, the data might actually return, invalidating our test. Depending on our needs, then, we can disable ticket cache usage through the system property http.use.global.creds=false.

因此,数据可能会实际返回,使我们的测试无效。根据我们的需要,我们可以通过系统属性http.use.global.creds=false.禁止使用票据缓存。

5. Conclusion

5.总结

In this tutorial, we explored Kerberos for centralized user management and how Spring Security supports the Kerberos protocol and SPNEGO authentication mechanism.

在本教程中,我们探讨了用于集中式用户管理的Kerberos以及Spring Security如何支持Kerberos协议和SPNEGO认证机制。

We used MiniKdc to stand up an embedded KDC and also created a very simple Kerberized client and server. This setup was handy for exploration and especially handy when we created an integration test to test things out.

我们使用 MiniKdc 来建立一个嵌入式的 KDC, 并创建了一个非常简单的 Kerberized 客户端和服务器。这种设置便于探索,特别是当我们创建一个集成测试来测试东西的时候,就更方便了。

Now, we’ve just scratched the surface. To dive deeper, check out the Kerberos wiki page or its RFC. Also, the official documentation page will be useful. Other than that,  to see how the things could be done in core java, the following Oracle’s tutorial shows it in details.

现在,我们只是触及了表面。要深入研究,请查看Kerberos wiki页面其RFC。另外,官方文档页面也将是有用的。除此之外,要想知道在核心java中如何完成这些事情,跟随Oracle的教程可以详细地看到。

As usual, the code can be found on our GitHub page.

像往常一样,可以在我们的GitHub页面找到代码。