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
Unknown action login.
  • 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)
    5. Keycloak 26.3.0 + SpringBoot 3.5.3 + 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

  • master

  • login with admin:admin
  • realm

  • Name: MyRealm

  • Enabled: On
  • Click on Create

Add user myuser

  • MyRealm

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

Add user mysubtaskuser

  • MyRealm

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

Set user password myuser

  • users

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

Set user password mysubtaskuser

  • users

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

Create role USER

  • MyRealm

  • Add role USER to MyRealm

  • Role name: USER
  • Click on Save

Create role USERSUBTASK

  • MyRealm

  • Add role USERSUBTASK to MyRealm

  • Role name: USERSUBTASK
  • Click on Save

Associate role to user myuser

  • users

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

Associate role to user mysubtaskuser

  • users

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

Create keycloak client

  • 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

  • signout

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>

UserRole.java

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

RolesAspect.java

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

SecurityConfiguration.java

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

AdminRole.java

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

DemoApplication.java

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

application.properties

   1 #  src/main/resources/application.properties 
   2 server.port=8081
   3 spring.security.oauth2.resourceserver.jwt.issuer-uri=http://debian:8080/realms/MyRealm
   4 #logging.level.root=DEBUG
   5 logging.level.root=INFO
   6 logging.file=/tmp/testout.log

Keycloak 26.3.0 + SpringBoot 3.5.3 + Spring Security + AspectJ (AOP)

   1 cd ~
   2 sudo apt install jq 
   3 rm -rf keycloak-26.3.0
   4 rm keycloak-26.3.0.tar.gz
   5 wget https://github.com/keycloak/keycloak/releases/download/26.3.0/keycloak-26.3.0.tar.gz
   6 tar xvzf keycloak-26.3.0.tar.gz
   7 cd keycloak-26.3.0/conf/
   8 sed -i 's/#hostname/hostname/g' keycloak.conf 
   9 sed -i 's/myhostname/debian/g' keycloak.conf 
  10 cd ../bin
  11 # create keycloak server keystore for hostname debian
  12 keytool -genkeypair -alias debian -keyalg RSA -keysize 2048 -validity 365 -keystore server.keystore -dname "cn=debian,o=Keycloak,c=PT" -keypass secret -storepass secret
  13 cp server.keystore ../conf
  14 
  15 ./kc.sh start --verbose --https-key-store-password=secret

Create a temporary administrative user

  • Go to https://debian:8443/

  • admin, 12345678, 12345678, create user
  • Open administration console

Create realm

  • realms

  • Click Create realm
  • Realm name: TestRealm

  • enabled: true
  • click on create

Create user

  • users

  • Create new user,
  • username: myuser
  • click create
  • select user myuser,
  • email verified: true
  • tab credentials
  • click set password
  • password mypwd
  • password confirmation: mypwd
  • temporary off
  • save
  • save password

Create roles

  • roles

  • click on create role
  • Role name: USER
  • save
  • click on create role
  • Role name: ADMIN
  • save

Map role to user

  • users

  • Select myuser
  • tab role mapping
  • assign realm role USER
  • click on signout

Create client

  • clients

  • clients list
  • click create client
  • Client type: openid connect
  • client id: oidc_test_client
  • next
  • client authentication: on
  • authentication flow: standard flow, direct access grants
  • next
  • valid redirect url http://localhost:8080

  • save
  • tab credentials of oidc_test_client
  • view client secret: F5yuRxN1kbMxI6oVISFlART276Ddi8vi

Disable required actions for realm

  • required-actions

  • set all off

Get token after authentication

   1 CLIENT_ID=oidc_test_client 
   2 CLIENT_SECRET=F5yuRxN1kbMxI6oVISFlART276Ddi8vi
   3 REALM=TestRealm
   4 
   5 curl --noproxy '*' -k -d "client_id=$CLIENT_ID" -d "client_secret=$CLIENT_SECRET" -d 'username=myuser' -d 'password=mypwd' -d 'grant_type=password' "https://debian:8443/realms/$REALM/protocol/openid-connect/token"
   6 
   7 TOKEN=$(curl --noproxy '*' -k -d "client_id=$CLIENT_ID" -d "client_secret=$CLIENT_SECRET" -d 'username=myuser' -d 'password=mypwd' -d 'grant_type=password' "https://debian:8443/realms/$REALM/protocol/openid-connect/token")
   8 echo $TOKEN 

Spring boot test project

   1 rm -rf ~/Documents/test-springboot-keycloak
   2 mkdir -p ~/Documents/test-springboot-keycloak/src/main/java/com/example/demo/
   3 mkdir -p ~/Documents/test-springboot-keycloak/src/main/resources
   4 cd ~/Documents/test-springboot-keycloak
   5 touch pom.xml
   6 touch src/main/java/com/example/demo/UserRole.java
   7 touch src/main/java/com/example/demo/RolesAspect.java
   8 touch src/main/java/com/example/demo/SecurityConfiguration.java
   9 touch src/main/java/com/example/demo/AdminRole.java
  10 touch src/main/java/com/example/demo/DemoApplication.java
  11 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.5.3</version>
   9     <relativePath/> <!-- lookup parent from repository -->
  10   </parent>
  11   <groupId>com.example</groupId>
  12   <artifactId>demo</artifactId>
  13   <version>0.0.2-SNAPSHOT</version>
  14   <name>demo</name>
  15   <description>Demo project for Spring Boot</description>
  16   <properties>
  17     <java.version>21</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>

application.properties

   1 # src/main/resources/application.properties
   2 server.port=8081
   3 server.ssl.enabled=false
   4 spring.security.oauth2.resourceserver.jwt.issuer-uri=https://debian:8443/realms/TestRealm
   5 #logging.level.root=DEBUG
   6 logging.level.root=INFO
   7 logging.file=/tmp/testout.log

AdminRole.java

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

DemoApplication.java

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

RolesAspect.java

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

SecurityConfiguration.java

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

UserRole.java

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

get keycloak server cert debian.pem

   1 # debian.pem
   2 rm src/main/resources/truststore.p12
   3 
   4 openssl s_client -showcerts -connect debian:8443 </dev/null > debian.pem
   5 nano debian.pem # remove lines before BEGIN CERTIFICATE and after END CERTIFICATE
   6 # pwd secret
   7 keytool -import -alias debian -keystore src/main/resources/truststore.p12 -file debian.pem -storepass secret

Build and run

   1 mvn clean install 
   2 
   3 cd target
   4 cp ../src/main/resources/truststore.p12 .
   5 java -jar demo-0.0.2-SNAPSHOT.jar

Call secure endpoint

   1 CLIENT_ID=oidc_test_client 
   2 CLIENT_SECRET=F5yuRxN1kbMxI6oVISFlART276Ddi8vi
   3 REALM=TestRealm
   4 TOKEN=$(curl -k -d "client_id=$CLIENT_ID" -d "client_secret=$CLIENT_SECRET" -d 'username=myuser' -d 'password=mypwd' -d 'grant_type=password' "https://localhost:8443/realms/$REALM/protocol/openid-connect/token")
   5 echo $TOKEN 
   6 ACCESS_TOKEN=$(echo $TOKEN | jq -r '.access_token')
   7 echo $ACCESS_TOKEN
   8 curl -X GET http://localhost:8081/hello  --header "Authorization: Bearer $ACCESS_TOKEN"
   9 # insecure endpoint
  10 curl localhost:8081/otherhello
  • MoinMoin Powered
  • Python Powered
  • GPL licensed
  • Valid HTML 4.01