The previous section describes how the front-end application integrates Keycloak to achieve unified authentication and permission control. For example, vue-element-Admin integrates Keycloak to achieve unified authentication and permission control. If you don’t know much about The concept of Keycloak, see the Quick And Easy Guide to get started on it. This article describes how a typical Spring Boot/Spring Security server application integrates Keycloak for SSO login, unified authentication, permission control, and more.

Keycloak Background configuration

Create the Keycloak client

Web applications on the server are most commonly used to provide Web page services and Restful apis. The setup behind Keycloak is slightly different for different types of applications. Three clients will be created below:

  • spring-boot-keycloak-web: Corresponds to a pure Web page application
  • srping-boot-keycloak-security-api: Indicates the application that only provides Restful API services
  • spring-boot-keycloak-webapi: Indicates an application that provides both Web pages and Restful API services

Create a spring-boot-keycloak-web client

Create the srping-boot-keycloak-security- API client

Create the spring-boot-keycloato-webAPI client

Client Access Type description

As mentioned in the previous article, Keycloak currently supports three types of access:

Confidential: Applies to server applications that require browser login and access token acquisition by key. A typical usage scenario is a server-side rendered Web system.

Public: applies to client applications that require browser login. Typical application scenarios are front-end Web systems, including front-end projects implemented by VUE and React.

Standard-only: Scenarios that apply to server applications that do not require browser login and are only allowed to use bearer token requests. A typical usage scenario is restful apis.

Create roles and users and bind them

Create user admin, customer, and roles ROLE_ADMIN and ROLE_CUSTOMER, and bind them

Spring Boot(Spring Security) integrates Keycloak configuration and code

Adding Maven dependencies

Spring Boot integrates the KeyCloak dependency

<dependency>
    <groupId>org.keycloak</groupId>
    <artifactId>keycloak-spring-boot-starter</artifactId>
    <version>${keycloak.version}</version>
</dependency>
Copy the code

If you want to use Spring Security, add the following dependencies

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

Keycloak Key configuration

Example Keycloak configuration file

The following uses the spring-boot-keycloato-webAPI project, which provides both web pages and Restful APIS, as an example to illustrate how to configure keycloato-webAPI

server:
  port: 8083

keycloak:
  realm: demo
  auth-server-url: http://127.0.0.1:8080/auth
  resource: spring-boot-keycloak-webapi
  ssl-required: external
  credentials:
    secret: d50a059a-484b-4138-8403-22a491fbc488
  use-resource-role-mappings: false
  bearer-only: false
  autodetect-bearer-only: true
  security-constraints:
    - authRoles:
        - ROLE_CUSTOMER
      securityCollections:
        - name: customer
          patterns:
            - /customer
    - authRoles:
        - ROLE_ADMIN
      securityCollections:
        - name: admin
          patterns:
            - /admin
Copy the code

Realm: The realm corresponding to the Keycloak background

Auth-server-url: the address of the Keycloak

Resource: Keycloak The Client created behind the scenes

Secret: Keycloak Adds the content of the credentials Tab on the client

Use-resource-role-mappings: Use realm level or application level role mappings

Standard-only: The Keycloak access type applied is standard-only set to true, otherwise false

Autodetect -bearer-only: When an app serves both web pages and Restful apis, this parameter must be set to true. Keycloak will either redirect unauthenticated requests to the login page or return the 401 status code, depending on the method of request

Security-constraints: Defines roles for different paths to manage permissions. If you are integrating Spring Security, this configuration is not required and is controlled in the Spring Security-related configuration instead

More configuration controls can be found in the official documentation: Keycloak Official Java adapter configuration

autodetect-bearer-onlyMechanisms that

Official documentation is vague about autodetect-bearer-only

Keycloak auto-detects SOAP or REST clients based on typical headers like X-Requested-With, SOAPAction or Accept.

It is not clear exactly what value an HTTP request header takes to be recognized as an API type request. We can only find the corresponding source code to see exactly what the actual implementation is.

The source code is in the RequestAuthenticator extraction class in the Keycloak – Adapter-core JAR package

