MoinMoin Logo
  • Comments
  • Immutable Page
  • Menu
    • Navigation
    • RecentChanges
    • FindPage
    • Local Site Map
    • Help
    • HelpContents
    • HelpOnMoinWikiSyntax
    • Display
    • Attachments
    • Info
    • Raw Text
    • Print View
    • Edit
    • Load
    • Save
  • Login

Navigation

  • Start
  • Sitemap

Upload page content

You can upload content for the page named below. If you change the page name, you can also upload content for another page. If the page name is empty, we derive the page name from the file name.

File to load page content from
Page name
Comment

  • keycloak

Contents

  1. keycloak
    1. OIDC
    2. Steps setup realm
    3. Setup keycloak as service in Raspberry pi
    4. Keycloak 21.1.1 + SpringBoot 3.1 + Spring Security + AspectJ (AOP)

keycloak

Open Source Identity and Access Management.

  • https://www.keycloak.org/

OIDC

  • https://www.scottbrady91.com/OpenID-Connect/OpenID-Connect-Overview

OpenID Connect (OIDC) provides a simple identity layer on top of the OAuth 2.0 protocol, enabling Single Sign-On (SSO) and API access in one round trip. It brings the missing user authentication story and identity layer to OAuth.

Steps setup realm

   1 cd /tmp
   2 wget https://github.com/keycloak/keycloak/releases/download/14.0.0/keycloak-14.0.0.zip
   3 unzip -t keycloak-14.0.0.zip
   4 unzip keycloak-14.0.0.zip
   5 cd ~/tmp/keycloak-14.0.0/bin
   6 sh standalone.sh 
   7 http://localhost:8080/auth

Create admin user

  • http://localhost:8080/auth

  • Administration Console
  • User: admin
  • Password: admin
  • Password confirmation: admin
  • Click on Create

Create realm

  • http://localhost:8080/auth/admin/master/console/#/realms/master

  • login with admin:admin
  • http://localhost:8080/auth/admin/master/console/#/create/realm

  • Name: MyRealm

  • Enabled: On
  • Click on Create

Add user myuser

  • http://localhost:8080/auth/admin/master/console/#/realms/MyRealm

  • Go to Users
  • Click on Add user
  • Username: myuser
  • User enabled: ON
  • Save

Add user mysubtaskuser

  • http://localhost:8080/auth/admin/master/console/#/realms/MyRealm

  • Go to Users
  • Click on Add user
  • Username: mysubtaskuser
  • User enabled: ON
  • Save

Set user password myuser

  • http://localhost:8080/auth/admin/master/console/#/realms/MyRealm/users

  • Select user myuser
  • Select credentials tab
  • Password: mypwd
  • Password confirmation: mypwd
  • Temporary: off
  • Click on "Set Password"

Set user password mysubtaskuser

  • http://localhost:8080/auth/admin/master/console/#/realms/MyRealm/users

  • Select user mysubtaskuser
  • Select credentials tab
  • Password: mypwd2
  • Password confirmation: mypwd2
  • Temporary: off
  • Click on "Set Password"

Create role USER

  • http://localhost:8080/auth/admin/master/console/#/create/role/MyRealm

  • Add role USER to MyRealm

  • Role name: USER
  • Click on Save

Create role USERSUBTASK

  • http://localhost:8080/auth/admin/master/console/#/create/role/MyRealm

  • Add role USERSUBTASK to MyRealm

  • Role name: USERSUBTASK
  • Click on Save

Associate role to user myuser

  • http://localhost:8080/auth/admin/master/console/#/realms/MyRealm/users

  • select user myuser
  • select tab Role mappings
  • select USER role and click on add selected

Associate role to user mysubtaskuser

  • http://localhost:8080/auth/admin/master/console/#/realms/MyRealm/users

  • select user mysubtaskuser
  • select tab Role mappings
  • select USERSUBTASK role and click on add selected

