Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ ScribeJava support out-of-box several HTTP clients:
* XING (https://www.xing.com/)
* Yahoo (https://www.yahoo.com/)
* Misfit (http://misfit.com/)
* WeChat (http://www.wechat.com/)
* check the [examples folder](https://github.com/scribejava/scribejava/tree/master/scribejava-apis/src/test/java/com/github/scribejava/apis/examples)

### Small and modular
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package com.github.scribejava.apis;

import com.github.scribejava.apis.service.WechatService;
import com.github.scribejava.apis.wechat.WechatOAuth2AccessTokenJsonExtractor;
import com.github.scribejava.apis.wechat.WechatOAuth2Constants;
import com.github.scribejava.core.builder.api.DefaultApi20;
import com.github.scribejava.core.builder.api.OAuth2SignatureType;
import com.github.scribejava.core.model.OAuthConfig;
import com.github.scribejava.core.model.ParameterList;
import com.github.scribejava.core.model.Verb;
import com.github.scribejava.core.oauth.OAuth20Service;

import java.util.Arrays;
import java.util.Map;

public class WechatApi extends DefaultApi20 {

protected WechatApi() {
}

private static class InstanceHolder {
private static final WechatApi INSTANCE = new WechatApi();
}

public static WechatApi instance() {
return InstanceHolder.INSTANCE;
}

@Override
public String getAccessTokenEndpoint() {
return WechatOAuth2Constants.ACCESS_TOKEN_ENDPOINT_URL;
}

@Override
public String getRefreshTokenEndpoint() {
return WechatOAuth2Constants.REFRESH_TOKEN_ENDPOINT_URL;
}

@Override
public WechatOAuth2AccessTokenJsonExtractor getAccessTokenExtractor() {
return WechatOAuth2AccessTokenJsonExtractor.instance();
}

@Override
public Verb getAccessTokenVerb() {
return Verb.GET;
}

/**
* @see <a href="https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140842">WeChat</a>
* The authorization provided by WeChat does not fully reference the oauth2.0.
* For example, the parameter name is not the same, and the parameter order requirements.
* so I have to override this method to meet the requirements of it.
*/
@Override
public String getAuthorizationUrl(OAuthConfig config, Map<String, String> additionalParams) {
final ParameterList parameters = new ParameterList(additionalParams);

parameters.add(WechatOAuth2Constants.CLIENT_ID, config.getApiKey());

final String callback = config.getCallback();
if (callback == null) {
throw new IllegalArgumentException("Missing required parameter 'redirect_uri'.");
}
parameters.add(WechatOAuth2Constants.REDIRECT_URI, callback);

final String responseType = config.getResponseType();
if (!WechatOAuth2Constants.CODE_RESPONSE_TYPE.equals(responseType)) {
throw new IllegalArgumentException("Parameter response_type must be 'code'.");
}
parameters.add(WechatOAuth2Constants.RESPONSE_TYPE, config.getResponseType());

final String scope = config.getScope();
if (scope == null || Arrays.binarySearch(WechatOAuth2Constants.SCOPE_VALUES, scope) < 0) {
throw new IllegalArgumentException("Parameter scope can only be 'snsapi_base' or 'snsapi_userinfo'.");
}
parameters.add(WechatOAuth2Constants.SCOPE, scope);

final String state = config.getState();
if (state != null) {
parameters.add(WechatOAuth2Constants.STATE, state);
}

return parameters.appendTo(getAuthorizationBaseUrl()).concat("#wechat_redirect");
}

@Override
public OAuth20Service createService(OAuthConfig config) {
return new WechatService(this, config);
}

@Override
public OAuth2SignatureType getSignatureType() {
return OAuth2SignatureType.BEARER_URI_QUERY_PARAMETER;
}

@Override
protected String getAuthorizationBaseUrl() {
return WechatOAuth2Constants.AUTHORIZE_URL;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package com.github.scribejava.apis.service;

import com.github.scribejava.apis.wechat.WechatOAuth2Constants;
import com.github.scribejava.core.builder.api.DefaultApi20;
import com.github.scribejava.core.model.OAuthConfig;
import com.github.scribejava.core.model.OAuthConstants;
import com.github.scribejava.core.model.OAuthRequest;
import com.github.scribejava.core.oauth.OAuth20Service;

import java.util.Map;


public class WechatService extends OAuth20Service {

public WechatService(DefaultApi20 api, OAuthConfig config) {
super(api, config);
}

/**
* The reason to override this method is the same as
* {@link com.github.scribejava.apis.WechatApi#getAuthorizationUrl(OAuthConfig, Map)}
*/
@Override
protected OAuthRequest createAccessTokenRequest(String code) {
final OAuthRequest request = new OAuthRequest(getApi().getAccessTokenVerb(), getApi().getAccessTokenEndpoint());
final OAuthConfig config = getConfig();

request.addParameter(WechatOAuth2Constants.CLIENT_ID, config.getApiKey());
request.addParameter(WechatOAuth2Constants.CLIENT_SECRET, config.getApiSecret());

request.addParameter(WechatOAuth2Constants.CODE, code);
request.addParameter(WechatOAuth2Constants.GRANT_TYPE, WechatOAuth2Constants.AUTHORIZATION_CODE);

return request;
}

/**
* The reason to override this method is the same as
* {@link com.github.scribejava.apis.WechatApi#getAuthorizationUrl(OAuthConfig, Map)}
*/
@Override
protected OAuthRequest createRefreshTokenRequest(String refreshToken) {
if (refreshToken == null || refreshToken.isEmpty()) {
throw new IllegalArgumentException("The refreshToken cannot be null or empty");
}
final OAuthRequest request = new OAuthRequest(getApi().getAccessTokenVerb(),
getApi().getRefreshTokenEndpoint());

request.addParameter(WechatOAuth2Constants.CLIENT_ID, getConfig().getApiKey());

request.addParameter(OAuthConstants.GRANT_TYPE, WechatOAuth2Constants.REFRESH_TOKEN);
request.addParameter(WechatOAuth2Constants.REFRESH_TOKEN, refreshToken);

return request;
}

@Override
protected OAuthRequest createAccessTokenPasswordGrantRequest(String username, String password) {
throw new UnsupportedOperationException("WeChat doesn't support Resource Owner Password Credentials Grant");
}

@Override
protected OAuthRequest createAccessTokenClientCredentialsGrantRequest() {
throw new UnsupportedOperationException("WeChat doesn't support Client Credentials Grant");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.github.scribejava.apis.wechat;

import com.github.scribejava.core.model.OAuth2AccessToken;

public class WechatOAuth2AccessToken extends OAuth2AccessToken {

private final String openid;

public WechatOAuth2AccessToken(String accessToken, Integer expiresIn, String refreshToken, String scope,
String openid, String response) {
super(accessToken, null, expiresIn, refreshToken, scope, response);
this.openid = openid;
}

public String getOpenid() {
return openid;
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof WechatOAuth2AccessToken)) {
return false;
}
if (!super.equals(o)) {
return false;
}

final WechatOAuth2AccessToken that = (WechatOAuth2AccessToken) o;

return openid.equals(that.openid);

}

@Override
public int hashCode() {
int result = super.hashCode();
result = 31 * result + openid.hashCode();
return result;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package com.github.scribejava.apis.wechat;

import com.github.scribejava.core.exceptions.OAuthException;

public class WechatOAuth2AccessTokenErrorResponse extends OAuthException {

private String errcode;

private String errmsg;

private String rawResponse;

public WechatOAuth2AccessTokenErrorResponse(String message) {
super(message);
}

public WechatOAuth2AccessTokenErrorResponse(String message, String errcode, String errmsg, String rawResponse) {
super(message);
this.errcode = errcode;
this.errmsg = errmsg;
this.rawResponse = rawResponse;
}

public String getErrcode() {
return errcode;
}

public String getErrmsg() {
return errmsg;
}

public String getRawResponse() {
return rawResponse;
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}

final WechatOAuth2AccessTokenErrorResponse that = (WechatOAuth2AccessTokenErrorResponse) o;

if (errcode != null ? !errcode.equals(that.errcode) : that.errcode != null) {
return false;
}
if (errmsg != null ? !errmsg.equals(that.errmsg) : that.errmsg != null) {
return false;
}
return rawResponse != null ? rawResponse.equals(that.rawResponse) : that.rawResponse == null;

}

@Override
public int hashCode() {
int result = errcode != null ? errcode.hashCode() : 0;
result = 31 * result + (errmsg != null ? errmsg.hashCode() : 0);
result = 31 * result + (rawResponse != null ? rawResponse.hashCode() : 0);
return result;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package com.github.scribejava.apis.wechat;

import com.github.scribejava.core.extractors.OAuth2AccessTokenJsonExtractor;
import com.github.scribejava.core.model.OAuth2AccessToken;
import com.github.scribejava.core.model.Response;
import com.github.scribejava.core.utils.Preconditions;

import java.io.IOException;
import java.util.regex.Pattern;

public class WechatOAuth2AccessTokenJsonExtractor extends OAuth2AccessTokenJsonExtractor {

private static final Pattern ERRCODE_REGEX_PATTERN = Pattern.compile("\"errcode\"\\s*:\\s*([0-9]+)");
private static final Pattern ERRMSG_REGEX_PATTERN = Pattern.compile("\"errmsg\"\\s*:\\s*\"([\\S\\s]*?)\"");
private static final Pattern OPENID_REGEX_PATTERN = Pattern.compile("\"openid\"\\s*:\\s*\"(\\S*?)\"");

protected WechatOAuth2AccessTokenJsonExtractor() {
}

private static class InstanceHolder {
private static final WechatOAuth2AccessTokenJsonExtractor INSTANCE = new WechatOAuth2AccessTokenJsonExtractor();
}

public static WechatOAuth2AccessTokenJsonExtractor instance() {
return InstanceHolder.INSTANCE;
}

@Override
public OAuth2AccessToken extract(Response response) throws IOException {
checkError(response.getBody());
return super.extract(response);
}

/**
* Try to extract the error information in response. If 'errcode' or 'errmsg' is found,
* {@link WechatOAuth2AccessTokenErrorResponse} will be thrown, otherwise execute super method.
*/
private void checkError(final String response) {
if (!Preconditions.hasText(response)) {
return;
}
final String errcode = extractParameter(response, ERRCODE_REGEX_PATTERN, false);
final String errmsg = extractParameter(response, ERRMSG_REGEX_PATTERN, false);

if (errcode != null || errmsg != null) {
throw new WechatOAuth2AccessTokenErrorResponse("Obtaining WeChat OAuth2 access_token failed.",
errcode, errmsg, response);
}
}

@Override
public void generateError(String response) {
throw new WechatOAuth2AccessTokenErrorResponse("An unknown failure occurred" +
" while obtaining the WeChat OAuth2 access_token.");
}

@Override
protected WechatOAuth2AccessToken createToken(String accessToken, String tokenType, Integer expiresIn,
String refreshToken, String scope, String response) {
final String openid = extractParameter(response, OPENID_REGEX_PATTERN, true);
return new WechatOAuth2AccessToken(accessToken, expiresIn, refreshToken, scope, openid, response);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.github.scribejava.apis.wechat;

import com.github.scribejava.core.model.OAuthConstants;

public interface WechatOAuth2Constants extends OAuthConstants {

String ACCESS_TOKEN_ENDPOINT_URL = "https://api.weixin.qq.com/sns/oauth2/access_token";

String REFRESH_TOKEN_ENDPOINT_URL = "https://api.weixin.qq.com/sns/oauth2/refresh_token";

String AUTHORIZE_URL = "https://open.weixin.qq.com/connect/oauth2/authorize";

String CODE_RESPONSE_TYPE = "code";

String[] SCOPE_VALUES = new String[]{"snsapi_base", "snsapi_userinfo "};

String CLIENT_ID = "appid";

String CLIENT_SECRET = "secret";
}
Loading