/* * -----------------------------------------------------------------------\ * PerfCake *   * Copyright (C) 2010 - 2017 the original author or authors. *   * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * -----------------------------------------------------------------------/ */ package org.perfcake.message.sender; import org.perfcake.message.Message; import org.perfcake.util.Utils; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; import java.util.Properties; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; /** * Allows token authentication to an HTTP based service. * * @author <a href="mailto:marvenec@gmail.com">Martin Večeřa</a> */ public class OauthHttpSender extends HttpSender { /** * URL of the server granting us a token. Default value works with Keycloak, supposing your realm name is demo. */ private String tokenServerUrl = "http://127.0.0.1:8180/auth/realms/demo/protocol/openid-connect/token"; /** * Data to send to the server to request a token. Default value works with Keycloak. */ private String tokenServerData = "grant_type=password&client_id=jboss-javaee-webapp&username=marvec&password=abc123"; /** * RegEx to create a capture group around the token value in the server's response. */ private String responseParser = ".*\"access_token\":\"([^\"]*)\".*"; /** * Header where the token is passed to the target service. The default value works with Keycloak. */ private String oauthHeader = "Authorization"; /** * String formatting the value of authorization header. The token is placed instead of %s. The default value works with Keycloak. */ private String oauthHeaderFormat = "Bearer %s"; /** * How long is a token valid before a new one is needed. Defaults to 60s. */ private long tokenTimeout = 60000L; /** * The cached token value. */ private String token = ""; /** * Time when the token was last updated. */ private long tokenUpdated = 0; void obtainToken() throws IOException { final HttpURLConnection http = (HttpURLConnection) new URL(tokenServerUrl).openConnection(); final byte[] data = tokenServerData.getBytes(Utils.getDefaultEncoding()); http.setRequestMethod("POST"); http.setDoOutput(true); http.setDoInput(true); http.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); http.setRequestProperty("Content-Length", Integer.toString(data.length)); http.connect(); http.getOutputStream().write(data); http.getOutputStream().flush(); http.getOutputStream().close(); String response = ""; try (BufferedReader buffer = new BufferedReader(new InputStreamReader(http.getInputStream()))) { response = buffer.lines().collect(Collectors.joining("\n")); } http.disconnect(); Pattern p = Pattern.compile(responseParser); Matcher m = p.matcher(response); m.find(); token = m.group(1); tokenUpdated = System.currentTimeMillis(); } private void setRequestHeader(final HttpURLConnection http) { http.setRequestProperty(oauthHeader, String.format(oauthHeaderFormat, token)); } private void refreshToken() throws IOException { if (token == null || token.isEmpty() || System.currentTimeMillis() > tokenUpdated + tokenTimeout) { obtainToken(); } } @Override public void preSend(final Message message, final Properties messageAttributes) throws Exception { super.preSend(message, messageAttributes); refreshToken(); setRequestHeader(requestConnection); } public String getTokenServerUrl() { return tokenServerUrl; } public void setTokenServerUrl(final String tokenServerUrl) { this.tokenServerUrl = tokenServerUrl; } public String getTokenServerData() { return tokenServerData; } public void setTokenServerData(final String tokenServerData) { this.tokenServerData = tokenServerData; } public String getResponseParser() { return responseParser; } public void setResponseParser(final String responseParser) { this.responseParser = responseParser; } public String getOauthHeader() { return oauthHeader; } public void setOauthHeader(final String oauthHeader) { this.oauthHeader = oauthHeader; } public String getOauthHeaderFormat() { return oauthHeaderFormat; } public void setOauthHeaderFormat(final String oauthHeaderFormat) { this.oauthHeaderFormat = oauthHeaderFormat; } public long getTokenTimeout() { return tokenTimeout; } public void setTokenTimeout(final long tokenTimeout) { this.tokenTimeout = tokenTimeout; } }