Create keycloak client

  • http://localhost:8080/auth/admin/master/console/#/realms/MyRealm/clients

  • click on create
  • client id: curl_confidential
  • client protocol: openid-connect
  • root url: http://localhost:8080

  • Click on save
  • Clients Curl_confidential settings:
  • access-type: confidential
  • Should appear tab Credentials
  • Client authenticator: Client ID and secret
  • Click on "Regenerate Secret"
  • # 3a862f1b-6687-4f7a-8e04-be494fca99e0
  • Clients Curl_confidential Mappers Add builtin "realm roles", "groups"
  • add selected
  • For each map add "Add to userinfo"
  • Clients Curl_confidential Scope,
  • select full scope allowed: ON

client data

  • realm: MyRealm

  • user pwd: myuser mypwd
  • client id: curl_confidential
  • protocol: openid-connect
  • Curl_confidential settings:
  • access-type confidential
  • valid redirect url http://localhost:8080

  • tab credentials: regenerate secret 3a862f1b-6687-4f7a-8e04-be494fca99e0

Signout

  • http://localhost:8080/auth/realms/MyRealm/account/

cUrl calls to test keycloak

   1 ACCESS_TOKEN=$(curl -d 'client_id=curl_confidential' -d 'client_secret=3a862f1b-6687-4f7a-8e04-be494fca99e0' -d 'username=myuser' -d 'password=mypwd' -d 'grant_type=password' 'http://localhost:8080/auth/realms/MyRealm/protocol/openid-connect/token' | json_reformat | jq -r '.access_token')
   2 echo $ACCESS_TOKEN
   3 
   4 curl -X POST -d "access_token=$ACCESS_TOKEN" http://localhost:8080/auth/realms/MyRealm/protocol/openid-connect/userinfo | json_reformat
   5 
   6 curl -X GET -d "access_token=$ACCESS_TOKEN" http://localhost:8080/auth/realms/MyRealm/.well-known/openid-configuration | json_reformat 

   1 CLIENT_ID="curl_confidential"
   2 CLIENT_SECRET="3a862f1b-6687-4f7a-8e04-be494fca99e0"
   3 TOKEN=$(curl -d "client_id=$CLIENT_ID" -d "client_secret=$CLIENT_SECRET" -d 'username=myuser' -d 'password=mypwd' -d 'grant_type=password' 'http://localhost:8080/auth/realms/MyRealm/protocol/openid-connect/token' | json_reformat)
   4 
   5 ACCESS_TOKEN=$(echo $TOKEN | jq -r '.access_token')
   6 REFRESH_TOKEN=$(echo $TOKEN | jq -r '.refresh_token')
   7 
   8 curl -X POST -d "access_token=$ACCESS_TOKEN" http://localhost:8080/auth/realms/MyRealm/protocol/openid-connect/userinfo
   9 
  10 curl -vvv -d "client_id=$CLIENT_ID" -d "client_secret=$CLIENT_SECRET" -d "refresh_token=$REFRESH_TOKEN" -H "Bearer: $ACCESS_TOKEN" 'http://localhost:8080/auth/realms/MyRealm/protocol/openid-connect/logout'
  11 
  12 curl -X POST -d "access_token=$ACCESS_TOKEN" http://localhost:8080/auth/realms/MyRealm/protocol/openid-connect/userinfo
  13 #
  14 

Setup keycloak as service in Raspberry pi

  • /etc/init.d/keycloak

   1 #! /bin/sh
   2 ### BEGIN INIT INFO
   3 # Provides:          keycloak
   4 # Default-Start:     2 3 4 5
   5 # Default-Stop:
   6 # Short-Description: keycloak
   7 # Description:       keycloak
   8 ### END INIT INFO
   9 #
  10 # Some things that run always
  11 touch /var/lock/keycloak
  12 # Carry out specific functions when asked to by the system
  13 case "$1" in
  14   start)
  15     echo "Starting script keycloak "
  16     su pi -c "nohup /home/pi/keycloak-14.0.0/bin/standalone.sh &"
  17     ;;
  18   stop)
  19     echo "Stopping script keycloak"
  20     kill $(ps uax | grep keycloak | grep java | awk '//{print $2}')
  21     ;;
  22   status)
  23     echo "keycloak PID: $(ps uax | grep keycloak | grep java | awk '//{print $2}')"
  24     ;;
  25   *)
  26     echo "Usage: /etc/init.d/keycloak {start|stop|status}"
  27     exit 1
  28     ;;
  29 esac
  30 
  31 exit 0

