A Quick Guide to Using Keycloak With Spring Boot – 在Spring Boot中使用Keycloak的快速指南

最后修改: 2017年 11月 8日

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

1. Overview

1.概述

In this tutorial, we’ll discuss the basics of setting up a Keycloak server and connecting a Spring Boot application to it using Spring Security OAuth2.0.

在本教程中,我们将讨论设置Keycloak服务器并使用Spring Security OAuth2.0将Spring Boot应用程序连接到它的基本知识。

2. What Is Keycloak?

2.什么是Keycloak?

Keycloak is an open source Identity and Access Management solution targeted towards modern applications and services.

Keycloak是一个开源的身份和访问管理解决方案,面向现代应用和服务。

Keycloak offers features such as Single-Sign-On (SSO), Identity Brokering and Social Login, User Federation, Client Adapters, an Admin Console, and an Account Management Console.

Keycloak提供的功能包括单点登录(SSO)、身份中介和社交登录、用户联盟、客户端适配器、管理控制台和账户管理控制台。

In our tutorial, we’ll use the Admin Console of Keycloak for setting up and connecting to Spring Boot using the Spring Security OAuth2.0.

在我们的教程中,我们将使用Keycloak的管理控制台来设置并使用Spring Security OAuth2.0连接到Spring Boot。

3. Setting Up a Keycloak Server

3.设置一个Keycloak服务器

In this section, we will set up and configure the Keycloak server.

在这一部分,我们将设置和配置Keycloak服务器。

3.1. Downloading and Installing Keycloak

3.1.下载和安装Keycloak

There are several distributions to choose from. However, in this tutorial, we’ll be using the standalone version.

有几个发行版可供选择。然而,在本教程中,我们将使用独立的版本。

Let’s download the Keycloak-19.0.1 Standalone server distribution from the official source.

让我们从官方渠道下载Keycloak-19.0.1独立服务器版本

Once we’ve downloaded the Standalone server distribution, we can unzip and start Keycloak from the terminal:

一旦我们下载了独立服务器发行版,我们就可以从终端解压并启动Keycloak:

$ unzip keycloak-legacy-19.0.1.zip 
$ cd keycloak-legacy-19.0.1/keycloak-19.0.1/bin
$ ./standalone.sh -Djboss.socket.binding.port-offset=100

After running ./standalone.sh, Keycloak will be starting its services. Once we see a line containing Keycloak 19.0.1 (WildFly Core 18.1.1.Final) started, we’ll know its start-up is complete.

在运行./standalone.sh之后,Keycloak将启动其服务。一旦我们看到包含Keycloak 19.0.1 (WildFly Core 18.1.1.Final) started的一行,我们就知道它的启动已经完成。

Now let’s open a browser and visit http://localhost:8180. We’ll be redirected to http://localhost:8180/auth to create an administrative login:

现在让我们打开浏览器,访问http://localhost:8180我们将被重定向到http://localhost:8180/auth,创建一个管理登录:

 

keycloak1

Let’s create an initial admin user named initial1 with the password zaq1!QAZ. Upon clicking Create, we’ll see the message User Created.

让我们创建一个名为initial1的初始管理员用户,密码为zaq1!QAZ。点击Create后,我们会看到User Created消息。

We can now proceed to the Administrative Console. On the login page, we’ll enter the initial admin user credentials:

我们现在可以进入管理控制台了。在登录页面,我们将输入最初的管理用户凭证。

keycloak admin console

3.2. Creating a Realm

3.2.创建一个境界

A successful login will take us to the console and open up the default Master realm for us.

登录成功后,我们将进入控制台,为我们打开默认的Master境界。

Here we’ll focus on creating a custom realm.

在这里,我们将专注于创建一个自定义境界。

Let’s navigate to the upper left corner to discover the Add realm button:

让我们导航到左上角,发现添加境界按钮:

keycloak add realm

On the next screen, let’s add a new realm called SpringBootKeycloak:

在下一个屏幕上,让我们添加一个名为SpringBootKeycloak的新领域:

keycloak new realm

After clicking the Create button, a new realm will be created and we’ll be redirected to it. All the operations in the next sections will be performed in this new SpringBootKeycloak realm.

点击Create按钮后,一个新的境界将被创建,我们将被重定向到它。接下来的所有操作都将在这个新的SpringBootKeycloak领域中进行。

3.3. Creating a Client

3.3.创建一个客户

Now we’ll navigate to the Clients page. As we can see in the image below, Keycloak comes with Clients that are already built-in:

现在,我们将导航到客户端页面。正如我们在下面的图片中看到的,Keycloak附带的客户端已经内置了

keycloak clients

We still need to add a new client to our application, so we’ll click Create. We’ll call the new Client login-app:

我们仍然需要向我们的应用程序添加一个新的客户端,所以我们将点击Create我们将调用新的客户端login-app

keycloak add client 1

In the next screen, for the purpose of this tutorial, we’ll leave all the defaults except the Valid Redirect URIs field. This field should contain the application URL(s) that will use this client for authentication:

在下一个屏幕,为了本教程的目的,我们将保留所有的默认值,除了有效的重定向URI字段。该字段应包含将使用该客户端进行验证的应用程序URL

keycloak valid redirect uris

Later on, we’ll be creating a Spring Boot Application running at the port 8081 that’ll use this client. Hence we’ve used a redirect URL of http://localhost:8081/* above.

稍后,我们将创建一个运行在8081端口的Spring Boot应用程序,它将使用这个客户端。因此,我们在上面使用了http://localhost:8081/*的重定向URL。

3.4. Creating a Role and a User

3.4.创建一个角色和一个用户

Keycloak uses Role-Based Access; therefore, each user must have a role.

Keycloak使用基于角色的访问;因此,每个用户必须有一个角色。

To do that, we need to navigate to the Roles page:

要做到这一点,我们需要导航到Roles页面。

keycloak roles

Then we’ll add the user role:

然后,我们将添加用户角色。

keycloak add role

Now we have a role that can be assigned to users, but as there are no users yet, let’s go the Users page and add one:

现在我们有一个可以分配给用户的角色,但由于还没有用户,让我们去Users页面添加一个:

keycloak users

We’ll add a user named user1:

我们将添加一个名为user1:的用户。

keycloak adduser

Once the user is created, a page with its details will be displayed:

一旦用户被创建,将显示一个包含其详细信息的页面。

keycloak user

We can now go to the Credentials tab. We’ll be setting the initial password to xsw2@WSX:

现在我们可以进入Credentials标签。我们将设置初始密码为xsw2@WSX

keycloak credentials

Finally, we’ll navigate to the Role Mappings tab. We’ll be assigning the user role to our user1:

最后,我们将导航到角色映射标签。我们将把user角色分配给我们的user1

keycloak userrole

4. Generating Access Tokens With Keycloak’s API

4.用Keycloak的API生成访问令牌

Keycloak provides a REST API for generating and refreshing access tokens. We can easily use this API to create our own login page.

Keycloak提供了一个REST API用于生成和刷新访问令牌。我们可以轻松地使用这个API来创建我们自己的登录页面。

First, we need to acquire an access token from Keycloak by sending a POST request to this URL:

首先,我们需要通过向这个URL发送一个POST请求,从Keycloak获得一个访问令牌。

http://localhost:8180/auth/realms/SpringBootKeycloak/protocol/openid-connect/token

The request should have this body in a x-www-form-urlencoded format:

该请求应具有x-www-form-urlencoded格式的该主体。

client_id:<your_client_id>
username:<your_username>
password:<your_password>
grant_type:password

In response, we’ll get an access_token and a refresh_token.

作为回应,我们将得到一个access_token和一个refresh_token

The access token should be used in every request to a Keycloak-protected resource by simply placing it in the Authorization header:

访问令牌应该在每次请求Keycloak保护的资源时使用,只需将其放在Authorization头中。

headers: {
    'Authorization': 'Bearer' + access_token
}

Once the access token has expired, we can refresh it by sending a POST request to the same URL as above, but containing the refresh token instead of username and password:

一旦访问令牌过期,我们可以通过向上述相同的URL发送一个POST请求来刷新它,但包含刷新令牌而不是用户名和密码。

{
    'client_id': 'your_client_id',
    'refresh_token': refresh_token_from_previous_request,
    'grant_type': 'refresh_token'
}

Keycloak will respond to this with a new access_token and refresh_token.

Keycloak将以一个新的access_tokenrefresh_token来回应。

5. Creating and Configuring a Spring Boot Application

5.创建和配置Spring Boot应用程序

In this section, we’ll create a Spring Boot application and configure it as an OAuth Client to interact with the Keycloak server.

在本节中,我们将创建一个Spring Boot应用程序,并将其配置为OAuth客户端,与Keycloak服务器进行交互。

5.1. Dependencies

5.1.依赖性

We use the Spring Security OAuth2.0 Client to connect to the Keycloak server.

我们使用Spring Security OAuth2.0客户端来连接到Keycloak服务器

Let’s start by declaring spring-boot-starter-oauth2-client dependency in a Spring Boot application in the pom.xml:

让我们首先在pom.xml中声明Spring Boot应用程序中的spring-boot-starter-oauth2-client依赖。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>

Also, as we need to use Spring Security with Spring Boot, we must add this dependency:

此外,由于我们需要在Spring Boot中使用Spring Security,我们必须添加这个依赖性

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

Now, the Spring Boot application can interact with Keycloak.

现在,Spring Boot应用程序可以与Keycloak互动。

5.2. Keycloak Configuration

5.2.Keycloak配置

We consider the Keycloak client as an OAuth Client. So, we need to configure the Spring Boot application to use the OAuth Client.

我们认为Keycloak客户端是一个OAuth客户端。因此,我们需要配置Spring Boot应用程序以使用OAuth客户端

The ClientRegistration class holds all of the basic information about the client. Spring auto-configuration looks for properties with the schema spring.security.oauth2.client.registration.[registrationId] and registers a client with OAuth 2.0 or OpenID Connect (OIDC).

ClientRegistration类持有关于客户端的所有基本信息。Spring自动配置寻找模式为spring.security.oauth2.client.registration.[registrationId]的属性,并通过OAuth 2.0或OpenID Connect(OIDC)注册一个客户端。

Let’s configure the client registration configuration:

让我们来配置一下客户端的注册配置。

spring.security.oauth2.client.registration.keycloak.client-id=login-app
spring.security.oauth2.client.registration.keycloak.authorization-grant-type=authorization_code
spring.security.oauth2.client.registration.keycloak.scope=openid

The value we specify in client-id matches the client we named in the admin console.

我们在client-id中指定的值与我们在管理控制台中命名的客户端相匹配。

The Spring Boot application needs to interact with an OAuth 2.0 or OIDC provider to handle the actual request logic for different grant types. So, we need to configure the OIDC provider. It can be auto-configured based on property values with the schema spring.security.oauth2.client.provider.[provider name].

Spring Boot应用程序需要与OAuth 2.0或OIDC提供商交互,以处理不同授予类型的实际请求逻辑。因此,我们需要配置OIDC提供者。它可以根据模式spring.security.oauth2.client.provider.[provider name]的属性值进行自动配置。

Let’s configure the OIDC provider configuration:

让我们来配置OIDC提供者的配置。

spring.security.oauth2.client.provider.keycloak.issuer-uri=http://localhost:8180/auth/realms/SpringBootKeycloak
spring.security.oauth2.client.provider.keycloak.user-name-attribute=preferred_username

As we can recall, we started Keycloak on port 8180, hence the path specified in issuer-uri. This property identifies the base URI for the authorization server. We enter the realm name we created in the Keycloak admin console. Additionally, we can define user-name-attribute as preferred_username so as to populate our controller’s Principal with a proper user.

