Spnego auth not working for response code driven http client - http

I am trying to write a http client which connects to a kerberos enabled tomcat(tested to be correct using browsers). It first gets the response code (which will be 401) and as according continue with its work.
The code is
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.*;
public class SampleHTTP2 {
static final String kuser = "correctusername"; // your account name
static final String kpass = "correctpassword"; // your password for the account
static class MyAuthenticator extends Authenticator {
public PasswordAuthentication getPasswordAuthentication() {
//System.out.println("I am reaching here");
// I haven't checked getRequestingScheme() here, since for NTLM
// and Negotiate, the username and password are all the same.
System.err.println("Feeding username and password for "
+ getRequestingScheme());
return (new PasswordAuthentication(kuser, kpass.toCharArray()));
}
}
public static void main(String[] args) throws Exception {
URL url = new URL("http://mycompname:6008/examples/");
HttpURLConnection h1 = (HttpURLConnection) url.openConnection();
int rescode = h1.getResponseCode();
System.out.println(rescode);
System.setProperty("sun.security.krb5.debug", "true");
System.setProperty("java.security.auth.login.config", "C:\\login2.conf");
System.setProperty("javax.security.auth.useSubjectCredsOnly","false");
System.setProperty("java.security.krb5.conf", "C:\\krb5.ini");
if(rescode == 401){
Authenticator.setDefault(new MyAuthenticator());
URL url2 = new URL("http://mycompname/examples/");
URLConnection h2 = url2.openConnection();
InputStream ins2 = h2.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(ins2));
String str;
while((str = reader.readLine()) != null)
System.out.println(str);
}
}
}
Now when i comment the line:-
int rescode = h1.getResponseCode();
and put if(true) instead of if(rescode ==401), it works.
I am not sure what is going wrong. getResponseCode() internally calls getinputStream and thus I have used a separate url connection. Even still it does not work
P.S - Server is perfectly set up and the Authenticator class is also correct.

Related

How to change client TLS preferences in Java?

I'm trying to make a POST request to an endpoint in Java, and when I try to send the request, I get the following error:
Caused by: javax.net.ssl.SSLHandshakeException: The server selected protocol version TLS10 is not accepted by client preferences [TLS13, TLS12]
This is what I have so far
Map<Object, Object> data = new HashMap<>();
data.put("username","foo");
data.put("password","bar");
String url = "https://google.com";
HttpRequest request = HttpRequest.newBuilder()
.POST(buildFormDataFromMap(data))
.uri(URI.create(url))
.build();
try{
HttpResponse<String> response = httpClient.send(request,
HttpResponse.BodyHandlers.ofString());
System.out.println(response.statusCode());
System.out.println(response.body());
} catch (Exception e){
e.printStackTrace();
}
Then when I run the code, the error gets thrown when sending the request/making the response object. My question is, if the TLS preferences are different for the server than the client, how can I change the preferences within Java so it can still make the request?
To solve this problem in jdk 11, I had to create an javax.net.ssl.SSLParameters object to enable "TLSv1", etc:
SSLParameters sslParameters = new SSLParameters();
sslParameters.setProtocols(new String[]{"TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3"});
Then create the HttpClient and add the sslParamters object:
HttpClient httpClient = HttpClient.newBuilder()
.sslParameters(sslParameters)
.build();
If you also want to disable hostname verification, add following code BEFORE HttpClient initialization;
final Properties props = System.getProperties();
props.setProperty("jdk.internal.httpclient.disableHostnameVerification", Boolean.TRUE.toString());
Also you can add a new TrustManager to trust all certificates (self signed).
To do so, add following code into your Class:
TrustManager[] trustAllCerts = new TrustManager[] {
new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
public void checkClientTrusted(
java.security.cert.X509Certificate[] certs, String authType) {
}
public void checkServerTrusted(
java.security.cert.X509Certificate[] certs, String authType) {
}
}
};
After this, you have to create an SSLContext object and add the TrustManger object:
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
And finally alter the HttpClient initialization like this:
httpClient = HttpClient.newBuilder()
.sslContext(sslContext)
.sslParameters(sslParameters)
.build()
Here is a complete Class example:
import java.net.http.HttpClient;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import java.util.Properties;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
public class HttpSSLClient {
private SSLContext sslContext;
private SSLParameters sslParameters;
private HttpClient httpClient;
public HttpSSLClient() throws KeyManagementException, NoSuchAlgorithmException {
sslParameters = new SSLParameters();
sslParameters.setProtocols(new String[]{"TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3"});
sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
final Properties props = System.getProperties();
props.setProperty("jdk.internal.httpclient.disableHostnameVerification", Boolean.TRUE.toString());
httpClient = HttpClient.newBuilder()
.sslContext(sslContext)
.sslParameters(sslParameters)
.build();
}
public HttpClient getHttplClient() {
return httpClient;
}
TrustManager[] trustAllCerts = new TrustManager[] {
new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
public void checkClientTrusted(
java.security.cert.X509Certificate[] certs, String authType) {
}
public void checkServerTrusted(
java.security.cert.X509Certificate[] certs, String authType) {
}
}
};
}
You can use the getHttplClient() function while calling your HttpRequest.
I had the same issue and this solution does not work for me.
Instead I saw this answer Android Enable TLSv1.2 in OKHttp and I tried this code:
ConnectionSpec spec = new ConnectionSpec
.Builder(ConnectionSpec.MODERN_TLS)
.tlsVersions(TlsVersion.TLS_1_2,TlsVersion.TLS_1_0,TlsVersion.TLS_1_1,TlsVersion.TLS_1_3).build();
client =client.newBuilder().connectionSpecs(Collections.singletonList(spec)).build();
And it worked for me:)
I think mmo's answer should be highlighted in bold. I had similar issue, but found out that the open-jdk jvm I was using has TLSv1 and TLSv1.1 as disabled in the jdk.tls.disabledAlgorithms line in java.security. So as soon as I removed it and restarted the JVM, I was able to connect usingthe older TLS protocols.
But please pay ATTENTION, This is not advisable in Production since it degrades the secure communication. So I'd say change it if you want at YOUR OWN RISK!!!