Keycloak 21.1.1 + SpringBoot 3.1 + Spring Security + AspectJ (AOP)

   1 cd ~
   2 wget https://github.com/keycloak/keycloak/releases/download/21.1.1/keycloak-21.1.1.zip
   3 unzip keycloak-21.1.1.zip
   4 cd keycloak-21.1.1/bin
   5 bash kc.sh start 
   6 bash kc.sh show-config
   7 keytool -genkeypair -alias debian -keyalg RSA -keysize 2048 -validity 365 -keystore server.keystore -dname "cn=Server Administrator,o=Keycloak,c=PT" -keypass secret -storepass secret
   8 cp server.keystore ../conf
   9 ./kc.sh start-dev --hostname=debian --https-key-store-password=secret
  10 #Sign in to your account 
  11 #Master, Create realm, MyRealm , Create 
  12 #Users, Create new user, myuser, create 
  13 #select user, credentials, set password,  mypwd mypwd, temporary off , save, save password
  14 #Realm roles, create role, USER, save 
  15 #Users, myuser, role mapping, assign role USER 
  16 #signout
  17 #http://debian:8080/admin/master/console/#/MyRealm
  18 #My realm, clients, create client 
  19 #  client type: openid connect 
  20 #  client id:  curl_confidential
  21 #  next 
  22 #  client authentication: on 
  23 #  standard flow, direct access grants 
  24 #  next 
  25 #  valid redirect url http://localhost:8080
  26 #  save 
  27 #  tab credentials of curl_confidential 
  28 #  client secret regenerate -> Cymorm3jWN2b5z49dNASwPWwgY5zAsdV
  29   
  30 curl -d 'client_id=curl_confidential' -d 'client_secret=Cymorm3jWN2b5z49dNASwPWwgY5zAsdV' -d 'usr' -d 'password=mypwd' -d 'grant_type=password' 'http://localhost:8080/realms/MyRealm/protocol/openid-connect/token'
  31 sudo apt install jq 
  32 
  33 TOKEN=$(curl -d 'client_id=curl_confidential' -d 'client_secret=Cymorm3jWN2b5z49dNASwPWwgY5zAsdV' -d 'username=myuser' -d 'password=mypwd' -d 'grant_type=password' 'http://localhost:8080/realms/MyRealm/protocol/openid-connect/token')
  34 echo $TOKEN 
  35 
  36 mkdir -p ~/Documents/test-springboot-keycloak/proj
  37 cd ~/Documents/test-springboot-keycloak/proj
  38 touch pom.xml
  39 touch src/main/java/com/example/demo/UserRole.java
  40 touch src/main/java/com/example/demo/RolesAspect.java
  41 touch src/main/java/com/example/demo/SecurityConfiguration.java
  42 touch src/main/java/com/example/demo/AdminRole.java
  43 touch src/main/java/com/example/demo/DemoApplication.java
  44 touch src/main/resources/application.properties

pom.xml

   1 <?xml version="1.0" encoding="UTF-8"?>
   2 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   3         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
   4         <modelVersion>4.0.0</modelVersion>
   5         <parent>
   6                 <groupId>org.springframework.boot</groupId>
   7                 <artifactId>spring-boot-starter-parent</artifactId>
   8                 <version>3.1.0</version>
   9                 <relativePath/> <!-- lookup parent from repository -->
  10         </parent>
  11         <groupId>com.example</groupId>
  12         <artifactId>demo</artifactId>
  13         <version>0.0.1-SNAPSHOT</version>
  14         <name>demo</name>
  15         <description>Demo project for Spring Boot</description>
  16         <properties>
  17                 <java.version>17</java.version>
  18         </properties>
  19         <dependencies>
  20                 <dependency>
  21                         <groupId>org.springframework.boot</groupId>
  22                         <artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
  23                 </dependency>
  24                 <dependency>
  25                         <groupId>org.springframework.boot</groupId>
  26                         <artifactId>spring-boot-starter-security</artifactId>
  27                 </dependency>
  28                 <dependency>
  29                         <groupId>org.springframework.boot</groupId>
  30                         <artifactId>spring-boot-starter-web</artifactId>
  31                 </dependency>
  32                 <dependency>
  33                         <groupId>org.springframework.boot</groupId>
  34                         <artifactId>spring-boot-starter-aop</artifactId>
  35                 </dependency>                
  36         </dependencies>
  37         <build>
  38                 <plugins>
  39                         <plugin>
  40                                 <groupId>org.springframework.boot</groupId>
  41                                 <artifactId>spring-boot-maven-plugin</artifactId>
  42                         </plugin>
  43                 </plugins>
  44         </build>
  45 </project>

