Input data:
- eureka (localhost:8761)
- spring boot cloud gateway service with keycloak (localhost:8765)
- developer resource service (localhost:8082)
- kecloak (localhost:8080)
Keycloak:
- created realm
- created client with auth ON
- created 2 users with 2 different roles: developer and manager
- created 1 resource for path
/developer/**
(it's a prefix for my developer-service endpoint) - created role based policy for role=developer (required!)
- created resource permission based on policy above
Case: in browser making request
http://localhost:8765/developer/developers
logging in as manager
!!!
EXPECTED: access denied
ACTUAL: 200 with response = list of developers
QUESTION:
have i missed something ? Is this role permission filtration inside of keacloak already?
Have already watched several videos and posts, some of them are based on front-end keycloak-js
lib and filtration, backend @RolesAllowed
. I'm just curious if it's possible to block the request just using the keycloak admin console?
GATEWAY yaml:
server:
port: 8765
logging:
level:
root: info
eureka:
client:
serviceUrl:
defaultZone: http://eurekauser:eureka!@localhost:8761/eureka
instance:
hostname: localhost
prefer-ip-address: false
spring:
application:
name: GATEWAY
cloud:
gateway:
discovery.locator.enabled: true
routes:
- id: developer
uri: lb://DEVELOPER
predicates:
- Path=/developer/**
filters:
TokenRelay=
security:
oauth2:
client:
registration:
keycloak:
provider: keycloak
client-id: test_client
client-secret: lBIz3la07j3a5uEEFdQgoapFa4s1seeD
authorization-grant-type: authorization_code
redirect-uri: "http://localhost:${server.port}/login/oauth2/code/{registrationId}"
scope:
- openid
provider:
keycloak:
issuer-uri: http://localhost:8080/realms/TestRealm
authorization-uri: http://localhost:8080/realms/TestRealm/protocol/openid-connect/auth
token-uri: http://localhost:8080/realms/TestRealm/protocol/openid-connect/token
user-info-uri: http://localhost:8080/realms/TestRealm/protocol/openid-connect/userinfo
jwk-set-uri: http://localhost:8080/realms/TestRealm/protocol/openid-connect/certs
jackson:
date-format: yyyy-MM-dd HH:mm:ss
DEVELOPER SERVICE: YAML:
server:
port: 8082
error:
include-message: always
servlet:
context-path: /developer
spring:
application:
name: DEVELOPER
security:
oauth2:
resource-server:
jwt:
jwk-set-uri: http://localhost:8080/realms/TestRealm/protocol/openid-connect/certs
eureka:
client:
serviceUrl:
defaultZone: http://eurekauser:eureka!@localhost:8761/eureka
instance:
prefer-ip-address: false
hostname: localhost
Endpoint:
@RestController
@RequestMapping("/developers")
class DevelopController {
private val developers = mapOf(
Pair(1L, "developer#1"),
Pair(2L, "developer#2"),
Pair(3L, "developer#3"),
Pair(4L, "developer#4"),
Pair(5L, "developer#5")
)
@GetMapping
fun findAll(authentication: Authentication) = developers.entries
@GetMapping("/{developerId}")
fun findById(@PathVariable developerId: Long): String = developers[developerId] ?: let {
throw RuntimeException("Not found by id=$developerId")
}
}
CodePudding user response:
Role based access-control won't be achieved on access-token emission: an access-token is emitted for a given user on a specific client and can be used to authorize many requests to many resources.
Details for role-based access-control in Spring-boot resource-servers in the accepted answer to "Use Keycloak Spring Adapter with Spring Boot 3"
There is an authorization-service in Keycloak which you could use to centralize access-control, but:
- as Keycloak adapters for Spring are not ported to spring-security 6, it requires you to write your own client service and authentication filter
- this service is not called before the request reaches the resource-server, it is called by the resource-server to delegate the decision to grant an access to a resource for a request with a given access-token
- this is pretty inefficient compared to evaluating, on the resource-server(s), the roles contained in a JWT (which requires only one single request to Keycloak at startup): with authorization-service, a request is made to Keycloak for each and every request to resource-server (just like when introspection is used instead of JWT decoding)
- it is very Keycloak adherent: there is no standard around it and access-control rules must be defined in Keycloak => if Keycloak stops supporting it or if you have to switch to another OIDC authorization-server, you're screwed
- it is not easily testable (you can easily write unit-tests of Spring access-control rules with mocked identities on resource-server)
- it is, in my opinion, harder to figure out exactly what access-control rules apply to a specific resource when all rules are put at the same place, compared to method security with SpEL on
@Controller
method