【问题标题】:Consuming REST API secured with Keycloak as Broker for OAUTH2-Provider使用 Keycloak 保护的 REST API 作为 OAUTH2-Provider 的代理
【发布时间】:2016-10-11 03:24:41
【问题描述】:

我必须使用受 Keycloak 保护的应用程序的 REST API,它充当 OAUTH2-Provider 的代理。

为此,我使用 OAuth2RestTemplate 和 ResourceOwnerPasswordDetails。 我可以毫无问题地从第三方提供商处获得访问令牌,但我如何进一步使用它,这是个问题。在标头中使用它作为承载没有帮助。

有什么建议吗?

OAuthConfig.java

@Bean
public ResourceOwnerPasswordResourceDetails resource(){
ResourceOwnerPasswordResourceDetails resource = new ResourceOwnerPasswordResourceDetails();
resource.setClientAuthenticationScheme(AuthenticationScheme.form);
resource.setAccessTokenUri(env.getProperty("access.token.uri"));
resource.setClientId(env.getProperty("access.client.id"));
resource.setGrantType("password");
resource.setClientSecret(env.getProperty("access.client.secret"));
resource.setUsername(env.getProperty("access.client.username"));
resource.setPassword(env.getProperty("access.client.password"));
resource.setScope(Arrays.asList(env.getProperty("access.client.scope")));
return resource;
}

获取访问令牌的服务

@Autowired
private OAuthConfig authConfig;

@Override
public OAuth2AccessToken getAccessToken(){
ResourceOwnerPasswordAccessTokenProvider provider = new ResourceOwnerPasswordAccessTokenProvider();
OAuth2AccessToken accessToken = provider.obtainAccessToken(authConfig.resource(), new DefaultAccessTokenRequest());
return accessToken;
}

OAuth2RestTemplate

@Autowired private ProdAuthService authService;

OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(authConfig.resource(), new DefaultOAuth2ClientContext(authService.getAccessToken())); 
restTemplate.setRequestFactory(requestFactory);
restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
restTemplate.getMessageConverters().add(new StringHttpMessageConverter());
HttpHeaders header = new HttpHeaders();
header.setContentType(MediaType.APPLICATION_JSON);
header.set("Authorization", "Bearer " + authService.getAccessToken());
HttpEntity<String> request = new HttpEntity<String>(header);
ResponseEntity <ProcessInstanceLogWrapper> response = restTemplate.exchange(uri, HttpMethod.GET, request, new ParameterizedTypeReference<ProcessInstanceLogWrapper>(){});
ProcessInstanceLogWrapper json = response.getBody();

我得到一个错误

Caused by: org.springframework.security.oauth2.client.http.AccessTokenRequiredException: OAuth2 access denied.

在 Keycloak 中,我们也使用 Authorizaion URL,但似乎无法将其与 ResourceOwnerPasswordResourceDetails 一起使用。根据我的观点,它也可能是这种情况,它不起作用。

【问题讨论】:

  • 你成功了吗?
  • 没办法...至少,我还没有解决这个问题。如果您有同样的问题,最快但不幸的是不是最安全的解决方案可能是启用基本身份验证并使用它......我已经做到了......
  • 我设法让它工作。明天分享代码。
  • 听起来不错!我期待您的解决方案!

标签: java spring rest oauth2 keycloak


【解决方案1】:

RestTemplate 创建:

 protected RestKeyCloakClient()
{
    MultiValueMap<String, String> header = new LinkedMultiValueMap<String, String>();
    OAuth2RestTemplate client;
    DefaultAccessTokenRequest accessTokenRequest = new DefaultAccessTokenRequest();
    DefaultOAuth2ClientContext context = new DefaultOAuth2ClientContext(accessTokenRequest);
    OAuth2AccessTokenSupport support = new OAuth2AccessTokenSupport()
    {
    };
    List<HttpMessageConverter<?>> messageConverters = new ArrayList<HttpMessageConverter<?>>();
    messageConverters.add(new FormOAuth2AccessTokenMessageConverter());
    messageConverters.add(new FormOAuth2ExceptionHttpMessageConverter());
    MappingJackson2HttpMessageConverter jackson = new MappingJackson2HttpMessageConverter();
    List<MediaType> mediaTypes = new ArrayList<MediaType>();
    mediaTypes.add(new MediaType("application", "x-www-form-urlencoded"));
    jackson.setSupportedMediaTypes(mediaTypes);
    messageConverters.add(jackson);
    support.setMessageConverters(messageConverters);
    client = new OAuth2RestTemplate(getAuthDetails(null, null), context);
    client.setErrorHandler(errorHandler);
    client.setRequestFactory(factory);
    token = client.getAccessToken();
}

ResourceOwnerPasswordResourceDetails:

private ResourceOwnerPasswordResourceDetails getAuthDetails(String userName, String userPwd)
{

    TrustManager[] trustAllCerts = new TrustManager[] {new X509TrustManager()
    {
        @Override
        public java.security.cert.X509Certificate[] getAcceptedIssuers()
        {
            return null;
        }

        @Override
        public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType)
        {
        }

        @Override
        public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType)
        {
        }
    }};

    try {
        SSLContext sc = SSLContext.getInstance("SSL");
        sc.init(null, trustAllCerts, new java.security.SecureRandom());
        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
    } catch (Exception e) {
    }

    ResourceOwnerPasswordResourceDetails authDetails = new ResourceOwnerPasswordResourceDetails();
    authDetails.setAccessTokenUri(LoggerAndReader.getInstance().getoAuth2tokenRequestUrl());
    authDetails.setClientId(LoggerAndReader.getInstance().getoAuth2ClientId());
    authDetails.setClientSecret(LoggerAndReader.getInstance().getoAuth2SecretToken());
    authDetails.setGrantType(LoggerAndReader.getInstance().getOauth2granttype());
    if (StringUtils.isNotBlank(userName) && StringUtils.isNotBlank(userPwd)) {
        authDetails.setUsername(userName);
        authDetails.setPassword(userPwd);
    } else {
        authDetails.setUsername(LoggerAndReader.getInstance().getOauth2UserName());
        authDetails.setPassword(LoggerAndReader.getInstance().getOauth2password());
    }
    // authDetails.setScope(Arrays.asList(new String[] {"cn mail sn givenname uid employeeNumber"}));
    return authDetails;
}

执行和进一步使用:

public ResponseEntity<String> execute(String url, HttpMethod httpMethod, Object o)
{
    HttpEntity request = new HttpEntity(o, this.header);
    ResponseEntity<String> resp = null;
    this.header.set("Authorization", token.getTokenType() + " " + token.getValue());
    try {
        resp = this.client.exchange(url, httpMethod, request, String.class);
    } catch (Exception e) {
        String str = getStackTrace(e);
        if (StringUtils.containsIgnoreCase(str, "SocketTimeoutException")) {
            throw new KeycloakHTTPClientSocketException(
                "Got a SocketTimeoutException for URL:" + url + ", HTTPMethod:" + httpMethod);
        } else
            throw new Exception(...);
    }
    return resp;
}

要添加更多标题,您需要添加它们:

public void setThisHeaderValue(String key, String value)
{
    this.header.add(key, value);
}

【讨论】:

    猜你喜欢
    • 2018-04-07
    • 1970-01-01
    • 2021-03-08
    • 2018-08-11
    • 2019-12-07
    • 2020-08-27
    • 2020-07-29
    • 2018-01-04
    相关资源
    最近更新 更多