protected boolean isAutodetectedBearerOnly(HttpFacade.Request request) {
    if(! deployment.isAutodetectBearerOnly())return false;

    String headerValue = facade.getRequest().getHeader("X-Requested-With");
    if(headerValue ! =null && headerValue.equalsIgnoreCase("XMLHttpRequest")) {
        return true;
    }

    headerValue = facade.getRequest().getHeader("Faces-Request");
    if(headerValue ! =null && headerValue.startsWith("partial/")) {
        return true;
    }

    headerValue = facade.getRequest().getHeader("SOAPAction");
    if(headerValue ! =null) {
        return true;
    }

    List<String> accepts = facade.getRequest().getHeaders("Accept");
    if (accepts == null) accepts = Collections.emptyList();

    for (String accept : accepts) {
        if (accept.contains("text/html") || accept.contains("text/*") || accept.contains("* / *")) {
            return false; }}return true;
}
Copy the code

It is clear from the above source code that HTTP headers must meet the following conditions to be considered API type requests when the autodetect-bearer-only configuration is set to true, in which case the 401 status code is returned without authentication rather than being redirected to the login page

  • X-Requested-WithThe request header value isXMLHttpRequest
  • Faces-RequestThe value of the request header ispartial/At the beginning
  • appearSOAPActionRequest header
  • AcceptCannot contain the value of the request headertext/html,text/*,* / *These values

Configuration instructions for integrating Spring Security

If you want to integrate Spring Security, you can remove security-constraints from the Spring Boot configuration using the following code

@KeycloakConfiguration
public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
    /** * Registers the KeycloakAuthenticationProvider with the authentication manager. */
    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(keycloakAuthenticationProvider());
    }

    /** * Read Keycloak config from spring boot config file */
    @Bean
    public KeycloakConfigResolver keycloakConfigResolver(a) {
        return new KeycloakSpringBootConfigResolver();
    }

    /** * Defines the session authentication strategy. */
    @Bean
    @Override
    protected SessionAuthenticationStrategy sessionAuthenticationStrategy(a) {
        return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        super.configure(http);
        http
                .csrf().disable()
                .authorizeRequests()
                .antMatchers("/customer/**").hasRole("CUSTOMER")
                .antMatchers("/admin/**").hasAnyRole("ADMIN") .anyRequest().permitAll(); }}Copy the code

Controller code

@RequestMapping(value = "/", method = RequestMethod.GET)
public String index(a) {
    return "index";
}

@RequestMapping(value = "/customer", method = {RequestMethod.GET, RequestMethod.POST})
public String customer(HttpServletRequest request) {
    KeycloakSecurityContext keycloak = (KeycloakSecurityContext) request.getAttribute(KeycloakSecurityContext.class.getName());
    return "customer :: " + keycloak.getTokenString();
}

@RequestMapping(value = "/admin", method = {RequestMethod.GET, RequestMethod.POST})
public String admin(HttpServletRequest request) {
    KeycloakSecurityContext keycloak = (KeycloakSecurityContext) request.getAttribute(KeycloakSecurityContext.class.getName());
    return "admin :: " + keycloak.getTokenString();
}

@RequestMapping(value = "/logout", method = {RequestMethod.GET, RequestMethod.POST})
public String logout(HttpServletRequest request) {
    try {
        request.logout();
        return "logout success";
    } catch (ServletException e) {
        LOGGER.error("keycloak logout error", e);
        return "logout fail"; }}Copy the code

Project effect Demonstration

Run spring-boot-keycloato-webAPI, which provides both web pages and Restful API services, to see the related effects

The browser logs in as user Customer and accesses /customer

The browser logs in as user customer and accesses /admin

Postman direct request/Customer

Postman requests /customer with the Accept request header

Postman accesses/Customer with Accept and Authorization request headers

Authorization is the access token value obtained after the Customer user logs in to the browser

Postman accesses /admin with Accept and Authorization request headers

Authorization is the access token value obtained after the Customer user logs in to the browser

conclusion

Thanks to the keycloato-Spring-boot-Starter package, It is very easy to integrate The Spring Boot (Spring Security) application into Keycloak. With a little configuration and code, you can complete unified authentication and permission control functions.

Item Example Address: spring-boot-keycloak