There are two ways to make a Retrofit call synchronous (with methods, returning values) and asynchronous (with callbacks).
Second one, async, works great out-of-the-box. But there is an issue, when it comes to OAuth2 authenticated access.
Can you recommend me a good RestAdapter, compatible with asynchronous retrofit calls.
I tried to use interceptors as follows, but it makes network calls on the main thread, which is not sufficient to me (Android). I am trying to use the following code (not mine).
public class SecuredRestBuilder extends RestAdapter.Builder {
private class OAuthHandler implements RequestInterceptor {
private boolean loggedIn;
private Client client;
private String tokenIssuingEndpoint;
private String username;
private String password;
private String clientId;
private String clientSecret;
private String accessToken;
public OAuthHandler(Client client, String tokenIssuingEndpoint, String username,
String password, String clientId, String clientSecret) {
super();
this.client = client;
this.tokenIssuingEndpoint = tokenIssuingEndpoint;
this.username = username;
this.password = password;
this.clientId = clientId;
this.clientSecret = clientSecret;
}
/**
* Every time a method on the client interface is invoked, this method is
* going to get called. The method checks if the client has previously obtained
* an OAuth 2.0 bearer token. If not, the method obtains the bearer token by
* sending a password grant request to the server.
*
* Once this method has obtained a bearer token, all future invocations will
* automatically insert the bearer token as the "Authorization" header in
* outgoing HTTP requests.
*
*/
#Override
public void intercept(RequestFacade request) {
// If we're not logged in, login and store the authentication token.
if (!loggedIn) {
try {
// This code below programmatically builds an OAuth 2.0 password
// grant request and sends it to the server.
// Encode the username and password into the body of the request.
FormUrlEncodedTypedOutput to = new FormUrlEncodedTypedOutput();
to.addField("username", username);
to.addField("password", password);
// Add the client ID and client secret to the body of the request.
to.addField("client_id", clientId);
to.addField("client_secret", clientSecret);
// Indicate that we're using the OAuth Password Grant Flow
// by adding grant_type=password to the body
to.addField("grant_type", "password");
// The password grant requires BASIC authentication of the client.
// In order to do BASIC authentication, we need to concatenate the
// client_id and client_secret values together with a colon and then
// Base64 encode them. The final value is added to the request as
// the "Authorization" header and the value is set to "Basic "
// concatenated with the Base64 client_id:client_secret value described
// above.
String base64Auth = BaseEncoding.base64().encode(new String(clientId + ":" + clientSecret).getBytes());
// Add the basic authorization header
List<Header> headers = new ArrayList<Header>();
headers.add(new Header("Authorization", "Basic " + base64Auth));
// Create the actual password grant request using the data above
Request req = new Request("POST", tokenIssuingEndpoint, headers, to);
// Request the password grant.
Response resp = client.execute(req);
// Make sure the server responded with 200 OK
if (resp.getStatus() < 200 || resp.getStatus() > 299) {
// If not, we probably have bad credentials
throw new SecuredRestException("Login failure: "
+ resp.getStatus() + " - " + resp.getReason());
} else {
// Extract the string body from the response
String body = IOUtils.toString(resp.getBody().in());
// Extract the access_token (bearer token) from the response so that we
// can add it to future requests.
accessToken = new Gson().fromJson(body, JsonObject.class).get("access_token").getAsString();
// Add the access_token to this request as the "Authorization"
// header.
request.addHeader("Authorization", "Bearer " + accessToken);
// Let future calls know we've already fetched the access token
loggedIn = true;
}
} catch (Exception e) {
throw new SecuredRestException(e);
}
}
else {
// Add the access_token that we previously obtained to this request as
// the "Authorization" header.
request.addHeader("Authorization", "Bearer " + accessToken );
}
}
private String username;
private String password;
private String loginUrl;
private String clientId;
private String clientSecret = "";
private Client client;
#Override
public RestAdapter build() {
if (username == null || password == null) {
throw new SecuredRestException(
"You must specify both a username and password for a "
+ "SecuredRestBuilder before calling the build() method.");
}
if (client == null) {
client = new OkClient();
}
OAuthHandler hdlr = new OAuthHandler(client, loginUrl, username, password, clientId, clientSecret);
setRequestInterceptor(hdlr);
return super.build();
}
// setters and getters here
}
So, I ended up splitting RestAdapter class into two separate classes. The first one gets token. Another one is a RestAdapter class that takes the token as input.
Class for getting token:
public class GetTokenRequest {
public static final String TAG = GetTokenRequest.class.getCanonicalName();
public static final String CLIENT_ID = AccessPoint.CLIENT_ID;
public static final String CLIENT_SECRET = AccessPoint.CLIENT_SECRET;
public static final String ENDPOINT = AccessPoint.ENDPOINT;
public static final String TOKEN_PATH = AccessPoint.TOKEN_PATH;
public interface Listener {
void onGetTokenSucess(String token);
void onGetTokenUnauthorized();
void onGetTokenFailure();
}
public static void getAccessToken(Client client, String username, String password,
final Listener callback) {
try {
// This code below programmatically builds an OAuth 2.0 password
// grant request and sends it to the server.
// Encode the username and password into the body of the request.
FormUrlEncodedTypedOutput to = new FormUrlEncodedTypedOutput();
to.addField("username", username);
to.addField("password", password);
// Add the client ID and client secret to the body of the request.
to.addField("client_id", CLIENT_ID);
to.addField("client_secret", CLIENT_SECRET);
// Indicate that we're using the OAuth Password Grant Flow
// by adding grant_type=password to the body
to.addField("grant_type", "password");
// The password grant requires BASIC authentication of the client.
// In order to do BASIC authentication, we need to concatenate the
// client_id and client_secret values together with a colon and then
// Base64 encode them. The final value is added to the request as
// the "Authorization" header and the value is set to "Basic "
// concatenated with the Base64 client_id:client_secret value described
// above.
String base64Auth = BaseEncoding.base64()
.encode(new String(CLIENT_ID + ":" + CLIENT_SECRET).getBytes());
// Add the basic authorization header
List<Header> headers = new ArrayList<Header>();
headers.add(new Header("Authorization", "Basic " + base64Auth));
// Create the actual password grant request using the data above
Request req = new Request("POST", ENDPOINT + TOKEN_PATH, headers, to);
// Request the password grant.
Response resp = client.execute(req);
if (resp == null) {
Log.e(TAG, "resp is null");
callback.onGetTokenFailure();
return;
}
int status = resp.getStatus();
// Make sure the server responded with 200 OK
if (status >= 200 && status < 300) {
Log.e(TAG, "getToken response code is okay");
// Extract the string body from the response
final String body = IOUtils.toString(resp.getBody().in());
// Extract the access_token (bearer token) from the response so that we
// can add it to future requests.
if (callback instanceof LoginActivity)
((LoginActivity) callback).runOnUiThread(new Runnable() {
#Override
public void run() {
callback.onGetTokenSucess(new Gson().fromJson(body, JsonObject.class)
.get("access_token").getAsString());
}
});
} else if (status == HttpStatus.SC_UNAUTHORIZED
|| status == HttpStatus.SC_BAD_REQUEST) {
Log.e(TAG, "getToken response code is 401");
// Incorrect credentials
if (callback instanceof LoginActivity)
((LoginActivity) callback).runOnUiThread(new Runnable() {
#Override
public void run() {
callback.onGetTokenUnauthorized();
}
});
} else {
// Other error
Log.e(TAG, "getToken response code - other");
if (callback instanceof LoginActivity)
((LoginActivity) callback).runOnUiThread(new Runnable() {
#Override
public void run() {
((LoginActivity) callback).onGetTokenFailure();
}
});
}
} catch (Exception e) {
Log.e(TAG, "Exception caught");
Log.e(TAG, e.toString());
if (callback instanceof LoginActivity)
((LoginActivity) callback).runOnUiThread(new Runnable() {
#Override
public void run() {
callback.onGetTokenFailure();
}
});
}
}
}
RestAdapter class:
public class SecuredRestAdapter extends RestAdapter.Builder {
private class OAuthHandler implements RequestInterceptor {
private boolean loggedIn;
private Client client;
private String tokenIssuingEndpoint;
private String username;
private String password;
private String clientId;
private String clientSecret;
private String accessToken;
public OAuthHandler(Client client, String accessToken) {
super();
this.client = client;
this.accessToken = accessToken;
}
#Override
public void intercept(RequestFacade request) {
// Add the access_token that we previously obtained to this request as
// the "Authorization" header.
request.addHeader("Authorization", "Bearer " + accessToken);
}
}
private String loginUrl;
private Client client;
private String token;
public SecuredRestAdapter setLoginEndpoint(String endpoint){
loginUrl = endpoint;
return this;
}
#Override
public SecuredRestAdapter setEndpoint(String endpoint) {
return (SecuredRestAdapter) super.setEndpoint(endpoint);
}
#Override
public SecuredRestAdapter setEndpoint(Endpoint endpoint) {
return (SecuredRestAdapter) super.setEndpoint(endpoint);
}
#Override
public SecuredRestAdapter setClient(Client client) {
this.client = client;
return (SecuredRestAdapter) super.setClient(client);
}
#Override
public SecuredRestAdapter setClient(Provider clientProvider) {
client = clientProvider.get();
return (SecuredRestAdapter) super.setClient(clientProvider);
}
#Override
public SecuredRestAdapter setErrorHandler(ErrorHandler errorHandler) {
return (SecuredRestAdapter) super.setErrorHandler(errorHandler);
}
#Override
public SecuredRestAdapter setExecutors(Executor httpExecutor,
Executor callbackExecutor) {
return (SecuredRestAdapter) super.setExecutors(httpExecutor,
callbackExecutor);
}
#Override
public SecuredRestAdapter setRequestInterceptor(
RequestInterceptor requestInterceptor) {
return (SecuredRestAdapter) super
.setRequestInterceptor(requestInterceptor);
}
#Override
public SecuredRestAdapter setConverter(Converter converter) {
return (SecuredRestAdapter) super.setConverter(converter);
}
#Override
public SecuredRestAdapter setProfiler(#SuppressWarnings("rawtypes") Profiler profiler) {
return (SecuredRestAdapter) super.setProfiler(profiler);
}
#Override
public SecuredRestAdapter setLog(Log log) {
return (SecuredRestAdapter) super.setLog(log);
}
#Override
public SecuredRestAdapter setLogLevel(LogLevel logLevel) {
return (SecuredRestAdapter) super.setLogLevel(logLevel);
}
public SecuredRestAdapter setToken(String token) {
this.token = token;
return this;
}
#Override
public RestAdapter build() {
if (this.token == null || this.token.equals(""))
throw new SecuredRestAdapterException(
"Token must be provided, when calling SecuredRestAdapter");
if (client == null) {
client = new OkClient();
}
OAuthHandler hdlr = new OAuthHandler(client, token);
setRequestInterceptor(hdlr);
return super.build();
}
}
Exception class:
public class SecuredRestAdapterException extends RuntimeException {
public SecuredRestAdapterException(String message) {
super(message);
}
}
Related
public void signInWithLinkedIn(View view) {
//First check if user is already authenticated or not and session is valid or not
if (!LISessionManager.getInstance(this).getSession().isValid()) {
//if not valid then start authentication
LISessionManager.getInstance(getApplicationContext()).init(LinkedInActivity.this, buildScope()//pass the build scope here
, new AuthListener() {
#Override
public void onAuthSuccess() {
// Authentication was successful. You can now do
// other calls with the SDK.
Toast.makeText(LinkedInActivity.this, "Successfully authenticated with LinkedIn.", Toast.LENGTH_SHORT).show();
//on successful authentication fetch basic profile data of user
//LISessionManager.getInstance(getApplicationContext()).clearSession();
fetchBasicProfileData();
}
#Override
public void onAuthError(LIAuthError error) {
// Handle authentication errors
//LISessionManager.getInstance(getApplicationContext()).clearSession();
Log.e("AUTH ERROR", "Auth Error :" + error.toString());
Toast.makeText(LinkedInActivity.this, "Failed to authenticate with LinkedIn. Please try again.", Toast.LENGTH_SHORT).show();
}
}, true);//if TRUE then it will show dialog if
// any device has no LinkedIn app installed to download app else won't show anything
} else {
//LISessionManager.getInstance(getApplicationContext()).clearSession();
Toast.makeText(this, "You have already been authenticated.", Toast.LENGTH_SHORT).show();
//if user is already authenticated fetch basic profile data for user
fetchBasicProfileData();
}
}
private static Scope buildScope() {
//Check Scopes in Application Settings before passing here else you won't able to read that data
// Scope.R_CONTACTINFO
return Scope.build(Scope.R_BASICPROFILE);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
LISessionManager.getInstance(getApplicationContext()).onActivityResult(this, requestCode, resultCode, data);
Log.d("Access token->", LISessionManager.getInstance(getApplicationContext()).getSession().getAccessToken().getValue());
}
/**
* method to fetch basic profile data
*/
private void fetchBasicProfileData() {
//In URL pass whatever data from user you want for more values check below link
//LINK : https://developer.linkedin.com/docs/fields/basic-profile
String url = "https://api.linkedin.com/v1/people/~:(id,first-name,last-name,headline,public-profile-url,picture-url,email-address,picture-urls::(original))";
APIHelper apiHelper = APIHelper.getInstance(getApplicationContext());
apiHelper.getRequest(this, url, new ApiListener() {
#Override
public void onApiSuccess(ApiResponse apiResponse) {
// Success!
JSONObject responseObject = apiResponse.getResponseDataAsJson();
try {
profileURL = responseObject.getString("publicProfileUrl");
imgURL = responseObject.getString("pictureUrl");
fetchConnectionsData();
} catch (JSONException e) {
e.printStackTrace();
}
}
#Override
public void onApiError(LIApiError liApiError) {
// Error making GET request!
Log.e("FETCH PROFILE ERROR", "Fetch profile Error :" + liApiError.getLocalizedMessage());
Toast.makeText(LinkedInActivity.this, "Failed. Please try again.", Toast.LENGTH_SHORT).show();
}
});
}
Linkedin returns "No value for accessTokenValue" and "access toke is not set". It was working like last month but suddenly it does not work and I could not find anything wrong with the code. After much digging on Google, I am still unable to find a solution. Or am I using the v1 api which I should not? Any help will be greatly appreciated.
This is because the token generated by Linkedin SDK can not be verified by backend. The best solution is to open a webview and use Linkedin web apis- Step 1 - Create a new Class LinkedinActivity
public class LinkedinActivity {
/****FILL THIS WITH YOUR INFORMATION*********/
//This is the public api key of our application
private static final String API_KEY = "apikey";
//This is the private api key of our application
private static final String SECRET_KEY = "secretcode";
//This is any string we want to use. This will be used for avoiding CSRF attacks. You can generate one here: http://strongpasswordgenerator.com/
private static final String STATE = "123456789";
//This is the url that LinkedIn Auth process will redirect to. We can put whatever we want that starts with http:// or https:// .
//We use a made up url that we will intercept when redirecting. Avoid Uppercases.
private static final String REDIRECT_URI = "https://example.com";
/*********************************************/
//These are constants used for build the urls
private static final String AUTHORIZATION_URL = "https://www.linkedin.com/oauth/v2/authorization";
private static final String ACCESS_TOKEN_URL = "https://www.linkedin.com/uas/oauth2/accessToken";
private static final String SECRET_KEY_PARAM = "client_secret";
private static final String RESPONSE_TYPE_PARAM = "response_type";
private static final String GRANT_TYPE_PARAM = "grant_type";
private static final String GRANT_TYPE = "authorization_code";
private static final String RESPONSE_TYPE_VALUE = "code";
private static final String CLIENT_ID_PARAM = "client_id";
private static final String STATE_PARAM = "state";
private static final String REDIRECT_URI_PARAM = "redirect_uri";
/*---------------------------------------*/
private static final String QUESTION_MARK = "?";
private static final String AMPERSAND = "&";
private static final String EQUALS = "=";
private WebView webView;
private ImageView close_icon;
private ProgressDialog pd;
//private OauthInterface oauthInterface;
String accessToken;
Context context;
Dialog dialog;
public LinkedinActivity(#NonNull Context context) {
this.context = context;
}
public void showLinkedin() {
dialog = new Dialog(context, R.style.AppTheme);
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
// dialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
dialog.setContentView(R.layout.linkedin_activity);
dialog.setCancelable(false);
dialog.setCanceledOnTouchOutside(false);
pd = ProgressDialog.show(context, "", "Loading...", true);
dialog.setOnKeyListener(new DialogInterface.OnKeyListener() {
#Override
public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
LinkedinData linkedinData = (LinkedinData) context;
linkedinData.linkedCancel();
dialog.dismiss();
}
return true;
}
});
//oauthInterface = new OauthPresenter(this);
//get the webView from the layout
webView = (WebView) dialog.findViewById(R.id.activity_web_view);
close_icon = (ImageView) dialog.findViewById(R.id.close_icon);
close_icon.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
LinkedinData linkedinData = (LinkedinData) context;
linkedinData.linkedCancel();
dialog.dismiss();
}
});
//Request focus for the webview
webView.requestFocus(View.FOCUS_DOWN);
//Show a progress dialog to the user
//Set a custom web view client
webView.setWebViewClient(new WebViewClient() {
#Override
public void onPageFinished(WebView view, String url) {
//This method will be executed each time a page finished loading.
//The only we do is dismiss the progressDialog, in case we are showing any.
if (pd != null && pd.isShowing()) {
pd.dismiss();
}
}
#Override
public boolean shouldOverrideUrlLoading(WebView view, String authorizationUrl) {
//This method will be called when the Auth proccess redirect to our RedirectUri.
//We will check the url looking for our RedirectUri.
if (authorizationUrl.startsWith(REDIRECT_URI)) {
Log.i("Authorize", "");
Uri uri = Uri.parse(authorizationUrl);
//We take from the url the authorizationToken and the state token. We have to check that the state token returned by the Service is the same we sent.
//If not, that means the request may be a result of CSRF and must be rejected.
String stateToken = uri.getQueryParameter(STATE_PARAM);
if (stateToken == null || !stateToken.equals(STATE)) {
Log.e("Authorize", "State token doesn't match");
return true;
}
//If the user doesn't allow authorization to our application, the authorizationToken Will be null.
String authorizationToken = uri.getQueryParameter(RESPONSE_TYPE_VALUE);
if (authorizationToken == null) {
Log.i("Authorize", "The user doesn't allow authorization.");
return true;
}
Log.i("Authorize", "Auth token received: " + authorizationToken);
//Generate URL for requesting Access Token
String accessTokenUrl = getAccessTokenUrl(authorizationToken);
//We make the request in a AsyncTask
new PostRequestAsyncTask().execute(accessTokenUrl);
} else {
//Default behaviour
Log.i("Authorize", "Redirecting to: " + authorizationUrl);
webView.loadUrl(authorizationUrl);
}
return true;
}
});
//Get the authorization Url
String authUrl = getAuthorizationUrl();
Log.i("Authorize", "Loading Auth Url: " + authUrl);
//Load the authorization URL into the webView
webView.loadUrl(authUrl);
dialog.show();
}
private static String getAccessTokenUrl(String authorizationToken) {
return ACCESS_TOKEN_URL
+ QUESTION_MARK
+ GRANT_TYPE_PARAM + EQUALS + GRANT_TYPE
+ AMPERSAND
+ RESPONSE_TYPE_VALUE + EQUALS + authorizationToken
+ AMPERSAND
+ CLIENT_ID_PARAM + EQUALS + API_KEY
+ AMPERSAND
+ REDIRECT_URI_PARAM + EQUALS + REDIRECT_URI
+ AMPERSAND
+ SECRET_KEY_PARAM + EQUALS + SECRET_KEY;
}
/**
* Method that generates the url for get the authorization token from the Service
*
* #return Url
*/
private static String getAuthorizationUrl() {
return AUTHORIZATION_URL
+ QUESTION_MARK + RESPONSE_TYPE_PARAM + EQUALS + RESPONSE_TYPE_VALUE
+ AMPERSAND + CLIENT_ID_PARAM + EQUALS + API_KEY
+ AMPERSAND + REDIRECT_URI_PARAM + EQUALS + REDIRECT_URI
+ AMPERSAND + STATE_PARAM + EQUALS + STATE
+ AMPERSAND + "scope=r_emailaddress";
}
private class PostRequestAsyncTask extends AsyncTask<String, Void, String> {
#Override
protected void onPreExecute() {
pd = ProgressDialog.show(context, "", "loading", true);
}
#Override
protected String doInBackground(String... urls) {
if (urls.length > 0) {
String url = urls[0];
HttpClient httpClient = new DefaultHttpClient();
HttpPost httpost = new HttpPost(url);
try {
HttpResponse response = httpClient.execute(httpost);
if (response != null) {
//If status is OK 200
if (response.getStatusLine().getStatusCode() == 200) {
String result = EntityUtils.toString(response.getEntity());
//Convert the string result to a JSON Object
JSONObject resultJson = new JSONObject(result);
//Extract data from JSON Response
int expiresIn = resultJson.has("expires_in") ? resultJson.getInt("expires_in") : 0;
accessToken = resultJson.has("access_token") ? resultJson.getString("access_token") : null;
Log.e("Tokenm", "" + accessToken);
if (expiresIn > 0 && accessToken != null) {
Log.i("Authorize", "This is the access Token: " + accessToken + ". It will expires in " + expiresIn + " secs");
//Calculate date of expiration
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.SECOND, expiresIn);
long expireDate = calendar.getTimeInMillis();
////Store both expires in and access token in shared preferences
SharedPreferences preferences = context.getSharedPreferences("user_info", 0);
SharedPreferences.Editor editor = preferences.edit();
editor.putLong("expires", expireDate);
editor.putString("accessToken", accessToken);
//oauthInterface.oauthAuthentication(accessToken, "linkedin", new HackedPrefence(getApplicationContext()).getDevice_token());
editor.commit();
return accessToken;
}
}
}
} catch (IOException e) {
Log.e("Authorize", "Error Http response " + e.getLocalizedMessage());
} catch (ParseException e) {
Log.e("Authorize", "Error Parsing Http response " + e.getLocalizedMessage());
} catch (JSONException e) {
Log.e("Authorize", "Error Parsing Http response " + e.getLocalizedMessage());
}
}
return accessToken;
}
#Override
protected void onPostExecute(String status) {
if (pd != null && pd.isShowing()) {
pd.dismiss();
}
LinkedinData linkedinData = (LinkedinData) context;
linkedinData.LinkedinSuccess(status);
if (dialog.isShowing()) {
dialog.dismiss();
}
}
}
interface LinkedinData {
void linkedCancel();
void LinkedinSuccess(String Token);
}
}
Step 2 - Call LinkdinActivity.java class using the following code
new LinkedinActivity(this).showLinkedin();
You can call the above code on the click of a button. Example
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
new LinkedinActivity(this).showLinkedin();
}
});
Here is linkedin_activity.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<WebView
android:id="#+id/activity_web_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<ImageView
android:id="#+id/close_icon"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_marginRight="10dp"
android:layout_marginTop="22dp"
android:src="#drawable/ic_close_black"
android:layout_alignParentRight="true"/>
</RelativeLayout>
I need to do authentification from my app android by using spring boot i try to send the username and password like this:
HttpAuthentication authHeader = new HttpBasicAuthentication(username, password);
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.setAuthorization(authHeader);
requestHeaders.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
and make network request using restTemplate.exchange :
ResponseEntity<Message> response = restTemplate.exchange(url, HttpMethod.POST,new HttpEntity<Object>(requestHeaders), Message.class);
but i still get null response.
this my code server :
Controller:
#Controller
public class RController {
#RequestMapping(value={"/welcome"})
public #ResponseBody Message getMessage() {
return new Message(100, "Congratulations!", "You have accessed a Basic
Auth protected resource.");
}
#RequestMapping(value={"/login"})
public String login() {
return "login";
}
}
config
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
DataSource dataSource;
#Autowired
public void configAuthentication(AuthenticationManagerBuilder auth) throws Exception {
auth.jdbcAuthentication().dataSource(dataSource)
.usersByUsernameQuery("select username,password, enabled from users where username=?")
.authoritiesByUsernameQuery("select username, role from user_roles where username=?");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/admin").hasRole("ADMIN")
.anyRequest().authenticated().and().formLogin().loginPage("/login").permitAll().and().logout()
.permitAll();
http.exceptionHandling().accessDeniedPage("/403");
}
}
I believe RController class would need to be annotated with #RestController instead of #Controller
I'm trying to get data from http://www.gibdd.ru/proxy/check/fines/2.0/client.php, but server responds that captcha is incorrect or expired. In advanced rest client into chrome request work's perfect. My alghoritm to getting data is:
Get session from http://www.gibdd.ru/proxy/check/getSession.php
Get captcha with session id http://www.gibdd.ru/proxy/check/getCaptcha.php?PHPSESSID=te67jgdiv53v956fcv8rk9mg81
And then send form data to http://www.gibdd.ru/proxy/check/fines/2.0/client.php
I think my problem with cookies, i set cookies = sessionId, like rest client, but something gone wrong
UPD: I'm using retrofit to server requests
That API class
private static final String URL= "http://www.gibdd.ru";
private static RestAdapter restAdapter;
public interface IGibdd {
#GET("/proxy/check/getSession.php")
void getSession(Callback<Session> callback);;
#FormUrlEncoded
#POST("/proxy/check/fines/2.0/client.php")
void getFines(#Field("regnum") String regnum,
#Field("regreg") String regreg,
#Field("stsnum") String stsnum,
#Field("req") String reqfines,
#Field("captchaWord") String captchaWord,
Callback<Answer> callback);
}
This method requesting captcha, after getting session id
private void getCaptcha() {
final API.IGibdd iGibdd = API.getRestAdapter(this).create(API.IGibdd.class);
iGibdd.getSession(new Callback<Response>() {
#Override
public void success(Response response, Response response2) {
if (response.getStatus() != 200) {
getCaptcha();
} else {
String cookie = API.loadCookies(CheckFinesActivity.this);
url = getString(R.string.captcha_url) + cookie + "&" + new Date().getTime();
HashMap<String, String> map = new HashMap<String, String>();
map.put("Cookie", cookie);
DisplayImageOptions defaultOptions = new DisplayImageOptions.Builder()
.cacheInMemory(true)
.cacheOnDisk(true)
.imageScaleType(ImageScaleType.IN_SAMPLE_POWER_OF_2)
.extraForDownloader(map)
.build();
ImageLoaderConfiguration conf = new ImageLoaderConfiguration.Builder(CheckFinesActivity.this)
.defaultDisplayImageOptions(defaultOptions)
.build();
ImageLoader imageLoader = ImageLoader.getInstance();
imageLoader.init(conf);
imageLoader.displayImage(url, mCaptchaImage);
}
}
#Override
public void failure(RetrofitError error) {
error.printStackTrace();
}
});
}
And then, after entering captcha code, i call this method
String reqfines = "fines:" + mCarNumber.getText().toString() + ":" + mCarRegion.getText().toString() + ":" + mDocNumber.getText().toString();
iGibdd.getFines(mCarNumber.getText().toString(),
mCarRegion.getText().toString(),
mDocNumber.getText().toString(),
reqfines,
mCaptcha.getText().toString(),
new Callback<Answer>() {
#Override
public void success(Answer s, Response response) {
Toast.makeText(CheckFinesActivity.this, "Success", Toast.LENGTH_LONG).show();
API.clearSP(CheckFinesActivity.this);
getCaptcha();
}
#Override
public void failure(RetrofitError error) {
Toast.makeText(CheckFinesActivity.this, "Error", Toast.LENGTH_LONG).show();
}
});
}
});
UPD2:
Problem is solved. I recreated OkHttp client for a new request and it clears current session
This question already has answers here:
How can I fix 'android.os.NetworkOnMainThreadException'?
(66 answers)
Closed 8 years ago.
public class Foursquare {
private static final String LOGIN = "oauth";
public static final String API_END_POING_BASE_URL = "https://api.foursquare.com/v2/";
public static String REDIRECT_URI;
public static final String API_URL = "https://foursquare.com/oauth2/";
//public static final String CANCEL_URI = "";
public static final String TOKEN = "access_token";
public static final String EXPIRES = "expires_in";
public static final String SINGLE_SIGN_ON_DISABLED = "service_disabled";
public static String AUTHENTICATE_URL = "https://foursquare.com/oauth2/authenticate";// +
private String mClientId;
private String mClientSecret;
private String mAccessToken = null;
private DialogListener mAuthDialogListener;
public Foursquare(String clientId, String clientSecret, String redirectUrl) {
if (clientId == null || clientSecret == null) {
throw new IllegalArgumentException(
"You must specify your application ID when instantiating "
+ "a Foursquare object. See README for details.");
}
mClientId = clientId;
mClientSecret = clientSecret;
REDIRECT_URI = redirectUrl;
}
public void authorize(Activity activity, final DialogListener listener) {
mAuthDialogListener = listener;
startDialogAuth(activity);
}
public void startDialogAuth(Activity activity) {
CookieSyncManager.createInstance(activity);
Bundle params = new Bundle();
dialog(activity, LOGIN, params, new DialogListener() {
public void onComplete(Bundle values) {
// ensure any cookies set by the dialog are saved
CookieSyncManager.getInstance().sync();
String _token = values.getString(TOKEN);
setAccessToken(_token);
// setAccessExpiresIn(values.getString(EXPIRES));
if (isSessionValid()) {
Log.d("Foursquare-authorize",
"Login Success! access_token=" + getAccessToken());
mAuthDialogListener.onComplete(values);
} else {
mAuthDialogListener.onFoursquareError(new FoursquareError(
"Failed to receive access token."));
}
}
public void onError(DialogError error) {
Log.d("Foursquare-authorize", "Login failed: " + error);
mAuthDialogListener.onError(error);
}
public void onFoursquareError(FoursquareError error) {
Log.d("Foursquare-authorize", "Login failed: " + error);
mAuthDialogListener.onFoursquareError(error);
}
public void onCancel() {
Log.d("Foursquare-authorize", "Login canceled");
mAuthDialogListener.onCancel();
}
});
}
public void dialog(Context context, String action, Bundle parameters,
final DialogListener listener) {
String endpoint = "";
parameters.putString("client_id", mClientId);
parameters.putString("display", "touch");
if (action.equals(LOGIN)) {
endpoint = AUTHENTICATE_URL;
parameters.putString("client_secret", mClientSecret);
parameters.putString("response_type", "token");
parameters.putString("redirect_uri", REDIRECT_URI);
}
// if (isSessionValid()) {
// parameters.putString(TOKEN, getAccessToken());
// }
String url = endpoint + "?" + Util.encodeUrl(parameters);
if (context.checkCallingOrSelfPermission(Manifest.permission.INTERNET) != PackageManager.PERMISSION_GRANTED) {
Util.showAlert(context, "Error",
"Application requires permission to access the Internet");
} else {
new FoursquareDialog(context, url, listener).show();
}
}
public boolean isSessionValid() {
if (getAccessToken() != null) {
return true;
}
return false;
}
public void setAccessToken(String token) {
mAccessToken = token;
}
public String getAccessToken() {
return mAccessToken;
}
public String request(String graphPath) throws MalformedURLException,
IOException {
return request(graphPath, new Bundle(), "GET");
}
public String request(String graphPath, Bundle parameters)
throws MalformedURLException, IOException {
return request(graphPath, parameters, "GET");
}
public String request(String graphPath, Bundle params, String httpMethod)
throws FileNotFoundException, MalformedURLException, IOException {
params.putString("format", "json");
if (isSessionValid()) {
params.putString("oauth_token", getAccessToken());
}
String url = API_END_POING_BASE_URL + graphPath;
return Util.openUrl(url, httpMethod, params);
}
public static interface DialogListener {
/**
* Called when a dialog completes.
*
* Executed by the thread that initiated the dialog.
*
* #param values
* Key-value string pairs extracted from the response.
*/
public void onComplete(Bundle values);
/**
* Called when a Foursquare responds to a dialog with an error.
*
* Executed by the thread that initiated the dialog.
*
*/
public void onFoursquareError(FoursquareError e);
/**
* Called when a dialog has an error.
*
* Executed by the thread that initiated the dialog.
*
*/
public void onError(DialogError e);
/**
* Called when a dialog is canceled by the user.
*
* Executed by the thread that initiated the dialog.
*
*/
public void onCancel();
}
}
i am using foursqure functionality in my application in which user share any data so user can share on foursqure..,the problem is when i am using "StrictMode "functionality in my oncreate.,its not giving me error.,but when i am not using its giving me networkon minthread exception
i am getting this exception in foursqure.when dialog is loading??what i do plese help me thankyou...:)
here is my logcat
Networking operations tend to take some time. That's why on Android, it's forbidden to perform Networking Tasks on the UI (foreground) thread. The System throws a NetworkingOnMainThreadException if you try it.
You have to move your Networking code to a Background thread. The easiest way to do that is by using an AsyncTask.
Implement AsyncTask, read here
I can't seem to figure out how to get android annotations rest client to work I'm having 2 main issues.
A)How to parse the generic json response and get the meaningful key
B)How to add parameters
For the first problem all responses come back as a json string fomatted like this
{"success":,"message":"","data":{}}
Where success is boolean message is a string and data is going to be the main data I want to parse that may be a boolean, an array, a string or an int
I'm pretty sure I need to intercept the response and handle the code but I'm not sure how to do that
Lets use a real response that look something like this
{"success":true,"message":"random message","data":{"profile":{"id":"44","user_id":"44","name":"Matt","username":"mitch","icon":"b1da7ae15027b7d6421c158d644f3220.png","med":"2a3df53fb39d1d8b5edbd0b93688fe4a.png","map":"b7bfed1f456ca4bc8ca748ba34ceeb47.png","background":null,"mobile_background":null}}
First in my interceptor I want to see if the boolean key "success" is true and then return the data value
#EBean
public class RestInterceptor implements ClientHttpRequestInterceptor {
final String TAG = "rest";
#Bean
AuthStore authStore;
#Override
public ClientHttpResponse intercept(HttpRequest request, byte[] data, ClientHttpRequestExecution execution)
throws IOException{
//Need to set the api key here but nothing happens code quits
// Log.d("Rest",authStore.getApiKey());
HttpHeaders headers = request.getHeaders();
headers.set("api_key","");
ClientHttpResponse resp = execution.execute(request, data);
HttpStatus code = resp.getStatusCode();
if(code.value() == 200){
Log.d(TAG,"success code 200");
//valid http request but is it a valid API request?
//perform some logic of if success == true in root json object
//if true cast return data key
}
else{
Log.d(TAG,"fail code" + code.toString());
}
return resp;
}
}
The second problem is sending params with the http request that have an api key and a session key, I define the application class like this
#EApplication
public class MyApp extends Application {
final String TAG = "app";
#Bean
AuthStore authStore;
#RestService
RestClient restClient;
public void onCreate() {
super.onCreate();
init();
}
#AfterInject
public void init() {
authStore.setApiKey("dummy_key");
Log.d(TAG, "api key set to " + authStore.getApiKey());
}
}
With the AuthStore class like this
#EBean(scope = Scope.Singleton)
public class AuthStore {
public String apiKey,sessionKey;
public String getApiKey() {
return apiKey;
}
public void setApiKey(String apiKey) {
this.apiKey = apiKey;
}
public String getSessionKey() {
return sessionKey;
}
public void setSessionKey(String sessionKey) {
this.sessionKey = sessionKey;
}
}
Basically I'm setting a dummy api key at the application level in a singleton, which I should be able to access in the rest interceptor interface but the code just quits without errors I'm basically following this guide https://github.com/excilys/androidannotations/wiki/Authenticated-Rest-Client
Finally I have an activity class which injects the app dependency which has refrence to the rest http class and the authstore class
#EActivity(R.layout.activity_login)
public class LoginActivity extends Activity {
#App
MyApp app;
#ViewById
TextView email;
#ViewById
TextView password;
#ViewById
Button loginButton;
#AfterInject
public void init() {
Log.d(app.TAG, "api in login key set to " + app.authStore.getApiKey());
}
#Click
#Trace
void loginButton() {
login(email.toString(), password.toString());
}
#Background
void login(String email, String password) {
app.restClient.forceLogin();
}
}
Sorry if it's a lot of info, I've been searching for a while and can't figure this out!
thanks in advance
I'm not known with the library you're using (annotations, spring) but it seems to me that you are struggling with parsing the success = true because that is not supposed to be in the JSON.
The JSON should preferably represent a class in your app 1on1 so you can easily map that into an object.
Communication between your app and the webservice, regarding the status of requests should go into the headers.
Like this you can check a request's headers, before parsing the JSON.
This is mathod I am using to parse any JSON object recursively.
private void parseJson(JSONObject data) {
if (data != null) {
Iterator<String> it = data.keys();
while (it.hasNext()) {
String key = it.next();
try {
if (data.get(key) instanceof JSONArray) {
JSONArray arry = data.getJSONArray(key);
int size = arry.length();
for (int i = 0; i < size; i++) {
parseJson(arry.getJSONObject(i));
}
} else if (data.get(key) instanceof JSONObject) {
parseJson(data.getJSONObject(key));
} else {
System.out.println("" + key + " : " + data.optString(key));
}
} catch (Throwable e) {
System.out.println("" + key + " : " + data.optString(key));
e.printStackTrace();
}
}
}
}