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 @@ -75,6 +75,7 @@ ScribeJava support out-of-box several HTTP clients:
* XING (https://www.xing.com/)
* Yahoo (https://www.yahoo.com/)
* Misfit (http://misfit.com/)
* WeChat (https://weixin.qq.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,81 @@
package com.github.scribejava.apis;

import com.github.scribejava.apis.service.WeChatOAuth20ServiceImpl;
import com.github.scribejava.apis.wechat.WeChatConstants;
import com.github.scribejava.apis.wechat.WeChatJsonTokenExtractor;
import com.github.scribejava.core.builder.api.DefaultApi20;
import com.github.scribejava.core.builder.api.SignatureType;
import com.github.scribejava.core.extractors.TokenExtractor;
import com.github.scribejava.core.model.OAuth2AccessToken;
import com.github.scribejava.core.model.OAuthConfig;
import com.github.scribejava.core.model.ParameterList;
import com.github.scribejava.core.oauth.OAuth20Service;

import java.util.Map;

/**
* WeChat OAuth 2.0 api.
*/
public class WeChatApi20 extends DefaultApi20 {

protected WeChatApi20() {
}

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

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

@Override
public String getAccessTokenEndpoint() {
return "https://api.weixin.qq.com/sns/oauth2/access_token";
}

@Override
protected String getAuthorizationBaseUrl() {
return "https://open.weixin.qq.com/connect/qrconnect";
}

@Override
public String getAuthorizationUrl(OAuthConfig config, Map<String, String> additionalParams) {

final ParameterList parameters = new ParameterList(additionalParams);
parameters.add(WeChatConstants.RESPONSE_TYPE, config.getResponseType());
parameters.add(WeChatConstants.CLIENT_ID, config.getApiKey());

final String callback = config.getCallback();
if (callback != null) {
parameters.add(WeChatConstants.REDIRECT_URI, callback);
}

final String scope = config.getScope();
if (scope != null) {
parameters.add(WeChatConstants.SCOPE, scope);
}

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

return parameters.appendTo(getAuthorizationBaseUrl());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pity. for single param name...
Maybe let it be like this:

final ParameterList parameters = new ParameterList(additionalParams);
parameters.add(WeChatConstants.CLIENT_ID, config.getApiKey());
return parameters.appendTo(super.getAuthorizationUrl(config, additionalParams));

or even

if (additionalParams==null) {
  additionalParams = Collections.singletonMap(WeChatConstants.CLIENT_ID, config.getApiKey());
} else {
  additionalParams.put(WeChatConstants.CLIENT_ID, config.getApiKey());
}
return super.getAuthorizationUrl(config, additionalParams);

}

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

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

@Override
public TokenExtractor<OAuth2AccessToken> getAccessTokenExtractor() {
return WeChatJsonTokenExtractor.instance();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.github.scribejava.apis.service;

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

public class WeChatOAuth20ServiceImpl extends OAuth20Service {

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

@Override
protected OAuthRequest createAccessTokenRequest(String code) {

final DefaultApi20 api = getApi();
final OAuthRequest request = new OAuthRequest(api.getAccessTokenVerb(), api.getAccessTokenEndpoint());
final OAuthConfig config = getConfig();

request.addParameter(WeChatConstants.CLIENT_ID, config.getApiKey());
request.addParameter(WeChatConstants.CLIENT_SECRET, config.getApiSecret());
request.addParameter(WeChatConstants.CODE, code);
request.addParameter(WeChatConstants.GRANT_TYPE, WeChatConstants.AUTHORIZATION_CODE);

return request;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the same here.
final OAuthRequest request = super.createAccessTokenRequest(code);
request.addParameter...
return request;

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.github.scribejava.apis.wechat;

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

/**
* This class contains OAuth constants, Custom for WeChat.
*/
public interface WeChatConstants extends OAuthConstants {

// WeChat's client_id is called appid and client_secret is called secret
String CLIENT_ID = "appid";
String CLIENT_SECRET = "secret";

String LANG = "lang";
String OPEN_ID = "openid";
String UNION_ID = "unionid";

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.github.scribejava.apis.wechat;

import com.github.scribejava.core.extractors.OAuth2AccessTokenJsonExtractor;

import java.util.regex.Pattern;

/**
* additionally parses openId openid and unionId unionid.
*/
public class WeChatJsonTokenExtractor extends OAuth2AccessTokenJsonExtractor {

private static final Pattern OPEN_ID_REGEX_PATTERN = Pattern.compile("\"openid\"\\s*:\\s*\"(\\S*?)\"");
private static final Pattern UNION_ID_REGEX_PATTERN = Pattern.compile("\"unionid\"\\s*:\\s*\"(\\S*?)\"");

protected WeChatJsonTokenExtractor() {
}

private static class InstanceHolder {

private static final WeChatJsonTokenExtractor INSTANCE = new WeChatJsonTokenExtractor();
}

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

@Override
protected WeChatToken createToken(String accessToken, String tokenType, Integer expiresIn,
String refreshToken, String scope, String response) {
return new WeChatToken(accessToken, tokenType, expiresIn, refreshToken, scope,
extractParameter(response, OPEN_ID_REGEX_PATTERN, false),
extractParameter(response, UNION_ID_REGEX_PATTERN, false), response);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package com.github.scribejava.apis.wechat;

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

import java.util.Objects;

public class WeChatToken extends OAuth2AccessToken {

// Unique identifier for Authorized user
private final String openId;

// User's unified identifier for a WeChat open platform account
private final String unionId;

public WeChatToken(String accessToken, String openId, String unionId, String rawResponse) {
this(accessToken, null, null, null, null, openId, unionId, rawResponse);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems we don't need this constructor. Constructor in GoogleToken as well.


public WeChatToken(String accessToken, String tokenType, Integer expiresIn, String refreshToken, String scope,
String openId, String unionId, String rawResponse) {
super(accessToken, tokenType, expiresIn, refreshToken, scope, rawResponse);
this.openId = openId;
this.unionId = unionId;

}

public String getOpenId() {
return openId;
}

public String getUnionId() {
return unionId;
}

@Override
public int hashCode() {
int hash = super.hashCode();
hash = 37 * hash + Objects.hashCode(openId);
hash = 37 * hash + Objects.hashCode(unionId);
return hash;
}

@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
if (!super.equals(obj)) {
return false;
}
if (!Objects.equals(openId, ((WeChatToken) obj).getOpenId())) {
return false;
}
return Objects.equals(unionId, ((WeChatToken) obj).getUnionId());
}

@Override
public String toString() {
return "WeChatToken{"
+ "access_token=" + getAccessToken()
+ ", expires_in=" + getExpiresIn()
+ ", refresh_token=" + getRefreshToken()
+ ", openid=" + openId
+ ", scope=" + getScope()
+ ", unionid=" + unionId
+ '}';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package com.github.scribejava.apis.examples;

import com.github.scribejava.apis.WeChatApi20;
import com.github.scribejava.apis.wechat.WeChatConstants;
import com.github.scribejava.apis.wechat.WeChatToken;
import com.github.scribejava.core.builder.ServiceBuilder;
import com.github.scribejava.core.model.OAuth2AccessToken;
import com.github.scribejava.core.model.OAuthRequest;
import com.github.scribejava.core.model.Response;
import com.github.scribejava.core.model.Verb;
import com.github.scribejava.core.oauth.OAuth20Service;

import java.io.IOException;
import java.util.Scanner;
import java.util.concurrent.ExecutionException;

public final class WeChat20Example {

private static final String NETWORK_NAME = "WeChat";
private static final String PROTECTED_RESOURCE_URL = "https://api.weixin.qq.com/sns/userinfo";

private WeChat20Example() {
}

public static void main(String... args) throws IOException, InterruptedException, ExecutionException {
// Replace these with your own api key and secret
final String apiKey = "your_appid";
final String apiSecret = "your_secret";
final OAuth20Service service = new ServiceBuilder()
.apiKey(apiKey)
.apiSecret(apiSecret)
.callback("http://your.site.com/callback") // your application's authorized callback field
.scope("snsapi_login")
.state("login")
.build(WeChatApi20.instance());
final Scanner in = new Scanner(System.in);

System.out.println("=== " + NETWORK_NAME + "'s OAuth Workflow ===");
System.out.println();

// Obtain the Authorization URL
System.out.println("Fetching the Authorization URL...");
final String authorizationUrl = service.getAuthorizationUrl();
System.out.println("Got the Authorization URL!");
System.out.println("Now go and authorize ScribeJava here:");
System.out.println(authorizationUrl);
System.out.println("And paste the authorization code here");
System.out.print(">>");
final String code = in.nextLine();
System.out.println();

// Trade the Request Token and Verifier for the Access Token
System.out.println("Trading the Request Token for an Access Token...");
final OAuth2AccessToken accessToken = service.getAccessToken(code);
System.out.println("Got the Access Token!");
System.out.println("(if your curious it looks like this: " + accessToken
+ ", 'rawResponse'='" + accessToken.getRawResponse() + "')");
System.out.println();

// Now let's go and ask for a protected resource!
System.out.println("Now we're going to access a protected resource...");

final OAuthRequest request = new OAuthRequest(Verb.GET, PROTECTED_RESOURCE_URL);
request.addQuerystringParameter(WeChatConstants.OPEN_ID, ((WeChatToken) accessToken).getOpenId());
request.addQuerystringParameter(WeChatConstants.LANG, "zh_CN");

service.signRequest(accessToken, request);
final Response response = service.execute(request);
System.out.println("Got it! Lets see what we found...");
System.out.println();
System.out.println(response.getCode());
System.out.println(response.getBody());

System.out.println();
System.out.println("Thats it man! Go and build something awesome with ScribeJava! :)");

}
}