This article tries out the keycloak counterpart of the Spring Boot Adapter to see how keycloak protects Spring Boot applications.

Client

I believe many of you have used WeChat Open Platform and Ant Open Platform. First we need to register a client on these open platforms to get a set of credentials like username and password. Some of them are called appid and secret; some are called clientid and secret, both mean the same thing. In fact, keycloak is similar, and requires a client to be registered in the corresponding realm. The following diagram not only clearly illustrates the relationship between Master realm and custom realm in keycloak, but also the relationship between users and clients in a realm.

realm

We need to create a client in the realm of felord.cn.

keycloak

Once created, you will see that the client of felord.cn has an additional.

keycloak

You can login to the created user via http://localhost:8011/auth/realms/felord.cn/account/.

Then we edit the configuration of the client spring-boot-client.

keycloak

For testing purposes, here I just filled in the only required field in the settings tab Valid redirect URI, which means that all APIs of the client springboot-client are subject to permission control.

Roles

Role-based permission control is the current dominant idea of permission control, and keycloak takes this approach. We need to create a role and grant the user felord created in the previous article. Let’s create a simple role.

keycloak

The role functionality of keycloak is very powerful, and we will learn this concept in depth with you in a later article in the series.

Mapping roles to users

Then we take the role base_user created above and assign it to the user felord :

keycloak

With the user, role, and role mapping all taken care of here, it’s left to define the resources on the client.

Getting and refreshing JWTs

We can get the JWT of the user login in the following way. execute the request:

1
2
3
4
5
POST /auth/realms/felord.cn/protocol/openid-connect/token HTTP/1.1
Host: localhost:8011
Content-Type: application/x-www-form-urlencoded

client_id=springboot-client&username=felord&password=123456&grant_type=password

will get a response:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
{
    "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiS 省略",
    "expires_in": 300,
    "refresh_expires_in": 1800,
    "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCIgOiAi 省略",
    "token_type": "Bearer",
    "not-before-policy": 0,
    "session_state": "2fc7e289-c86f-4f6f-b4d3-1183a9518acc",
    "scope": "profile email"
}

Refresh Token just need to bring refresh_token, change grant_type to refresh_token then you can refresh Token pair, below is the request refresh message.

1
2
3
4
5
POST /auth/realms/felord.cn/protocol/openid-connect/token HTTP/1.1
Host: localhost:8011
Content-Type: application/x-www-form-urlencoded

client_id=springboot-client&grant_type=refresh_token&refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJlYWE2MThhMC05Y2UzLTQxZWMtOTZjYy04MGQ5ODVkZjJjMTIifQ.eyJleHAiOjE2MjU3NjI4ODYsImlhdCI6MTYyNTc2MTA4NiwianRpIjoiZjc2MjVmZmEtZWU3YS00MjZmLWIwYmQtOTM3MmZiM2Q4NDA5IiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDExL2F1dGgvcmVhbG1zL2ZlbG9yZC5jbiIsImF1ZCI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODAxMS9hdXRoL3JlYWxtcy9mZWxvcmQuY24iLCJzdWIiOiI0YzFmNWRiNS04MjU0LTQ4ZDMtYTRkYS0wY2FhZTMyOTk0OTAiLCJ0eXAiOiJSZWZyZXNoIiwiYXpwIjoic3ByaW5nYm9vdC1jbGllbnQiLCJzZXNzaW9uX3N0YXRlIjoiZDU2NmU0ODMtYzc5MS00OTliLTg2M2ItODczY2YyNjMwYWFmIiwic2NvcGUiOiJwcm9maWxlIGVtYWlsIn0.P4vWwyfGubSt182P-vcyMdKvJfvwKYr1nUlOYBWzQks

Note: The content-type of both requests is application/x-www-form-urlencoded.

Spring Boot Client

Build a very traditional Spring Boot application, don’t forget to bring the Spring MVC module, and add the starter for keycloak.

1
2
3
4
5
<dependency>
    <groupId>org.keycloak</groupId>
    <artifactId>keycloak-spring-boot-starter</artifactId>
    <version>14.0.0</version>
</dependency>

Then write a random Spring MVC interface.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
/**
 * @author felord.cn
 * @since 2021/7/7 17:05
 */
@RestController
@RequestMapping("/foo")
public class FooController {

    @GetMapping("/bar")
    public String bar(){
        return "felord.cn";
    }

}

Next, we declare that we define that only users with the base_user role in felord.cn realm can access the /foo/bar interface. So where is the definition? We start with a static definition in application.yml in spring boot, and we will implement dynamic control later. The configuration is as follows.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
keycloak:
# 声明客户端所在的realm
  realm: felord.cn
# keycloak授权服务器的地址
  auth-server-url: http://localhost:8011/auth
# 客户端名称
  resource: springboot-client
# 声明这是一个公开的客户端,否则不能在keycloak外部环境使用,会403
  public-client: true
# 这里就是配置客户端的安全约束,就是那些角色映射那些资源
  security-constraints:
# 角色和资源的映射关系。下面是多对多的配置方式 ,这里只配置base_user才能访问 /foo/bar
    - auth-roles:
        - base_user
      security-collections:
        - patterns:
            - '/foo/bar'

Then start the Spring Boot application and call http://localhost:8080/foo/bar in the browser and you will see that the browser will jump to the following address.

1
http://localhost:8011/auth/realms/felord.cn/protocol/openid-connect/auth?response_type=code&client_id=springboot-client&redirect_uri=http%3A%2F%2Flocalhost%3A8080%2Ffoo%2Fbar&state=20e0958d-a7a9-422a-881f-cbd8f25d7842&login=true&scope=openid

keycloak

The authentication authorization model is based on OIDC (an enhanced version of OAuth 2.0). Only if you fill in the username and password correctly can you get the correct response from /foo/bar.

Summary

It was so easy to implement OIDC authentication authorization to protect the interface in Spring Boot with just a few configurations. But after reading this article you will have a lot of questions because you don’t know much about the OIDC protocol. This protocol is very important and is used by big manufacturers. I will explain it in a future article.

Reference https://felord.cn/keycloak-2.html