Hit Zaim's API (OAuth 1.0) in Java

It's a painful word, OAuth 1.0 ... I'm not sure because I haven't used Spring so much, but it seems that RestTemplate should be used when hitting the API. But with OAuth 1.0, I didn't get into it well, and I ended up using HttpClient. Is there a best practice for hitting the OAuth 1.0 API in Spring? .. .. Please teach if there is a better way.

(Added on 2018/08/06) It seems that OAuth 1.0 works well with Spring Security's sister project called spring-security-oauth. https://projects.spring.io/spring-security-oauth/docs/oauth1.html There are a lot of things like trying to implement OAuth2 in the streets, but 1.0 is really scarce.

Thing you want to do

Get the data by hitting Zaim's OAuth 1.0 API.

use

Gradle The dependency looks like this. The following are required, and the others are as you like.

build.gradle


dependencies {
	// spring-boot
	compile('org.springframework.boot:spring-boot-starter-thymeleaf')
	compile('org.springframework.boot:spring-boot-starter-web')
	compile('org.springframework.boot:spring-boot-devtools')
	compileOnly('org.projectlombok:lombok')
	testCompile('org.springframework.boot:spring-boot-starter-test')
	// OAuth1.0a
	compile('oauth.signpost:signpost-core:1.2.1.2')
	compile('oauth.signpost:signpost-commonshttp4:1.2.1.2')
}

Application registration with Zaim

Add a new application below. https://dev.zaim.net

If you add it, you can get the following information, so make a note of it.

--Consumer ID --Consumer Secret --Request token acquisition URL --Authentication URL --Access token acquisition URL

Be careful not to leak your consumer ID and consumer secret.

Process flow

Learn more about OAuth 1.0 here. http://yuroyoro.hatenablog.com/entry/20100506/1273137673

  1. Create an instance to use for OAuth authentication
  2. Generate the URL of Zaim's authentication page
  3. Display Zaim's authentication page using the generated URL
  4. After authentication, generate an access token from the authentication result
  5. After signing the request with the access token, communicate

1. Create an instance to use for OAuth authentication

Create an instance using the information you saved when you added a new application in Zaim.

python


private static final String CONSUMER_KEY = "Zaim registration information";
private static final String CONSUMER_SECRET = "Zaim registration information";
private static final String REQUEST_TOKEN_URL = "Zaim registration information";
private static final String AUTHORIZE_URL = "Zaim registration information";
private static final String ACCESS_TOKEN_URL = "Zaim registration information";

private OAuthConsumer consumer;
private OAuthProvider provider;

consumer = new CommonsHttpOAuthConsumer(CONSUMER_KEY, CONSUMER_SECRET);
provider = new CommonsHttpOAuthProvider(REQUEST_TOKEN_URL, ACCESS_TOKEN_URL, AUTHORIZE_URL);

This is enough for use, but of course you should go out to the property file etc. for the information around here. Create a bean for the property.

application.properties


zaim.consumerKey=your_consumerKey
zaim.consumerSecret=your_consumerSecret
zaim.requestTokenUrl=https://api.zaim.net/v2/auth/request
zaim.authorizeUrl=https://auth.zaim.net/users/auth
zaim.accessTokenUrl=https://api.zaim.net/v2/auth/access

OAuthProperty


/**
 *Obtain and retain the information required for OAuth authentication from the property file
 */
public class OAuthProperty {

	@Getter
	@Value("${zaim.consumerKey}")
	private String consumerKey;

	@Getter
	@Value("${zaim.consumerSecret}")
	private String consumerSecret;

	@Getter
	@Value("${zaim.requestTokenUrl}")
	private String requestTokenUrl;

	@Getter
	@Value("${zaim.authorizeUrl}")
	private String authorizeUrl;

	@Getter
	@Value("${zaim.accessTokenUrl}")
	private String accessTokenUrl;

}

BeanConfig.java


@Configuration
public class BeanConfig {

	@Bean
	OAuthProperty oAuthProperty() {
		return new OAuthProperty();
	}

	//Since the accessToken is different for each user, it is necessary to have at least for each session
	@Bean
	@Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
	OAuthConsumer oAuthConsumer(OAuthProperty oAuthProperty) {
		return new CommonsHttpOAuthConsumer(oAuthProperty.getConsumerKey(),
				oAuthProperty.getConsumerSecret());
	}