src/main/java/com/example/demo/UserRole.java

   1 package com.example.demo;
   2 
   3 import java.lang.annotation.Retention;
   4 import java.lang.annotation.Target;
   5 import java.lang.annotation.ElementType;
   6 import java.lang.annotation.RetentionPolicy;
   7 
   8 /**
   9  * Annotation to identify code associated with USER role
  10  * 
  11  */
  12 @Retention(RetentionPolicy.RUNTIME)
  13 @Target(ElementType.METHOD)
  14 public @interface UserRole {
  15 }

src/main/java/com/example/demo/RolesAspect.java

   1 package com.example.demo;
   2 
   3 import java.util.List;
   4 import java.util.ArrayList;
   5 import java.util.Map;
   6 
   7 import org.aspectj.lang.ProceedingJoinPoint;
   8 import org.aspectj.lang.annotation.Around;
   9 import org.aspectj.lang.annotation.Aspect;
  10 import org.springframework.stereotype.Component;
  11 import org.springframework.security.core.Authentication;
  12 import org.springframework.security.oauth2.jwt.Jwt;
  13 
  14 @Aspect
  15 @Component
  16 public class RolesAspect {
  17 
  18     private boolean hasRole(String role, ProceedingJoinPoint joinPoint) {
  19         if (joinPoint.getArgs() != null && joinPoint.getArgs().length >= 1) {
  20             Object authArg = joinPoint.getArgs()[0];
  21             if (Authentication.class.isAssignableFrom(authArg.getClass())) {
  22                 Jwt jwt = (Jwt) ((Authentication) authArg).getCredentials();
  23                 Map<String, Object> realmAccess = jwt.getClaimAsMap("realm_access");
  24                 List<String> roles = (ArrayList<String>) realmAccess.get("roles");
  25                 if (roles.contains(role)) {
  26                     return true;
  27                 }
  28             }
  29         }
  30 
  31         return false;
  32     }
  33 
  34     /**
  35      * Intercept stuff annotated with UserRole annotation
  36      * 
  37      * @param joinPoint
  38      * @return
  39      * @throws Throwable
  40      */
  41     @Around("@annotation(UserRole)")
  42     public Object interceptUserRoleAnnotation(ProceedingJoinPoint joinPoint) throws Throwable {
  43 
  44         if (hasRole("USER", joinPoint)) {
  45             return joinPoint.proceed();
  46         } else {
  47             System.out.println("USER role not found");
  48             return null;
  49         }
  50     }
  51 
  52     /**
  53      * Intercept stuff annotated with AdminRole annotation
  54      * 
  55      * @param joinPoint
  56      * @return
  57      * @throws Throwable
  58      */
  59     @Around("@annotation(AdminRole)")
  60     public Object interceptAdminRoleAnnotation(ProceedingJoinPoint joinPoint) throws Throwable {
  61 
  62         if (hasRole("ADMIN", joinPoint)) {
  63             return joinPoint.proceed();
  64         } else {
  65             System.out.println("ADMIN role not found");
  66             return null;
  67         }
  68     }
  69 }

src/main/java/com/example/demo/SecurityConfiguration.java

   1 package com.example.demo;
   2 
   3 import org.springframework.context.annotation.Bean;
   4 import org.springframework.context.annotation.Configuration;
   5 import org.springframework.security.config.annotation.web.builders.HttpSecurity;
   6 import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
   7 import org.springframework.security.web.SecurityFilterChain;
   8 
   9 @Configuration
  10 public class SecurityConfiguration {
  11 
  12         @Bean
  13         protected SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
  14                 return http.oauth2ResourceServer(
  15                                 (oauth2ResourceServer) -> {
  16                                         oauth2ResourceServer.jwt((jwt) -> {
  17                                                 jwt.decoder(null);
  18                                         });
  19                                 }).build();
  20         }
  21 
  22         @Bean
  23         public WebSecurityCustomizer webSecurityCustomizer() {
  24                 return (web) -> web.ignoring().requestMatchers("/otherhello**").requestMatchers("/static**");
  25         }
  26 }

