Hello i didn't found solution for my problem. In new OkHttp and Retrofit is some function:
HandshakeCertificates certificates = new HandshakeCertificates.Builder()
.addPlatformTrustedCertificates()
.addInsecureHost("192.168.0.150")
.build();
I am trying to connect my Android App to Spring Boot Server. This Server must use HTTPS - not my idea.
On this server i generated self-sign certyficate, but still i got error, now i don't have any ideas. Here is a full error:
java.security.cert.CertificateException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
Below i insert code of Retrofit generator:
public class ServiceGenerator {
public static final String API_BASE_URL = "https://192.168.0.150:8443/";
private static OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
// private static OkHttpClient httpClient = getUnsafeOkHttpClient();
private static Retrofit.Builder builder =
new Retrofit.Builder()
.baseUrl(API_BASE_URL)
.addConverterFactory(GsonConverterFactory.create());
private static Retrofit retrofit = builder.build();
public static <S> S createService(Class<S> serviceClass) {
return createService(serviceClass, null);
}
static public <S> S createService(
Class<S> serviceClass, final String authToken) {
if (!TextUtils.isEmpty(authToken)) {
AuthenticationInterceptor interceptor =
new AuthenticationInterceptor(authToken);
if (!httpClient.interceptors().contains(interceptor)) {
httpClient.addInterceptor(interceptor);
HandshakeCertificates certificates = new HandshakeCertificates.Builder()
.addPlatformTrustedCertificates()
.addInsecureHost("192.168.0.150")
.build();
httpClient.sslSocketFactory(certificates.sslSocketFactory(), certificates.trustManager());
OkHttpClient okHttpClient = httpClient.build();
builder.client(okHttpClient);
retrofit = builder.build();
}
}
return retrofit.create(serviceClass);
}
}
Request Code:
private void doLoginRequest() {
DeviceAPI deviceAPI = ServiceGenerator.createService(DeviceAPI.class);
Call<JWTResponse> call = deviceAPI.login(new Login(usernameEditText.getText().toString(), passwordEditText.getText().toString()));
call.enqueue(new Callback<JWTResponse>() {
#Override
public void onResponse(Call<JWTResponse> call, Response<JWTResponse> response) {
if (response.isSuccessful()) {
Toast.makeText(LoginActivity.this, response.body().toString(), Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(LoginActivity.this, response.message(), Toast.LENGTH_SHORT).show();
}
}
#Override
public void onFailure(Call<JWTResponse> call, Throwable t) {
System.out.println(t.getCause());
Toast.makeText(LoginActivity.this, t.getMessage() , Toast.LENGTH_SHORT).show();
}
});
}
Api Code:
public interface DeviceAPI {
#POST("api/auth/signin")
Call<JWTResponse> login(#Body Login login);
}
If you need more information give me feedback!
This example shows connecting twice to a host - Once with a valid HTTPS Handshake, and second using addInsecureHost. n.b. you won't get a handshake peer in the second because the handshake won't result in valid certificates.
The same will apply if you run against a dev server, so edit the example to use your devserver.
https://gist.github.com/yschimke/796e58a6152137bdcd7d2f9d63e26363
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.tls.HandshakeCertificates.Builder
fun main() {
val request = Request.Builder()
.url("https://httpbin.org/get")
.build()
var client = OkHttpClient();
var response = client.newCall(request).execute()
println(response.handshake?.peerPrincipal) // CN=httpbin.org
println(response.code)
val certificates = Builder()
.addInsecureHost("httpbin.org")
.build()
client = OkHttpClient.Builder().sslSocketFactory(certificates.sslSocketFactory(),
certificates.trustManager
).build();
response = client.newCall(request).execute()
println(response.handshake?.peerPrincipal) // null
println(response.code)
}
Related
I am searching for this error whole internet, but yet, only one stackoverflow entry with no answer or comment.
I am trying to use Retrofit 2. It is my first time using it. Here are my dependencies:
compile 'com.squareup.retrofit2:retrofit:2.1.0'
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
compile 'com.google.code.gson:gson:2.6.2'
I exclued any OkHttp libraries as Retrofit already uses it.
This is my request interface:
public interface LoginService {
#POST(HTTPService.AUTHENTICATIO_URL)
Call<User> login();
}
Next: my Service generator:
public class ServiceGenerator {
public static final String API_BASE_URL = HTTPService.BASE_URL;
private static OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
private static Retrofit.Builder builder =
new Retrofit.Builder()
.baseUrl(API_BASE_URL)
.addConverterFactory(GsonConverterFactory.create());
public static <S> S createService(Class<S> serviceClass, String username, String password, String roleId) {
if (username != null && password != null) {
String credentials = username+":"+password+":"+roleId;
final String basic =
"Basic " + Base64.encodeToString(credentials.getBytes(), Base64.NO_WRAP);
httpClient.addInterceptor(chain -> {
Request original = chain.request();
Request.Builder requestBuilder = original.newBuilder()
.header("Authorization", basic)
.header("Accept", "application/json")
.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(serviceClass);
}
}
And next: where I make the request:
#Override
public void loadData(DataSource.LoadJsonCallback loadJsonCallback) {
String login = mUser.getLogin();
String password = mUser.getPassword();
LoginService loginService =
ServiceGenerator.createService(LoginService.class, login, password, "");
Call<User> call = loginService.login();
String a = call.request().url().toString();
call.enqueue(new Callback<User>() {
#Override
public void onResponse(Call<User> call, Response<User> response) {
try {
loadJsonCallback.onTasksLoaded(response.body());
User a = response.body();
mUser = getDataFromJson("");
if (mUser != null) {
mUser.setPassword(password);
}
} catch (JSONException e) {
e.printStackTrace();
}
}
#Override
public void onFailure(Call<User> call, Throwable t) {
loadJsonCallback.onDataNotAvailable(t.getMessage());
}
});
}
So, I get this exception, that I cannot find anywhere:
java.lang.NoSuchMethodError: No virtual method newJsonReader(Ljava/io/Reader;)Lcom/google/gson/stream/JsonReader; in class Lcom/google/gson/Gson; or its super classes (declaration of 'com.google.gson.Gson' appears in /data/app/org.ucomplex.ucomplex-2/base.apk)
Thank you for any help.
I started using the newer versions:
compile 'com.google.code.gson:gson:2.7'
compile 'com.squareup.retrofit2:retrofit:2.2.0'
compile 'com.squareup.retrofit2:converter-gson:2.2.0'
And the error disappeared.
I have this class to create a service with retrofit2:
public class ServiceGenerator {
private static Retrofit.Builder builder = new Retrofit.Builder()
.baseUrl(UrlProvider.BASE_URL)
.addConverterFactory(GsonConverterFactory.create());
private static Retrofit retrofit = builder.build();
private static HttpLoggingInterceptor.Level logLevel = BuildConfig.DEBUG ?
HttpLoggingInterceptor.Level.BODY : HttpLoggingInterceptor.Level.NONE;
private static HttpLoggingInterceptor logging =
new HttpLoggingInterceptor().setLevel(logLevel);
private static OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
public static <S> S createService(Class<S> serviceClass) {
if (!httpClient.interceptors().contains(logging)) {
httpClient.addInterceptor(logging);
httpClient.addInterceptor(new Interceptor() {
#Override
public Response intercept(Interceptor.Chain chain) throws IOException {
Request original = chain.request();
Request request = original.newBuilder()
.header("HEADER_USER_AGENT", "USER_AGENT")
.method(original.method(), original.body())
.build();
return chain.proceed(request);
}
});
httpClient.authenticator(new TokenAuthenticator());
builder.client(httpClient.build());
retrofit = builder.build();
}
return retrofit.create(serviceClass);
}
}
I got this error in crash reporter:
java.lang.IllegalStateException: Null interceptor: [null, okhttp3.logging.HttpLoggingInterceptor#85a6f78, *.*.*.e.c#1f1feb6, *.*.*.e.c#66917b7]
at java.lang.Thread.run(Thread.java:764)
at *.*.*.S.run
at *.*.*.Y.a
at *.*.*.Y.e
at okhttp3.OkHttpClient$Builder.build(OkHttpClient.java:1040)
at okhttp3.OkHttpClient.<init>(OkHttpClient.java:283)
This error occurs on some devices and in a special case.
The probability of call createService method from different thread.
Does ExecuterService help us?
How to fix this error? help me please.
Getting this error when make api call in retrofit
Response{protocol=http/1.1, code=407, message=Proxy Authentication
Required, url=http://example.com/test.xml}
below was my api call.
private void showProxies() {
serviceCall = apiService.listOfServers();
serviceCall.enqueue(new Callback<ServerListModel>() {
#Override
public void onResponse(Call<ServerListModel> call, Response<ServerListModel> response) {
if (response.body() != null) {
ArrayList<Proxies> proxyArrayList = response.body().proxiesArrayList;
showProxyDialog(proxyArrayList);
}
else
Toast.makeText(BrowserActivity.this, "Server 407 error.", Toast.LENGTH_SHORT).show();
}
#SuppressLint("LongLogTag")
#Override
public void onFailure(Call<ServerListModel> call, Throwable t) {
Log.e(TAG, t.toString());
}
});
}
you have add proxy to you Retrofit object
java.net.Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyHost, proxyPort));
OkHttpClient client = new OkHttpClient.Builder().proxy(proxy).build();
Retrofit.Builder builder = new Retrofit.Builder().client(client);
Retrofit retrofit = builder.build();
In my case, I was connected to the proxy network this issue is not related to retrofit.
Im trying to authenticate to Cloudinary API service using the below code but i get 401 unauthorized error, it expects credentials in this format https://API_KEY:API_SECRET#..., when i substitute with actual values it works great with browser/postman but fails with retrofit2, below is my code.
// create and initialize retrofit2 client
public static OkHttpClient getClient(){
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(Level.BASIC);
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request().newBuilder()
.addHeader("API_KEY","API_SECRET")
.addHeader("Accept","Application/JSON").build();
return chain.proceed(request);
}
})
.addInterceptor(interceptor)
.build();
return client;
}
private static Retrofit retrofit = null;
public static Retrofit getClient(String baseUrl){
if (retrofit == null){
retrofit = new Retrofit.Builder()
.client(getClient())
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}
// Interface with get methods to access image resources
public interface CloudinaryService {
#GET("resources/image")
Call<imageresponse> getImageResource();
}
// Util class to make requests
public class ApiUtils {
private static final String BASE_URL = "http://api.cloudinary.com/v...";
public static CloudinaryService getImageService(){
return RetrofitClient.getClient(BASE_URL)
.create(CloudinaryService.class);
}
}
Any help fixing the error will be highly appreciated, not sure if need custom converter. thanks
***** Edit******
public static String credentials = Credentials.basic(API_KEY,API_SECRET);
OkHttpClient client = new OkHttpClient.Builder()
// .authenticator(new Authenticator() {
// #Override
// public Request authenticate(Route route, Response response) throws IOException {
//
// return response.request().newBuilder().header("Authorization", credentials).build();
// }
// })
.addInterceptor(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Request request = (chain.request().newBuilder()
.header("Accept","Application/JSON")
.header("Cache-Control", "public, max-age=" + 60)
.header("Authorization",credentials).build());
return chain.proceed(request);
}
})
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.addInterceptor(loggingInterceptor)
.addInterceptor(provideOfflineCacheInterceptor())
.addNetworkInterceptor(provideCacheInterceptor())
.cache(getCache())
.build();
return client;
}
I was able to fix the issue with adding authenticator to the builder.
.authenticator(new Authenticator() {
#Override
public Request authenticate(Route route, Response response) throws IOException {
return response.request().newBuilder().header("Authorization", credentials).build();
}
})
thanks for all your help.
request = chain.request();
builder = request.newBuilder();
if (TextUtils.isEmpty(request.header(AUTH)) && UserPreference.getInstance().isSignin())
builder.addHeader(AUTH, UserPreference.getInstance().getAccessToken());
if (NetUtil.hasNetwork(GridInnApplication.getInstance()))
builder.header(USER_AGENT, userAgent);
else
builder.cacheControl(CacheControl.FORCE_CACHE);
request = builder.build();
Response response = chain.proceed(request);
if (NetUtil.hasNetwork(GridInnApplication.getInstance())) {
String cacheControl = request.cacheControl().toString();
return response.newBuilder()
.header(CACHE_CONTROL, cacheControl)
.removeHeader(PRAGMA)
.build();
} else {
return response.newBuilder()
.addHeader(CACHE_CONTROL, CACHE_CONTROL_ONLY_CACHED)
.removeHeader(PRAGMA)
.build();
}
//you can results before returing intercept
The answer provided by leafNext will work but will cause every request to be sent twice - The authenticator only kicks in if the server responds with 401. You send the request, get 401 and then send it again with proper credentials.
The correct solution is to provide the credentials from the get go, using the interceptor. It's similar to what you tried to do originally, but you got the syntax wrong. The expected format is basic authentication.
.addInterceptor(new Interceptor() {
#Override
public Response intercept(Interceptor.Chain chain) throws IOException {
// Request customization: add request headers
return chain.proceed(chain.request().newBuilder()
.header("Authorization", credentials).build());
}
});
Where credentials should follow the basic authentication protocol: Assuming the Api key is key and the secret is secret, you base64-encode the expression key:secret and prefix it with Basic. In this example the value of credentials should end up like so:
Basic a2V5OnNlY3JldA==
Edit - Added a fully working independent code bit to verify basic auth is working for okhttp (and thus with retrofit when using okhttp):
public int testBasicAuth() throws IOException {
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Request request = (chain.request().newBuilder()
.header("Authorization",okhttp3.Credentials.basic(KEY, SECRET)).build());
return chain.proceed(request);
}
}).build();
Request request = new Request.Builder()
.url("https://api.cloudinary.com/v1_1/[cloud_name]/resources/image")
.build();
int code = client.newCall(request).execute().code();
return code; // 200
}
When I log into my account in my app, I save an auth token in my SharedPreferences, like this:
PreferenceUtils.setAuthToken(LoginActivity.this, authToken);
Here is my PreferenceUtils class:
public class PreferenceUtils {
public static SharedPreferences getSharedPreferences(Context context) {
return PreferenceManager.getDefaultSharedPreferences(context);
}
public static String getAuthToken(Context context) {
SharedPreferences sp = getSharedPreferences(context);
return sp.getString("auth_token", null);
}
public static void setAuthToken(Context context, final String token) {
SharedPreferences sp = getSharedPreferences(context);
sp.edit().putString("auth_token", token).apply();
}
}
When I log out of the account, I delete the auth token by calling the logOut() method in my UserUtils class:
public class UserUtils {
public static void logOut(Context context) {
SharedPreferences prefs = PreferenceUtils.getSharedPreferences(context);
SharedPreferences.Editor editor = prefs.edit();
editor.remove("auth_token");
editor.apply();
}
}
However, even after logging out of my account and removing the auth token from SharedPreferences, all Retrofit calls still somehow have the auth token saved and I'm not sure how.
In other words, when I log out of my account and Retrofit makes a new call, it will print out the auth token that I thought I had deleted when the user logged out.
Only when I restart my app does the auth token get fully removed.
Why is it doing this?
Here is my Retrofit client class (note the comment):
public class ApiClient {
public static final String API_BASE_URL = "https://www.example.com/";
private static OkHttpClient.Builder httpClient =
new OkHttpClient.Builder();
private static Retrofit.Builder builder =
new Retrofit.Builder()
.baseUrl(API_BASE_URL)
.addConverterFactory(GsonConverterFactory.create());
private static Retrofit retrofit = builder.build();
private static HttpLoggingInterceptor logging =
new HttpLoggingInterceptor()
.setLevel(HttpLoggingInterceptor.Level.BODY);
public static Retrofit getRetrofit() {
return retrofit;
}
public static <S> S createService(Class<S> serviceClass) {
if (!httpClient.interceptors().contains(logging)) {
httpClient.addInterceptor(logging);
builder.client(httpClient.build());
retrofit = builder.build();
}
return retrofit.create(serviceClass);
}
public static <S> S createService(Class<S> serviceClass, final String authToken) {
if (authToken != null) {
httpClient.addInterceptor(new Interceptor() {
#Override
public Response intercept(Interceptor.Chain chain) throws IOException {
Request original = chain.request();
// THIS STILL PRINTS THE AUTH TOKEN EVEN AFTER I'VE
// REMOVED IT FROM THE SHARED PREFERENCES
Log.d("AUTH TOKEN", authToken);
Request.Builder requestBuilder = original.newBuilder()
.header("Authorization", "Bearer " + authToken)
.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(serviceClass);
}
}
How do I fix this??
Most likely the reason you still are seeing the token is because while you delete the token from the share preferences, you never reset the variable in the program. You need to make sure that you set the variable to null or empty, not just delete it from shared preferences.
As you requested, here is my previous comment as an answer:
It's because the interceptor you added is still alive in the httpClient. Once you've removed the token (or pass a null to the createService() method, you need to remove the interceptor as well.