我们可以回忆一下,我们在端口8180上启动Keycloak,因此在 issuer-uri中指定了路径。这个属性确定了授权服务器的基本URI。我们输入我们在Keycloak管理控制台创建的境界名称。此外,我们可以将 user-name-attribute定义为preferred_username,以便在控制器的Principal中加入一个合适的用户。

5.3. Configuration Class

5.3.配置类

We configure HttpSecurity by creating a SecurityFilterChain bean. Also, we need to enable OAuth2 login using http.oauth2Login().

我们通过创建一个 SecurityFilterChain Bean来配置HttpSecurity。此外,我们还需要使用http.oauth2Login()来启用OAuth2登录。

Let’s create the security configuration:

让我们来创建安全配置。

@Configuration
@EnableWebSecurity
class SecurityConfig {

    private final KeycloakLogoutHandler keycloakLogoutHandler;

    SecurityConfig(KeycloakLogoutHandler keycloakLogoutHandler) {
        this.keycloakLogoutHandler = keycloakLogoutHandler;
    }

    @Bean
    protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
        return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.authorizeRequests()
            .antMatchers("/customers*", "/users*")
            .hasRole("USER")
            .anyRequest()
            .permitAll();
        http.oauth2Login()
            .and()
            .logout()
            .addLogoutHandler(keycloakLogoutHandler)
            .logoutSuccessUrl("/");
        return http.build();
    }
}

In the code above, the oauth2Login() method adds OAuth2LoginAuthenticationFilter to the filter chain. This filter intercepts requests and applies the needed logic for OAuth 2 authentication.

在上面的代码中,oauth2Login()方法将OAuth2LoginAuthenticationFilter添加到过滤器链。该过滤器拦截请求并应用所需的逻辑进行OAuth 2认证

