我试图在面向微服务的应用程序的后端服务中实现身份验证,使用带有Security和JWT的Keycloak和Spring (Keycloak中的只承载设置)。
我有一个后端服务,它需要身份验证才能访问其他端点。此服务为web提供数据,并将数据存储在数据库中,以便以后处理。UI中的用户身份验证和针对该后端服务的UI都已经工作。
然后,在后台运行另一个后端服务,计算在前面提到的后端服务中也应该出现的值。因为需要身份验证,所以首先执行计算的服务需要从Keycloak检索访问令牌,以便针对另一个后端服务进行身份验证,以便HTTP工作。
我试图用KeycloakRestTemplate来做HTTP,但是当我调用.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服务之前,计算服务似乎不会自动检索身份验证令牌。我在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更新
谢谢,西普拉提克和特雷比克。这似乎把我引向了正确的方向。
我应用了这个解决方案,但是我还是得到了一个例外,我认为keycloak配置是错误的。我现在有三个穿着keycloak的客户端: webui,后端服务1,后端服务2。
webui配置为: Access : public
后端服务1配置为:访问类型:仅承载
后端服务2配置为:访问类型:仅承载
例外是:
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-url更改为http://localhost:<PORT>/auth (没有HTTPS),这样证书验证就不会因为开发中的自签名证书而失败。
发布于 2019-03-04 17:36:56
好的,我自己找到了解决方案:我需要在keycloak内部的“后端服务2”的客户端配置中将“服务帐户启用”按钮设置为ON。
发布于 2019-02-15 15:42:23
看起来您缺少了一些授权服务器的配置。KeycloakRestTemplate使用客户端ID、客户端机密、用户名和密码对密钥披风服务器进行验证。您需要将clientid、clientsecret、realm和身份验证服务器url设置为KeycloakClientCredentialsRestTemplate,例如-
@Service
public class MyKeycloakClientCredentialsConfig {
@Value("${keycloak.realm}")
private String realm;
@Value("${keycloak.auth-server-url}")
private String authServerUrl;
@Value("${keycloak.resource}")
private String clientId;
@Value("${keycloak.credentials.secret}")
private String clientSecret;
@Bean
public KeycloakClientCredentialsRestTemplate createRestTemplate() {
return new KeycloakClientCredentialsRestTemplate(getClientCredentialsResourceDetails(),
new DefaultOAuth2ClientContext());
}
private ClientCredentialsResourceDetails getClientCredentialsResourceDetails() {
String accessTokenUri = String.format("%s/realms/%s/protocol/openid-connect/token",
authServerUrl, realm);
List<String> scopes = new ArrayList<String>(0); // TODO introduce scopes
ClientCredentialsResourceDetails clientCredentialsResourceDetails =
new ClientCredentialsResourceDetails();
clientCredentialsResourceDetails.setAccessTokenUri(accessTokenUri);
clientCredentialsResourceDetails.setAuthenticationScheme(AuthenticationScheme.header);
clientCredentialsResourceDetails.setClientId(clientId);
clientCredentialsResourceDetails.setClientSecret(clientSecret);
clientCredentialsResourceDetails.setScope(scopes);
return clientCredentialsResourceDetails;
}
}我的this模板是这样的-
public class SampleRestTemplate extends OAuth2RestTemplate {
public KeycloakClientCredentialsRestTemplate(OAuth2ProtectedResourceDetails resource,
OAuth2ClientContext context) {
super(resource, context);
}
}它对我来说是完美的。
https://stackoverflow.com/questions/54712132
复制相似问题