	//Since the accessToken is different for each user, it is necessary to have at least for each session
	@Bean
	@Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
	OAuthProvider oAuthProvider(OAuthProperty oAuthProperty) {
		return new CommonsHttpOAuthProvider(oAuthProperty.getRequestTokenUrl(),
				oAuthProperty.getAccessTokenUrl(), oAuthProperty.getAuthorizeUrl());
	}

}

2. Generate the URL of Zaim's authentication page

Generate the URL of the authentication page using the created instance. After authenticating on the Zaim page, set the returned URL as the callback URL. (As an example, let's use the URL of the controller to be created later)

python


provider.retrieveRequestToken(consumer, "Callback URL");

Example


provider.retrieveRequestToken(consumer, "http://localhost:8080/authenticated");

3. Display Zaim's authentication page using the generated URL

Transit to the URL created above. This time, we will create an appropriate controller and redirect when accessing it. Now when you visit http: // localhost: 8080 / you will be taken to Zaim's authentication page.

indexController


@Controller
public class IndexController {

	@Autowired
	OAuthConsumer consumer;

	@Autowired
	OAuthProvider provider;

	@RequestMapping("/")
	public String index() throws Exception {
		//URL generation for authentication
		String URL = provider.retrieveRequestToken(consumer, "http://localhost:8080/authenticated");
		return "redirect:" + URL;
	}
}

4. After authentication, generate an access token from the authentication result

When you enter the login password in Zaim and the authentication is completed, it will return to the callback URL set in "Generate the URL of the authentication page of Zaim". At this time, the following two parameters are returned together, so get them.

Use this oauth_verifier to generate an access token.

python


	/**
	 *Access with callback after authentication with zaim
	 * @Returned from param oauthToken zaim
	 * @Returned from param oauthVerifier zaim
	 * @return
	 * @throws Exception
	 */
	@RequestMapping("/authenticated")
	public String authenticated(@RequestParam(value = "oauth_token") String oauthToken,
			@RequestParam(value = "oauth_verifier") String oauthVerifier, Model model)
			throws Exception {

		//Generate accessToken and accessTokenSecret
		provider.retrieveAccessToken(consumer, oauthVerifier);


		return "index";
	}

The access token generated in this way can be obtained. By saving these two, you will be able to access them without having to authenticate again with zaim.

python


		//Generate accessToken and accessTokenSecret
		provider.retrieveAccessToken(consumer, oauthVerifier);

		//Get a token (save this so you don't have to reauthenticate
		String accessToken = consumer.getToken();
		String accessTokenSecret = consumer.getTokenSecret();

If you use the token like this, you don't have to reauthenticate.

Use it like this


		consumer = new CommonsHttpOAuthConsumer(CONSUMER_KEY, CONSUMER_SECRET);
		provider = new CommonsHttpOAuthProvider(REQUEST_TOKEN_URL, ACCESS_TOKEN_URL, AUTHORIZE_URL);

		//Set the saved token
		consumer.setTokenWithSecret(accessToken, accessTokenSecret);

Communicate after signing the request with the access token

Now that I have an access token, I can finally hit Zaim with the API. First, prepare HttpClient.

There is a nice error handler on the HttpClient official page, so I will use it as it is. If the response code when hitting the API is not 200 series, Exception will be thrown.

APIResponseHandler


/**
 *Response code check when hitting the API
 *If there is no problem, return the body
 *Exception if not normal
 * https://hc.apache.org/httpcomponents-client-ga/httpclient/examples/org/apache/http/examples/client/ClientWithResponseHandler.java
 */
public class APIResponseHandler implements ResponseHandler<String> {

	@Override
	public String handleResponse(
			final HttpResponse response) throws ClientProtocolException, IOException {
		int status = response.getStatusLine().getStatusCode();
		if (status >= 200 && status < 300) {
			HttpEntity entity = response.getEntity();
			return entity != null ? EntityUtils.toString(entity) : null;
		} else {
			throw new ClientProtocolException("Unexpected response status: " + status);
		}
	}

}

Now that we're ready, let's hit the API. This time, after authentication, I will try to acquire user information as it is.

python


	/**
	 *Access by callback after authentication with zaim, then get user information
	 * @Returned from param oauthToken zaim
	 * @Returned from param oauthVerifier zaim
	 * @return
	 * @throws Exception
	 */
	@RequestMapping("/authenticated")
	public String authenticated(@RequestParam(value = "oauth_token") String oauthToken,
			@RequestParam(value = "oauth_verifier") String oauthVerifier, Model model)
			throws Exception {

        //Generate accessToken and accessTokenSecret
        provider.retrieveAccessToken(consumer, oauthVerifier);

        //HttpClient preparation
        CloseableHttpClient httpclient = HttpClients.createDefault();
        ResponseHandler<String> responseHandler = new APIResponseHandler();

        //Sign the request
        HttpGet httpget = new HttpGet("https://api.zaim.net/v2/home/user/verify");
        consumer.sign(httpget);

        //Get
        String responseBody = httpclient.execute(httpget, responseHandler);
        model.addAttribute("zaimUserVerify", responseBody);

		return "index";
	}

I think that the JSON of the login information has been returned.

Hit the API without re-authentication

If you want to hit the API without re-authentication after authentication, you can hit it as it is by setting the saved accessToken and accessTokenSecret in the consumer.

When using accessToken and accessTokenSecret



        //Set the saved token
        consumer.setTokenWithSecret(accessToken, accessTokenSecret);

        //HttpClient preparation
        CloseableHttpClient httpclient = HttpClients.createDefault();
        ResponseHandler<String> responseHandler = new APIResponseHandler();

        //Sign the request
        HttpGet httpget = new HttpGet("https://api.zaim.net/v2/home/user/verify");
        consumer.sign(httpget);

        //Get
        String responseBody = httpclient.execute(httpget, responseHandler);
        model.addAttribute("zaimUserVerify", responseBody);

at the end

I was able to hit Zaim's API (OAuth 1.0) in Java like this.

If you use Spring RestTemplate, it will map the returned JSON to an object and return it, which seems to be insanely convenient, but I can not use it anyway. .. Isn't it possible to use OAuth 1.0 a little better? ..

For the time being, I will put what I implemented in my own way. I'm glad if you can use it as a reference. https://github.com/kxn4t/saifu

Recommended Posts

Hit Zaim's API (OAuth 1.0) in Java
Zabbix API in Java
Java Stream API in 5 minutes
Generate CloudStack API URL in Java
Parsing the COTOHA API in Java
Hit the Docker API in Rust
JPA (Java Persistence API) in Eclipse
I tried using Elasticsearch API in Java
Try using the Stream API in Java
Call the Windows Notification API in Java
Hit the Salesforce REST API from Java
Try using JSON format API in Java
Partization in Java
Changes in Java 11
Rock-paper-scissors in Java
Java Stream API
Pi in Java
FizzBuzz in Java
ChatWork4j for using the ChatWork API in Java
[Java] API creation using Jerjey (Jax-rs) in eclipse
Try using the COTOHA API parsing in Java
[java] sort in list
Read JSON in Java
Interpreter implementation in Java
I tried Mastodon's Toot and Streaming API in Java
Rock-paper-scissors app in Java
Constraint programming in Java
Put java8 in centos7
NVL-ish guy in Java
Combine arrays in Java
"Hello World" in Java
Pack API response (java)
Callable Interface in Java
Call Amazon Product Advertising API 5.0 (PA-API v5) in Java
I tried using Google Cloud Vision API in Java
Comments in Java source
[Java] Stream API / map
Docker-Client Java API Troubleshooting
Format XML in Java
Simple htmlspecialchars in Java
Boyer-Moore implementation in Java
Hello World in Java
Use OpenCV in Java
webApi memorandum in java
Type determination in Java
Java8 Stream API practice
Ping commands in Java
Various threads in java
Heapsort implementation (in java)
ASCII art in Java
Compare Lists in Java
POST JSON in Java
Express failure in Java
Create JSON in Java
Date manipulation in Java 8
What's new in Java 8
Use PreparedStatement in Java
What's new in Java 9,10,11
Parallel execution in Java
Initializing HashMap in Java
Call this cat API of Metadata Co., Ltd. in Java.