We configure access based on authorities and roles in the configure() method. These constraints ensure that every request to /customers/* will only be authorized if the one requesting it is an authenticated user with the role USER.

我们在configure()方法中基于权限和角色配置访问。这些约束确保对/customers/*的每个请求只有在请求者是具有USER角色的认证用户时才会被授权。

Finally, we need to handle logout from Keycloak. To do this, we add KeycloakLogoutHandler class:

最后,我们需要处理从Keycloak注销的问题。为了做到这一点,我们添加KeycloakLogoutHandler类。

@Component
public class KeycloakLogoutHandler implements LogoutHandler {

    private static final Logger logger = LoggerFactory.getLogger(KeycloakLogoutHandler.class);
    private final RestTemplate restTemplate;

    public KeycloakLogoutHandler(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }

    @Override
    public void logout(HttpServletRequest request, HttpServletResponse response, 
      Authentication auth) {
        logoutFromKeycloak((OidcUser) auth.getPrincipal());
    }

    private void logoutFromKeycloak(OidcUser user) {
        String endSessionEndpoint = user.getIssuer() + "/protocol/openid-connect/logout";
        UriComponentsBuilder builder = UriComponentsBuilder
          .fromUriString(endSessionEndpoint)
          .queryParam("id_token_hint", user.getIdToken().getTokenValue());

        ResponseEntity<String> logoutResponse = restTemplate.getForEntity(
        builder.toUriString(), String.class);
        if (logoutResponse.getStatusCode().is2xxSuccessful()) {
            logger.info("Successfulley logged out from Keycloak");
        } else {
            logger.error("Could not propagate logout to Keycloak");
        }
    }

}

The KeycloakLogoutHandler class implements LogoutHandler class and sends logout request to the Keycloak.

KeycloakLogoutHandler类实现了LogoutHandler类并向Keycloak发送注销请求。

Now, after we authenticate, we’ll be able to access the internal customers page.

现在,在我们认证之后,我们将能够访问内部客户页面。

5.4. Thymeleaf Web Pages

5.4.Thymeleaf网页

We’re using Thymeleaf for our web pages.

我们正在使用Thymeleaf制作我们的网页。

We’ve got three pages:

我们有三页。

  • external.html – an externally facing web page for the public
  • customers.html – an internally facing page that will have its access restricted to only authenticated users with the role user
  • layout.html – a simple layout, consisting of two fragments, that’s used for both the externally facing page and the internally facing page

The code for the Thymeleaf templates is available on Github.

Thymeleaf模板的代码是可在Github上获得。

5.5. Controller

5.5.控制器

The web controller maps the internal and external URLs to the appropriate Thymeleaf templates:

Web控制器将内部和外部URL映射到适当的Thymeleaf模板。

@GetMapping(path = "/")
public String index() {
    return "external";
}
    
@GetMapping(path = "/customers")
public String customers(Principal principal, Model model) {
    addCustomers();
    model.addAttribute("customers", customerDAO.findAll());
    model.addAttribute("username", principal.getName());
    return "customers";
}

For the path /customers, we’re retrieving all customers from a repository and adding the result as an attribute to the Model. Later on, we iterate through the results in Thymeleaf.

对于路径/customers,我们从资源库中检索所有客户,并将结果作为属性添加到Model中。稍后,我们在Thymeleaf中遍历这些结果。

To be able to display a username, we’re injecting the Principal as well.

为了能够显示一个用户名,我们也要注入Principal

We should note that we’re using customer here just as raw data to display, and nothing more.

我们应该注意到,我们在这里使用客户只是作为显示的原始数据,而不是其他。

6. Demonstration

6.示范

Now we’re ready to test our application. To run a Spring Boot application, we can start it easily through an IDE, like Spring Tool Suite (STS), or run this command in the terminal:

现在我们已经准备好测试我们的应用程序了。要运行Spring Boot应用程序,我们可以通过IDE(如Spring Tool Suite(STS))轻松启动它,或者在终端运行这个命令。

mvn clean spring-boot:run

On visiting http://localhost:8081 we see:

在访问http://localhost:8081时,我们看到。

externalFacingKeycloakPage e1551248465138

Now we click customers to enter the intranet, which is the location of sensitive information.

现在我们点击customers进入内网,这是敏感信息的位置。

Note that we’ve been redirected to authenticate through Keycloak to see if we’re authorized to view this content:

注意,我们已经被重定向到通过Keycloak进行认证,以查看我们是否被授权查看此内容:

keycloak userlogin

Once we log in as user1, Keycloak will verify our authorization that we have the user role, and we’ll be redirected to the restricted customers page:

一旦我们以user1登录,Keycloak将验证我们的授权,即我们有user角色,我们将被重定向到受限制的customers页面。

customers page

Now we’ve finished the set up of connecting Spring Boot with Keycloak and demonstrating how it works.

现在我们已经完成了连接Spring Boot与Keycloak的设置,并演示了它的工作原理。

As we can see, Spring Boot seamlessly handled the entire process of calling the Keycloak Authorization Server. We did not have to call the Keycloak API to generate the Access Token ourselves, or even send the Authorization header explicitly in our request for protected resources.

正如我们所看到的,Spring Boot无缝地处理了调用Keycloak授权服务器的整个过程。我们不必调用Keycloak API来生成访问令牌,甚至不必在请求受保护资源时明确发送授权头。

Next we’ll be reviewing how to use Spring Security in conjunction with our existing application.

接下来我们将回顾如何将Spring Security与我们现有的应用程序结合起来使用。

7. Conclusion

7.结论

In this article, we configured a Keycloak server and used it with a Spring Boot Application.

在这篇文章中,我们配置了一个Keycloak服务器,并将其用于Spring Boot应用程序。

We also learned how to set up Spring Security and use it in conjunction with Keycloak. A working version of the code shown in this article is available over on Github.

我们还学习了如何设置Spring Security并将其与Keycloak结合使用。本文中所示代码的工作版本可在Github上获得。