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.
Get the data by hitting Zaim's OAuth 1.0 API.
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')
}
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.
Learn more about OAuth 1.0 here. http://yuroyoro.hatenablog.com/entry/20100506/1273137673
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());
}
}
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");
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;
}
}
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);
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.
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);
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