java - 如何使用 Spring Boot/Keycloak 验证后端到后端

标签 java spring-boot spring-security jwt keycloak

我正在尝试使用 Keycloak 和 Spring Boot 以及 Spring Security 和 JWT-tokens(Keycloak 中的仅承载设置)在面向微服务的应用程序的整个后端服务中实现身份验证。

我有一个后端服务需要身份验证才能访问 REST 端点。此服务为 Web UI 提供数据,并将数据存储在数据库中,以便稍后进行处理。 UI 中的用户身份验证以及针对该后端服务的 UI 都已经可以正常工作。

然后,还有另一个后端服务在后台运行,计算第一个提到的后端服务中也应该存在的值。由于该服务需要身份验证,因此执行计算的服务首先需要从 Keycloak 检索访问 token ,以针对其他后端服务进行身份验证,以便 HTTP post 正常工作。

我正在尝试使用 KeycloakRestTemplate 执行 HTTP post,但是当我调用 .postForObject 方法时,出现异常:

Caused by: java.lang.IllegalStateException: Cannot set authorization header because there is no authenticated principal
    at org.keycloak.adapters.springsecurity.client.KeycloakClientRequestFactory.getKeycloakSecurityContext(KeycloakClientRequestFactory.java:70)
    at org.keycloak.adapters.springsecurity.client.KeycloakClientRequestFactory.postProcessHttpRequest(KeycloakClientRequestFactory.java:55)
    at org.springframework.http.client.HttpComponentsClientHttpRequestFactory.createRequest(HttpComponentsClientHttpRequestFactory.java:160)

似乎计算服务在调用其他 REST 服务之前不会自动检索身份验证 token 。我在 Google 上对所有这些 Keycloak 特定类进行了大量研究,但没有找到我需要做什么。

有人可以给我提示吗? 我也不知道 Spring 配置的哪些部分在这里是相关的,但如果您需要它们,我会提供它们。

编辑

我的计算服务的 application.properties 如下所示:

keycloak.auth-server-url=https://localhost/auth
keycloak.realm=myrealm
keycloak.bearer-only=true
keycloak.resource=backend-service2
keycloak.principal-attribute=preferred_username
keycloak.cors=true
keycloak.realm-key=<PUBKEY>
keycloak.credentials.secret=<SECRET_UUID_STYLE>
keycloak.use-resource-role-mappings=true

更新

感谢@Sai prateek 和@Xtreme Biker。 这似乎将我引向了正确的方向。

我应用了这个解决方案,但我仍然遇到异常,我认为 keycloak 配置有误。我现在在 keycloak 中有三个客户端:webui、backend-service1、backend-service2。

webui配置为: 访问类型:公共(public)

后端服务 1 配置为: 接入类型:bearer-only

后端服务2配置为: 接入类型:bearer-only

异常(exception)情况是:

2019-02-18 11:15:32.914 DEBUG 22620 --- [  restartedMain] o.s.web.client.RestTemplate              : POST request for "http://localhost:<PORT>/auth/realms/<REALM_NAME>/protocol/openid-connect/token" resulted in 400 (Bad Request); invoking error handler

Exception in thread "restartedMain" java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49)
Caused by: error="access_denied", error_description="Access token denied."
    at org.springframework.security.oauth2.client.token.OAuth2AccessTokenSupport.retrieveToken(OAuth2AccessTokenSupport.java:142)
    at org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsAccessTokenProvider.obtainAccessToken(ClientCredentialsAccessTokenProvider.java:44)
    at org.springframework.security.oauth2.client.token.AccessTokenProviderChain.obtainNewAccessTokenInternal(AccessTokenProviderChain.java:148)
    at org.springframework.security.oauth2.client.token.AccessTokenProviderChain.obtainAccessToken(AccessTokenProviderChain.java:121)
    at org.springframework.security.oauth2.client.OAuth2RestTemplate.acquireAccessToken(OAuth2RestTemplate.java:221)
    at org.springframework.security.oauth2.client.OAuth2RestTemplate.getAccessToken(OAuth2RestTemplate.java:173)
    at org.springframework.security.oauth2.client.OAuth2RestTemplate.createRequest(OAuth2RestTemplate.java:105)
    at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:683)
    at org.springframework.security.oauth2.client.OAuth2RestTemplate.doExecute(OAuth2RestTemplate.java:128)
    at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:644)
    at org.springframework.web.client.RestTemplate.postForObject(RestTemplate.java:399)
[STRIPPED]


    ... 5 more
Caused by: error="invalid_client", error_description="Bearer-only not allowed"
    at org.springframework.security.oauth2.common.exceptions.OAuth2ExceptionJackson2Deserializer.deserialize(OAuth2ExceptionJackson2Deserializer.java:80)
    at org.springframework.security.oauth2.common.exceptions.OAuth2ExceptionJackson2Deserializer.deserialize(OAuth2ExceptionJackson2Deserializer.java:33)
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4001)
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3072)
    at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:237)
    at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readInternal(AbstractJackson2HttpMessageConverter.java:217)
    at org.springframework.http.converter.AbstractHttpMessageConverter.read(AbstractHttpMessageConverter.java:198)
    at org.springframework.security.oauth2.client.token.OAuth2AccessTokenSupport$AccessTokenErrorHandler.handleError(OAuth2AccessTokenSupport.java:237)
    at org.springframework.web.client.ResponseErrorHandler.handleError(ResponseErrorHandler.java:63)
    at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:730)
    at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:688)
    at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:654)
    at org.springframework.security.oauth2.client.token.OAuth2AccessTokenSupport.retrieveToken(OAuth2AccessTokenSupport.java:137)
    ... 18 more

另请注意,我更改了 keycloak.auth-server-urlhttp://localhost:<PORT>/auth (无 HTTPS)因此证书验证不会因开发中的自签名证书而失败。

最佳答案

好的,我自己找到了解决方案:我需要在客户端配置中为 keycloak 中的“backend-service2”将开关“启用服务帐户”按钮设置为 ON。

关于java - 如何使用 Spring Boot/Keycloak 验证后端到后端,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54712132/

相关文章:

Java:获取Iterable中给定元素之前或之后的元素

java - 当我滚动放置的图像时,自行创建的图形消失

java - 如何使用 Gradle 构建依赖于另一个 JAR 的 JAR

java - Spring Boot 应用程序无法实例化类,除非使用 Autowired 注解

java - 自定义 UserDetailsS​​ervice 似乎没有 Autowiring

java - Spring - 使用 hibernate 和 spring security 扩展类

java - Swing 中模态对话框的正确父级

java - Hsql - 如果在 spring boot 应用程序中不存在则创建模式

java - Spring 集成测试中 ContextRefreshedEvent 过早触发

java - 如何将盐传递给 Spring?