基於Spring的微信第三方登入實現
在前幾篇文章中,我們介紹了OAuth2.0認證和授權機制講解,並實現了基於Spring的Github第三方登入--通用化的第三方登陸實現,之後,我們基於該通用化的框架,介紹了國內的兩個比較流行的第三方登入平臺:基於Spring的新浪微博第三方登入實現和基於Spring的QQ第三方登入實現。以上幾個第三方登入平臺都嚴格遵守了OAuth2.0協議。但是,近來作者發現微信的第三方登入確不是那麼嚴格的遵守通用化的OAuth2.0協議,有些細節實現不太一樣(例如獲取AccessToken時其他OAuth平臺用的欄位名為client_id,但是微信中是appid),這就導致了之前的通用化框架在微信登入時遇到了很多問題,今天我們就來介紹一下微信的第三方登入,並重構一下我們的通用化框架,使之在遇到其他不那麼嚴格遵守OAuth協議的平臺依然簡單實用。
申請第三方應用
接下來我們首先來看看如何在微信開放平臺中申請一個第三方應用。
首先,我們需要完善開發者資質認證,在導航欄選擇【賬號中心】,然後選擇【開發者資質認證】,我們會看到如下頁面:
從頁面資訊介紹中的第二點我們可以知道,通過開發者資質認證後,我們就能夠獲得微信第三方登入的能力。需要注意:個人是不能申請第三方認證的,必須為以下機構或者團體才能進行認證,每次稽核需要花費300元:
開發者資質認證提交併繳費後,會在兩個工作日內進行稽核,如果有問題會有客服人員電話溝通。
稽核完成後,進入【管理中心】-> 【網站應用】。
點選【建立網站應用】,按照其提示填寫相關內容即可:
提交完成後,如果你的開發者資質已經通過,就可以直接進行開發上線啦。現在,讓我們看看如何新增微信第三方登入的程式碼。
第三方登入通用架構
微信第三方登入服務分析
首先,讓我們我們看看微信登入開發文件,看過之後,我們發現微信登入雖然是基於OAuth2.0協議,但是API的引數確不一樣。之前我們做過github、微博、QQ其獲取Access
Token時所需的引數為client_id
以及client_secret
,Scribe預設的OAuthService在處理也是同樣的引數,因此我們可以通過其預設的OAuth20ServiceImpl
來處理OAuth的相關操作。
但是微信中client_id
、client_secret
兩個引數統一用的是:appid
、secret
。這也就意味著我們需要實現自己的WeixinOAuth20Service
WeixinOAuthDeractorService
來適配通用化的第三方登陸實現中的設計。
但是WeixinOAuth20Service
以及WeixinOAuthDeractorService
實際上功能是一樣的,只是我們需要一個CustomOAuthService來對OAuthService進行管理。如果我們通過一個介面對OAuthService進行管理的話,我們就只需要新增一個類WeixinOAuthService
即可,同時這也符合依賴於介面而不是實現的最佳原則。
通過介面管理OAuthService
我們首先來新增一個介面CustomOAuthService
:
public interface CustomOAuthService extends OAuthService{
String getoAuthType();
String getAuthorizationUrl();
OAuthUser getOAuthUser(Token accessToken);
}
之後,我們將之前所有對OAuthServiceDeractor
修改為CustomOAuthService
:
@Service
public class OAuthServices {
@Autowired List<CustomOAuthService> oAuthServiceDeractors;
public CustomOAuthService getOAuthService(String type){
Optional<CustomOAuthService> oAuthService = oAuthServiceDeractors.stream().filter(o -> o.getoAuthType().equals(type))
.findFirst();
if(oAuthService.isPresent()){
return oAuthService.get();
}
return null;
}
public List<CustomOAuthService> getAllOAuthServices(){
return oAuthServiceDeractors;
}
}
這樣,我們對於第三方框架的重構就完成了。接下來我們來具體實現微信的第三方登入。
WeixinApi
首先,新增WeixinApi,為OAuthService提供進行OAuth驗證的各個地址,相關的地址可以在微信登入開發文件中得到,想了解微信第三方登入具體細節的同學可以仔細研讀一下該文件。最終的WeixinApi程式碼如下:
public class WeixinApi extends DefaultApi20 {
private static final String AUTHORIZE_URL = "https://open.weixin.qq.com/connect/qrconnect?appid=%s&redirect_uri=%s&response_type=code&state=esfadsgsad34fwdef&scope=snsapi_login#wechat_redirect";
private static final String ACCESS_TOKEN_URL = "https://api.weixin.qq.com/sns/oauth2/access_token?grant_type=authorization_code";
@Override
public String getAuthorizationUrl(OAuthConfig config) {
return String.format(AUTHORIZE_URL, config.getApiKey(), OAuthEncoder.encode(config.getCallback()));
}
@Override
public String getAccessTokenEndpoint() {
return ACCESS_TOKEN_URL;
}
@Override
public OAuthService createService(OAuthConfig config){
return new WeixinOAuthService(this, config);
}
}
WeixinOAuthService
最後是WeixinOAuthService的實現,除了CustomOAuthService的所定位的方法外,我們還需要重寫getAccessToken
:
public class WeixinOAuthService extends OAuth20ServiceImpl implements CustomOAuthService {
private final DefaultApi20 api;
private final OAuthConfig config;
private final String authorizationUrl;
public WeixinOAuthService(DefaultApi20 api, OAuthConfig config) {
super(api, config);
this.api = api;
this.config = config;
this.authorizationUrl = getAuthorizationUrl(null);
}
@Override
public Token getAccessToken(Token requestToken, Verifier verifier){
OAuthRequest request = new OAuthRequest(api.getAccessTokenVerb(), api.getAccessTokenEndpoint());
request.addQuerystringParameter("appid", config.getApiKey());
request.addQuerystringParameter("secret", config.getApiSecret());
request.addQuerystringParameter(OAuthConstants.CODE, verifier.getValue());
if(config.hasScope()) request.addQuerystringParameter(OAuthConstants.SCOPE, config.getScope());
Response response = request.send();
String responceBody = response.getBody();
Object result = JSON.parse(responceBody);
return new Token(JSONPath.eval(result, "$.access_token").toString(), "", responceBody);
}
@Override
public OAuthUser getOAuthUser(Token accessToken) {
OAuthUser oAuthUser = new OAuthUser();
oAuthUser.setoAuthType(getoAuthType());
Object result = JSON.parse(accessToken.getRawResponse());
oAuthUser.setoAuthId(JSONPath.eval(result, "$.openid").toString());
oAuthUser.setUser(new User());
return oAuthUser;
}
@Override
public String getoAuthType() {
return OAuthTypes.WEIXIN;
}
@Override
public String getAuthorizationUrl() {
return authorizationUrl;
}
}
配置WeixinOAuthService
最後新增WeixinOAuthService
的相關配置:
@Configuration
public class OAuthConfig {
private static final String CALLBACK_URL = "http://tianmaying.com/oauth/%s/callback";
@Value("${oAuth.weixin.appId}") String weixinAppId;
@Value("${oAuth.weixin.appSecret}") String weixinAppSecret;
@Bean
public CustomOAuthService getSinaOAuthService(){
return (CustomOAuthService) new ServiceBuilder()
.provider(WeixinApi.class)
.apiKey(weixinAppId)
.apiSecret(weixinAppSecret)
.scope("snsapi_login")
.callback(String.format(CALLBACK_URL, OAuthTypes.WEIXIN))
.build();
}
}
修改hosts
與新浪微博一樣,修改hosts一遍進行本地除錯。
windows系統hosts檔案一般在C:\WINDOWS\system32\drivers\etc
mac系統hosts檔案地址一般為:/etc/hosts
在hosts檔案新增以下一行:
127.0.0.1 tianmaying.com
除錯
進入根目錄,執行sudo mvn spring-boot:run
命令,訪問http://tianmaying.com (之前填寫應用資訊以及修改hosts時所填寫的域名,這三個域名必須一致),由於必須通過域名進行訪問,所以我們需要監聽80埠,執行時需要超級管理員許可權。