5344 views
Keycloak to secure spring-boot microservices ==== ## 1. Overview In this article, we'll cover the basics of setting up a Keycloak server, how to connect a Spring Boot application to it, and how to use it with Spring Security. It is a simplified version from : https://www.baeldung.com/spring-boot-keycloak ## 2. What Is Keycloak? Keycloak is an open source Identity and Access Management solution targeted towards modern applications and services. 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. To learn more about Keycloak, please visit the official page. In our tutorial, we'll be using the Admin Console of Keycloak for setting up and then connecting to Spring Boot using the Keycloak Client Adapter. ## 3. Setting Up a Keycloak Server There are several distributions to choose from. However, in this tutorial, we'll be using the docker version. ```bash docker run -p 8090:8080 -e KEYCLOAK_USER=admin -e KEYCLOAK_PASSWORD=admin quay.io/keycloak/keycloak:12.0.3 ``` ## Creating a Client 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: ![](https://www.baeldung.com/wp-content/uploads/2017/11/keycloak-clients-2048x626.png) But we need to add a new client to our application, so we'll click Create. We'll call the new Client login-app: ![](https://www.baeldung.com/wp-content/uploads/2017/11/keycloak-add-client-1-1024x371.png) In the next screen, for this tutorial, we'll be leaving all the defaults except the Valid Redirect URIs field. This field should contain the application URL(s) that will use this client for authentication: ![](https://www.baeldung.com/wp-content/uploads/2017/11/keycloak-valid-redirect-uris-768x328.png) 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. ## Creating a Role and a User Keycloak uses Role-Based Access. Therefore, each user must have a role. To do that, we need to navigate to the Roles page: ![](https://www.baeldung.com/wp-content/uploads/2017/11/keycloak-roles-2048x508.png) Then, we'll add the user role: ![](https://www.baeldung.com/wp-content/uploads/2017/11/keycloak-add-role-1024x353.png) Now we've got a role that can be assigned to users, but there are no users yet. So let's go the Users page and add one: ![](https://www.baeldung.com/wp-content/uploads/2017/11/keycloak-users-2048x710.png) We'll add a user named user1: ![](https://www.baeldung.com/wp-content/uploads/2017/11/keycloak-add-user-1024x690.png) Once the user is created, a page with its details will be displayed: ![](https://www.baeldung.com/wp-content/uploads/2017/11/keycloak-user-1024x604.png) We can now go to the Credentials tab. We'll be setting the initial password to **xsw2@WSX**: ![](https://www.baeldung.com/wp-content/uploads/2017/11/keycloak-credentials-768x422.png) Finally, we'll navigate to the Role Mappings tab. We'll be assigning the user role to our user1: ![](https://www.baeldung.com/wp-content/uploads/2017/11/role-mappings-1.png) ## Generating Access Tokens with Keycloak's API (Optional to play with this example) Keycloak provides a REST API for generating and refreshing access tokens. We can easily use this API to create our own login page. First, we need to acquire an access token from Keycloak by sending a POST request to this URL: http://localhost:8090/auth/realms/master/protocol/openid-connect/token The request should have this body, in a x-www-form-urlencoded format: ```txt 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. The access token should be used in every request to a Keycloak-protected resource by simply placing it in the Authorization header: ```json 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: ```json { '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. ## Creating a Spring Boot Application The working code of this example is available on github. https://github.com/barais/spring-boot-keycloak ### Dependencies The latest Spring Boot Keycloak Starter dependencies can be found on Maven Central. The Keycloak Spring Boot adapter capitalizes on Spring Boot’s auto-configuration, so all we need to do is add the Keycloak Spring Boot starter to our project. Within the dependencies XML element, we need the following to run Keycloak with Spring Boot: ```xml <dependency> <groupId>org.keycloak</groupId> <artifactId>keycloak-spring-boot-starter</artifactId> </dependency> ``` After the dependencies XML element, we need to specify dependencyManagement for Keycloak: ```xml <dependencyManagement> <dependencies> <dependency> <groupId>org.keycloak.bom</groupId> <artifactId>keycloak-adapter-bom</artifactId> <version>12.0.3</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> ``` The following embedded containers are supported now and don't require any extra dependencies if using Spring Boot Keycloak Starter: - Tomcat - Undertow - Jetty ### Thymeleaf Web Pages We're using Thymeleaf for our web pages. 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 is used for both the externally facing page and the internally facing page ### Controller The web controller maps the internal and external URLs to the appropriate Thymeleaf templates: ```java @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. To be able to display a username, we're injecting the Principal as well. Note that we're using customer here just as raw data to display, and nothing more. ### Keycloak Configuration Here's the basic, mandatory configuration: ```txt keycloak.auth-server-url=http://localhost:8090/auth keycloak.realm=master keycloak.resource=login-app keycloak.public-client=true ``` As we recall, we started Keycloak on port 8180, hence the path specified in keycloak.auth-server-url. We enter the realm name we created in the Keycloak admin console. The value we specify in keycloak.resource matches the client we named in the admin console. Here are the security constraints we'll be using: ```txt keycloak.security-constraints[0].authRoles[0]=user keycloak.security-constraints[0].securityCollections[0].patterns[0]=/customers/* ``` 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. Additionally, we can define keycloak.principal-attribute as preferred_username so as to populate our controller's Principal with a proper user: ```txt keycloak.principal-attribute=preferred_username ``` ### Demonstration 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: ```bash mvn clean spring-boot:run ``` On visiting http://localhost:8081 we see: ![](https://www.baeldung.com/wp-content/uploads/2017/11/externalFacingKeycloakPage-e1551248465138.png) Now we click customers to enter the intranet, which is the location of sensitive information. We can see that we've been redirected to authenticate through Keycloak to see if we're authorized to view this content: ![](https://www.baeldung.com/wp-content/uploads/2017/11/keycloak-login.png) 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: ![](https://www.baeldung.com/wp-content/uploads/2017/11/customers-page-2048x613.png) Now we've finished the set up of connecting Spring Boot with Keycloak and demonstrating how it works. As we can see, the entire process of calling the Keycloak Authorization Server was handled seamlessly by Spring Boot for us. 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. Next, we'll be reviewing how to use Spring Security in conjunction with our existing application. ## Spring Security There is a Keycloak Spring Security Adapter, and it’s already included in our Spring Boot Keycloak Starter dependency. We'll now see how to integrate Spring Security with Keycloak. ### Dependency To use Spring Security with Spring Boot, we must add this dependency: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> <version>2.2.6.RELEASE</version> </dependency> The latest Spring Boot Starter Security release can be found on Maven Central. ### Configuration Class Keycloak provides a KeycloakWebSecurityConfigurerAdapter as a convenient base class for creating a WebSecurityConfigurer instance. This is helpful because any application secured by Spring Security requires a configuration class that extends WebSecurityConfigurerAdapter: ```java @Configuration @EnableWebSecurity @ComponentScan(basePackageClasses = KeycloakSecurityComponents.class) class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter { @Autowired public void configureGlobal( AuthenticationManagerBuilder auth) throws Exception { KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider(); keycloakAuthenticationProvider.setGrantedAuthoritiesMapper( new SimpleAuthorityMapper()); auth.authenticationProvider(keycloakAuthenticationProvider); } @Bean public KeycloakSpringBootConfigResolver KeycloakConfigResolver() { return new KeycloakSpringBootConfigResolver(); } @Bean @Override protected SessionAuthenticationStrategy sessionAuthenticationStrategy() { return new RegisterSessionAuthenticationStrategy( new SessionRegistryImpl()); } @Override protected void configure(HttpSecurity http) throws Exception { super.configure(http); http.authorizeRequests() .antMatchers("/customers*") .hasRole("user") .anyRequest() .permitAll(); } } ``` In the code above, the method **configureGlobal()** tasks the **SimpleAuthorityMapper** to make sure roles are not prefixed with **ROLE_**. Another method, keycloakConfigResolver defines that we want to use the Spring Boot properties file support instead of the default keycloak.json. Because we've set up the security constraints with Spring Security, we can remove or comment these security constraints we'd placed earlier in the properties file: ```txt #keycloak.security-constraints[0].authRoles[0]=user #keycloak.security-constraints[0].securityCollections[0].patterns[0]=/customers/* ``` Now, after we authenticate, we'll be able to access the internal customers' page, same as we saw before. ## Conclusion In this tutorial, we’ve configured a Keycloak server and used it with a Spring Boot Application. We've also seen how to set up Spring Security and use it in conjunction with Keycloak. ## Appendix (do not follow) ### Deploy in jetty Download the json { "realm": "master", "auth-server-url": "http://localhost:8090/auth/", "ssl-required": "external", "resource": "jetty", "public-client": true, "confidential-port": 0 } ## Download jetty Download jetty 9.4 https://www.eclipse.org/jetty/download.php ## Download keycloak adapter for jetty Keycloak has a separate adapter for Jetty 9.1.x, Jetty 9.2.x and Jetty 9.3.x that you will have to install into your Jetty installation. You then have to provide some extra configuration in each WAR you deploy to Jetty. Let’s go over these steps. https://github.com/keycloak/keycloak/releases/download/12.0.3/keycloak-oidc-jetty94-adapter-12.0.3.zip ## Adapter Installation Adapters are no longer included with the appliance or war distribution.Each adapter is a separate download on the Keycloak download site. They are also available as a maven artifact. You must unzip the Jetty 9.x distro into Jetty 9.x’s base directory. Including adapter’s jars within your WEB-INF/lib directory will not work! In the example below, the Jetty base is named your-base: $ cd your-base $ unzip keycloak-jetty93-adapter-dist-2.5.0.Final.zip