Spring RestTemplate does not allow for persistent https connections

I want to make use of persistent http connections using Spring RestTemplate when accessing a REST api over https. I cannot make it work; a new connection is created for each request and SSL handshake takes place each time.
Is it possible to have reusable connections over https with RestTemplate and if so, how to configure it?
I set up a RestTemplate to make requests over https. That works correctly.
However I notice in the logs that a new SSL handshake takes place with every request.
I set up a RestTemplate in a test as follows:
#Before
public void setupPersistentHttpConnectionBackedRestTemplate() {
final SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(
sslContext,
new String[] { "TLSv1.2" },
null,
SSLConnectionSocketFactory.getDefaultHostnameVerifier());
final Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("https", sslSocketFactory)
.build();
final PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(registry);
final CloseableHttpClient httpClient = HttpClients.custom()
.setSSLSocketFactory(sslSocketFactory)
.setConnectionManager(connectionManager)
.build();
final HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
requestFactory.setHttpClient(httpClient);
restTemplate.getRestTemplate().setRequestFactory(requestFactory);
}
Then I make several calls using this RestTemplate like this:
ResponseEntity<String> response = restTemplate.exchange("/tomcat/sleep?millis={millis}", HttpMethod.GET, HttpEntity.EMPTY, String.class, SLEEP_DURATION);
I investigated the code of spring-mvc and apache and notice the following.
In Spring RestTemplate execute method, a new request is created and then the request gets executed and the result returned.
ClientHttpRequest request = createRequest(url, method);
if (requestCallback != null) {
requestCallback.doWithRequest(request);
}
response = request.execute();
handleResponse(url, method, response);
return (responseExtractor != null ? responseExtractor.extractData(response) : null);
That in turn ends in calling HttpComponentsClientHttpRequestFactory where a new http context is created every time:
#Override
public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException {
HttpClient client = getHttpClient();
HttpUriRequest httpRequest = createHttpUriRequest(httpMethod, uri);
postProcessHttpRequest(httpRequest);
HttpContext context = createHttpContext(httpMethod, uri);
if (context == null) {
context = HttpClientContext.create();
}
...
When following the chain of calls during the request execute call, I end up in apache MainClientExec. There it tries to reuse a connection based on the route and the context user token. After the request is executed, the user token is retrieved from the context and stored for further lookup.
#Override
public CloseableHttpResponse execute(
final HttpRoute route,
final HttpRequestWrapper request,
final HttpClientContext context,
final HttpExecutionAware execAware) throws IOException, HttpException {
...
Object userToken = context.getUserToken();
final ConnectionRequest connRequest = connManager.requestConnection(route, userToken);
...
if (userToken == null) {
userToken = userTokenHandler.getUserToken(context);
context.setAttribute(HttpClientContext.USER_TOKEN, userToken);
}
if (userToken != null) {
connHolder.setState(userToken);
}
...
In the case of a https connection, the user token gets retrieved from the SSL principal, that in turn gets it from the SSL certificate:
#Override
public Object getUserToken(final HttpContext context) {
...
if (userPrincipal == null) {
final HttpConnection conn = clientContext.getConnection();
if (conn.isOpen() && conn instanceof ManagedHttpClientConnection) {
final SSLSession sslsession = ((ManagedHttpClientConnection) conn).getSSLSession();
if (sslsession != null) {
userPrincipal = sslsession.getLocalPrincipal();
}
}
}
public Principal getLocalPrincipal() {
if (this.cipherSuite.keyExchange != KeyExchange.K_KRB5 && this.cipherSuite.keyExchange != KeyExchange.K_KRB5_EXPORT) {
return this.localCerts == null ? null : this.localCerts[0].getSubjectX500Principal();
} else {
return this.localPrincipal == null ? null : this.localPrincipal;
}
}
The PoolingHttpClientConnectionManager tries to return reusable connections based on the route and the state (in which the user token has been stored).
But since the RestTemplate starts with a new request with a new context each time, the uset token is lost and the PoolingHttpClientConnectionManager can not find a reusable connection and thus creates a new one every time.
I would expect that the RestRemplate could create a request that re-uses that connection instead of creating a new one every time.
I was trying to achieve the same and the only way I see to do this is by extending HttpComponentsClientHttpRequestFactory to set a UserToken, in this case a Principal cert.getSubjectDN() then override createHttpContext(HttpMethod httpMethod, URI uri)
#Override
protected HttpContext createHttpContext(HttpMethod httpMethod, URI uri) {
HttpClientContext context = HttpClientContext.create();
context.setUserToken(userToken);
return context;
}

Single Instance of HttpClient with UseDefaultCredentials

I have been trying to update my API call using the suggestion from here
to only have 1 instance of HttpClient. https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/#
This works fine and I have my do not use up all of the ports, however when I try to create a HttpClientHandler to pass along the default credentials my ports start being used up again. My API is setup to use Windows Auth for security reasons so I need to pass along the app pools credentials for a successful call.
Here are the 2 code blocks
public static class WebApiCallUtility
{
private static HttpClientHandler _handlerNoCred = new HttpClientHandler();
private static HttpClient _clientNoCred = new HttpClient(_handlerNoCred);
private static HttpClientHandler _handlerCred = new HttpClientHandler { UseDefaultCredentials = true };
private static HttpClient _clientCred = new HttpClient(_handlerCred);
//Working ports are not used up
public static HttpResponseMessage SendHttpGetRequestNoCred(string webApiUrl, string logSourceName, string subId)
{
_clientNoCred.DefaultRequestHeaders.Accept.Clear();
_clientNoCred.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage result = _clientNoCred.GetAsync(webApiUrl).Result;
return result;
}
//No working tons of ports open hanging out with TIME_WAIT status
public static HttpResponseMessage SendHttpGetRequestCred(string webApiUrl, string logSourceName, string subId)
{
_clientCred.DefaultRequestHeaders.Accept.Clear();
_clientCred.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage result = _clientCred.GetAsync(webApiUrl).Result;
return result;
}
}
Any help would be appreciated.
Thanks
I was able to revolve this issue buy using a different handler type
private static WebRequestHandler _handlerCred = new WebRequestHandler
{
UseDefaultCredentials = true,
UnsafeAuthenticatedConnectionSharing = true
};
private static HttpClient _clientCred = new HttpClient(_handlerCred);
I found this answer here Static HttpClient still creating TIME_WAIT tcp ports

Enabling Cross Origin Requests for WebSockets in Spring

I have a OpenShift Wildfly server. I am building a website with the Spring MVC framework. One of my webpages also uses a WebSocket connection. On the server side, I have used the #ServerEndpoint annotation and javax.websocket.* library to create my websocket:
package com.myapp.spring.web.controller;
import java.io.IOException;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
import org.springframework.web.socket.server.standard.SpringConfigurator;
#ServerEndpoint(value="/serverendpoint", configurator = SpringConfigurator.class)
public class serverendpoint {
#OnOpen
public void handleOpen () {
System.out.println("JAVA: Client is now connected...");
}
#OnMessage
public String handleMessage (Session session, String message) throws IOException {
if (message.equals("ping")) {
// return "pong"
session.getBasicRemote().sendText("pong");
}
else if (message.equals("close")) {
handleClose();
return null;
}
System.out.println("JAVA: Received from client: "+ message);
MyClass mc = new MyClass(message);
String res = mc.action();
session.getBasicRemote().sendText(res);
return res;
}
#OnClose
public void handleClose() {
System.out.println("JAVA: Client is now disconnected...");
}
#OnError
public void handleError (Throwable t) {
t.printStackTrace();
}
}
OpenShift gives a default URL, so all of my webpages (html files) have the common (canonical) hostname. For the sake of simplicity, I am calling this URL URL A (projectname-domainname.rhclound.com). I created an alias, CNAME, of URL A, which is called URL B (say https://www.mywebsite.tech). URL B is secure, as it has the https.
I am using a JavaScript client to connect to the WebSocket at the path /serverendpoint. The URI I am using in my html webpage file, test.html, is the following:
var wsUri = "wss://" + "projectname-domainname.rhclound.com" + ":8443" + "/serverendpoint";
When I open up URL A (projectname-domainname.rhclound.com/test), the WebSocket connects and everything works fine. However, when I try to connect to the websocket using URL B (https://mywebsite.tech/test), the JavaScript client immediately connects and disconnects.
Here is the message from the console that I receive:
Here is my JavaScript code that connects to the WebSocket:
/****** BEGIN WEBSOCKET ******/
var connectedToWebSocket = false;
var responseMessage = '';
var webSocket = null;
function initWS() {
connectedToWebSocket = false;
var wsUri = "wss://" + "projectname-domainname.rhcloud.com" + ":8443" + "/serverendpoint";
webSocket = new WebSocket(wsUri); // Create a new instance of WebSocket using usUri
webSocket.onopen = function(message) {
processOpen(message);
};
webSocket.onmessage = function(message) {
responseMessage = message.data;
if (responseMessage !== "pong") { // Ping-pong messages to keep a persistent connection between server and client
processResponse(responseMessage);
}
return false;
};
webSocket.onclose = function(message) {
processClose(message);
};
webSocket.onerror = function(message) {
processError(message);
};
console.log("Exiting initWS()");
}
initWS(); //Connect to websocket
function processOpen(message) {
connectedToWebSocket = true;
console.log("JS: Server Connected..."+message);
}
function sendMessage(toServer) { // Send message to server
if (toServer != "close") {
webSocket.send(toServer);
} else {
webSocket.close();
}
}
function processClose(message) {
connectedToWebSocket = false;
console.log("JS: Client disconnected..."+message);
}
function processError(message) {
userInfo("An error occurred. Please contact for assistance", true, true);
}
setInterval(function() {
if (connectedToWebSocket) {
webSocket.send("ping");
}
}, 4000); // Send ping-pong message to server
/****** END WEBSOCKET ******/
After a lot of debugging and trying various things, I concluded that this was problem was occurring because of the Spring Framework. This is because before I introduced the Spring Framework in my project, URL B could connect to the WebSocket, but after introducing Spring, it cannot.
I read on spring's website about WebSocket Policy. I came across their same origin policy which states that an alias, URL B, cannot connect to the WebSocket because it is not the same origin as URL A is. To solve this problem I disabled the same origin policy with WebSockets as said in the documentation, so I added the following code. I thought that doing so would fix my error. Here is what I added:
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.socket.AbstractSecurityWebSocketMessageBrokerConfigurer;
#Configuration
public class WebSocketSecurityConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer {
#Override
protected boolean sameOriginDisabled() {
return true;
}
}
However, this did not fix the problem, so I added the following method to my ApplicationConfig which extends WebMvcConfigurerAdapter:
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedOrigins("https://www.mywebsite.com");
}
This also didn't work either. Then I tried this:
package com.myapp.spring.security.config;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
#Configuration
public class MyCorsFilter {
// #Bean
// public FilterRegistrationBean corsFilter() {
// System.out.println("Filchain");
// UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
// CorsConfiguration config = new CorsConfiguration();
// config.setAllowCredentials(true);
// config.addAllowedOrigin("https://www.mymt.tech");
// config.addAllowedHeader("*");
// config.addAllowedMethod("*");
// source.registerCorsConfiguration("/**", config);
// FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
// bean.setOrder(0);
// System.out.println("Filchain");
// return bean;
// }
#Bean
public CorsFilter corsFilter() {
System.out.println("Filchain");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true); // you USUALLY want this
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
config.addAllowedMethod("*");
source.registerCorsConfiguration("/**", config);
System.out.println("Filchain");
return new CorsFilter(source);
}
}
This also did not work.
I even changed the var wsURI in the JS code to the following:
var wsUri = "wss://" + "www.mywebsite.com" + ":8443" + "/serverendpoint";
Then var wsUri = "wss://" + "mywebsite.com" + ":8443" + "/serverendpoint";
When I did this, the Google Chrome gave me an error, saying that the handshake failed. However, when I have this URL, var wsUri = "wss://" + "projectname-domianname.rhcloud.com" + ":8443" + "/serverendpoint";, I did not get the error that the handshake didn't occur, but I get a message that the connection opened and closed immediately (as seen above).
So how can I fix this?
Have you tried implementing the WebMvcConfigurer and overriding the method addCorsMappings()? If not try this and see.
#EnableWebMvc
#Configuration
#ComponentScan
public class WebConfig implements WebMvcConfigurer {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("GET", "POST")
.allowedHeaders("Origin", "Accept", "Content-Type", "Authorization")
.allowCredentials(true)
.maxAge(3600);
}
}
I don't think it's a CORS issue because it's connected successully before being disconnected. If that's CORS, you can't even connect.
I think it's a communication problem between your DNS & openshift because WebSocket need a persistent connection (long-live) which keeps opening between client & server. If your DNS (e.g. CloudFlare or something like that) does not support / not configured to use WebSocket, the client would be disconnected immediately as in your issue.

Basic Authentication with Retrofit

I am trying to build a client for a REST API using Retrofit. The API uses basic auth and I have been unable to authenticate using Retrofit.
I tested the API using the curl below and it works as expected
curl -H "Accept: application/json" -H "Content-type: application/json" -X POST -d '{some_json}' -u api_key: https://apitest.com/api/v1/customers
Below is the Retrofit client
public interface UserService {
String HOST = "https://apitest.com";
public static OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
public static Retrofit.Builder builder =
new Retrofit.Builder()
.baseUrl(HOST)
.addConverterFactory(GsonConverterFactory.create());
/*
* CREATE/UPDATE User
*/
#POST("api/v1/customers")
Call<UserAPIResponse> userUpdate(#Body UserUpdateRequest userUpdateRequest);
static UserService newInstance(String userAPIKey) {
String credentials = userAPIKey + ":";
final String basic = "Basic "+ Base64.encodeBase64(credentials.getBytes());
httpClient.addInterceptor(new Interceptor() {
#Override
public Response intercept(Interceptor.Chain chain) throws IOException {
Request original = chain.request();
Request.Builder requestBuilder = original.newBuilder()
.header("Authorization", basic);
requestBuilder.header("Accept", "application/json");
requestBuilder.method(original.method(),original.body());
Request request = requestBuilder.build();
return chain.proceed(request);
}
});
OkHttpClient client = httpClient.build();
Retrofit retrofit = builder.client(client).build();
return retrofit.create(BlueshiftUserService.class);
}
When I call updateUser on the UserService
Response<UserAPIResponse> response = UserService.userUpdate(userUpdateRequest).execute();
The response.code is 401 (unauthorized/authentication failed)
The curl command with -u and the same credentials works as expected.
The issue was with the credentials encoding. I wasnt sending it as string.
byte[] encodedAuth= Base64.encodeBase64(credentials.getBytes());
final String basic = "Basic " + new String(encodedAuth);
use these libraries in Gradle file
compile 'com.squareup.retrofit:retrofit:1.9.0'
compile 'com.squareup.okhttp:okhttp:2.3.0'
compile 'com.cookpad.android.rxt4a:rxt4a:0.9.0'
compile 'io.reactivex:rxjava:1.0.12'
and put this classes in your project
public class ServiceGenerator {
private static final String TAG = erviceGenerator.class.getSimpleName();
public static final int READ_TIMEOUT = 10000;
public static final int CONNECT_TIMEOUT = 100000;
// No need to instantiate this class.
private ServiceGenerator(){}
public static <S> S createService(Class<S> serviceClass, String
endpoint) {
// Call basic auth generator method without user and pass
return createService(serviceClass, endpoint, null, null); }
public static <S> S createService(Class<S> serviceClass, String
endpoint, String username, String password) {
OkHttpClient okHttpClient = new OkHttpClient();
okHttpClient.setReadTimeout(READ_TIMEOUT, TimeUnit.SECONDS);
okHttpClient.setConnectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS);
// Set endpoint url and use OkHTTP as HTTP client
RestAdapter.Builder builder = new RestAdapter.Builder()
.setEndpoint(endpoint)
.setConverter(new GsonConverter(new Gson()))
.setClient(new OkClient(okHttpClient));
if (username != null && password != null) {
// Concatenate username and password with colon for authentication
final String credentials = username + ":" + password;
builder.setRequestInterceptor(new RequestInterceptor() {
#Override
public void intercept(RequestFacade request) {
// Create Base64 encoded string
String string = "Basic " + Base64.encodeToString(credentials.getBytes(), Base64.NO_WRAP);
request.addHeader("Authorization", string);
request.addHeader("Accept", "application/json");
}
});
}
RestAdapter adapter = builder.build();
return adapter.create(serviceClass); } }
and this interface
public class TodolyClient {
private static final String TAG = TodolyClient.class.getSimpleName();
public static final String ENDPOINT = "your base URL";
public interface TodolyService {
#GET("/wp-json/wc/v2/products")(your remaining url)
Observable<Object> isAuthenticated();
}
}
and call the below method in your main activity
private void createProject() {
final TodolyClient.TodolyService service =ServiceGenerator.createService(
TodolyClient.TodolyService.class, TodolyClient.ENDPOINT, "your user name",
"your password");
Observable<Object> observable = service.isAuthenticated();
AndroidCompositeSubscription compositeSubscription = new AndroidCompositeSubscription();
observable
.lift(new OperatorAddToCompositeSubscription<Object>(compositeSubscription))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Object>() {
#Override
public void onNext(Object project) {
android.util.Log.d(TAG, "onNext: "+project.toString());
}
#Override
public void onCompleted() {
android.util.Log.d(TAG, "onNext:commm " );
}
#Override
public void onError(Throwable e) {
android.util.Log.d(TAG, "onNext: eeeeeeeee"+e.getMessage());
}
});
}
This is so far the easiest method i have ever tried for "Basic Authentication".
Use the below code to generate the auth header (API/Repository class), You can add any character set for encoding as the third parameter here.
var basic = Credentials.basic("YOUR_USERNAME", "YOUR_PASSWORD")
Pass this as header to the webservice call (API/Repository class)
var retrofitCall = myWebservice.getNewsFeed(basic)
Add the basic header as parameter (Retrofit Webservice interface class)
#GET("newsfeed/daily")
fun getNewsFeed(#Header("Authorization") h1:String):Call<NewsFeedResponse>
Sorry, my code is in Kotlin, but can be easily translated to Java.
References: https://mobikul.com/basic-authentication-retrofit-android/

Resources