src/main/java/com/example/demo/AdminRole.java

   1 package com.example.demo;
   2 
   3 import java.lang.annotation.Retention;
   4 import java.lang.annotation.Target;
   5 import java.lang.annotation.ElementType;
   6 import java.lang.annotation.RetentionPolicy;
   7 
   8 /**
   9  * Annotation to identify code associated with ADMIN role
  10  * 
  11  */
  12 @Retention(RetentionPolicy.RUNTIME)
  13 @Target(ElementType.METHOD)
  14 public @interface AdminRole {
  15 }

src/main/java/com/example/demo/DemoApplication.java

   1 package com.example.demo;
   2 
   3 import java.util.ArrayList;
   4 import java.util.List;
   5 import java.util.Map;
   6 
   7 import org.springframework.boot.SpringApplication;
   8 import org.springframework.boot.autoconfigure.SpringBootApplication;
   9 import org.springframework.web.bind.annotation.GetMapping;
  10 import org.springframework.web.bind.annotation.PathVariable;
  11 import org.springframework.web.bind.annotation.RestController;
  12 import org.springframework.security.core.Authentication;
  13 import org.springframework.security.core.GrantedAuthority;
  14 import org.springframework.security.oauth2.jwt.Jwt;
  15 
  16 @RestController
  17 @SpringBootApplication
  18 public class DemoApplication {
  19 
  20   public static void main(String[] args) {
  21     SpringApplication.run(DemoApplication.class, args);
  22   }
  23 
  24   @GetMapping("/otherhello")
  25   public String otherHello() {
  26     return "other hello";
  27   }
  28 
  29   @GetMapping("/hello")
  30   @UserRole
  31   public String hello(Authentication authentication) {
  32     String authorities = "";
  33     for (int authorityIndex = 0; authorityIndex < authentication.getAuthorities().size(); authorityIndex++) {
  34       GrantedAuthority ga = (GrantedAuthority) authentication.getAuthorities().toArray()[authorityIndex];
  35       authorities += ga.getAuthority() + " ";
  36     }
  37 
  38     Jwt jwt = (Jwt) authentication.getCredentials();
  39 
  40     Object[] keys = jwt.getClaims().keySet().toArray();
  41     String allkeys = "";
  42 
  43     String preferredUsername = jwt.getClaim("preferred_username").toString();
  44     Map<String, Object> realmAccess = jwt.getClaimAsMap("realm_access");
  45     List<String> roles = (ArrayList<String>) realmAccess.get("roles");
  46     System.out.println("Contains USER " + roles.contains("USER"));
  47 
  48     for (int j = 0; j < keys.length; j++) {
  49       allkeys = allkeys + " " + (String) keys[j] + ":" + jwt.getClaims().get(keys[j]).toString() + " ";
  50     }
  51 
  52     return "I am authenticated with user " + authentication.getName() + " Authorities: " + authorities + " Details: "
  53         + authentication.getDetails().toString() + " allKeys: " + allkeys + " ... " + preferredUsername + " ... "
  54         + roles;
  55   }
  56 
  57   @GetMapping("/helloAdmin")
  58   @AdminRole
  59   public String helloAdmin(Authentication authentication) {
  60     return "Hello ADMIN";
  61   }
  62 
  63   @GetMapping("/helloUser/{text}")
  64   @UserRole
  65   public String helloUser(Authentication authentication, @PathVariable String text) {
  66     return "Hello USER " + text;
  67   }
  68 }

src/main/resources/application.properties

   1 server.port=8081
   2 spring.security.oauth2.resourceserver.jwt.issuer-uri=http://debian:8080/realms/MyRealm
   3 #logging.level.root=DEBUG
   4 logging.level.root=INFO
   5 logging.file=/tmp/testout.log
  • MoinMoin Powered
  • Python Powered
  • GPL licensed
  • Valid HTML 4.01