明确协议中的三个参与对象:
准备工作:
第三方应用开发者在服务提供方注册应用,并获得:
client_id(也有叫appid或其他,如微信):第三方应用在服务提供方的id
client_secret(也有叫appSecret或其他,如微信):服务提供方给第三方应用提供的私钥,第三方应用应该把它放在安全的地方,不能对外暴露
一)、OAuth2
二)、OAuth1
以下的泛型参数A都只授权服务提供方提供的接口
1、Connection<A>
这是整个oauth认证过程的最终结果,得到Connection后,就可以对授权提供方获取被授权获取的资源。Connection是用户在第三方应用和授权服务提供方账号的绑定抽象,具体看org.springframework.social.connect.Connection的java doc
2、ConnectionFactory<A>
Connection<A>的工厂类,也是封装之后外部直接打交道的入口类,见org.springframework.social.connect.ConnectionFactory 的java doc。它一般通过ConnectionFactoryLocator获得(因为你可能不止使用一个授权服务提供方,比如同时支持微信、微博、qq等)
3、ServiceProvider<A>
服务提供者的标记接口,根据使用的协议有子接口OAuth1ServiceProvider<A>,OAuth2ServiceProvider<A>,它们都有getOAuthOperations和getApi方法(但是参数或返回不同),分别提供认证流程抽象和API
以上就是我们实现自己的ServiceProvider需要实现的接口或者继承的抽象类,但Spring Social为我们提供了Connection的实现(根据使用的协议不同,有OAuth2Connection<A>和OAuth1Connection<A>),所以我们不需要自己实现Connection了。
三、实现微信ServiceProvider(以微信为授权服务提供方)
关于包名以及包结构的约定:
当提供自定义ServiceProvider是实现时,Spring Social推荐的根包名为:org.springframework.social.{providerId},其中{providerId}是服务提供方的标识,比如wechat、weibo、facebook等等。然后下面提供两个子包api和connect:其中api用来定义接口,在api包下面提供包impl放接口的实现;connect用来实现连接相关的接口、抽象类
1、抽象微信提供的接口
定义接口时,一般是提供一个总接口,总接口用于提供各类子接口,比如为微信提供一个总结口WeChat,然后在通过WeChat根据Rest资源的分类获取不同的接口。比如,用户信息获取和更新等操作的接口定义为UserOperations,发送和接收消息等消息处理的接口定义为MessageOperations(微信目前没有提供,只是假设),然后使用WeChat接口获取这些子接口。以微信为例,展示结构:
总结口WeChat:
package org.springframework.social.wechat.api;
import org.springframework.social.ApiBinding;
/**
* Interface specifying a basic set of operations for interacting with WeChat.
*
* @author huangjinzhou
*/
public interface WeChat extends ApiBinding {
/**
* Returns the portion of the WeChat API containing the user operations.
*
* @return user operations
*/
UserOperations userOperations();
}
子接口UserOperations:
package org.springframework.social.wechat.api;
/**
* Interface defining the operations for working with WeChat users.
*
* @author huangjinzhou
*/
public interface UserOperations {
/**
* Retrieves the user's WeChat profile details.
*
* @param openId 普通用户的标识
* @return the user's WeChat profile
*/
WeChatUserProfile getUserProfile(String openId);
}
2、实现微信接口
具体看github代码啦,实现的WeChat的时候继承AbstractOAuth2ApiBinding,如果你实现的服务提供方使用的是OAuth1协议,则继承AbstractOAuth1ApiBinding,里面替我们做了很多烦人的事情
3、实现ApiAdapter<A>
这个是用来适配Connection的API适配器,及在Connection实现中调用适配器来获得用户信息、测试连接是否有效等
package org.springframework.social.wechat.connect;
import org.springframework.social.connect.ApiAdapter;
import org.springframework.social.connect.ConnectionValues;
import org.springframework.social.connect.UserProfile;
import org.springframework.social.connect.UserProfileBuilder;
import org.springframework.social.wechat.api.WeChat;
import org.springframework.social.wechat.api.WeChatUserProfile;
/**
* WeChat ApiAdapter implementation
*
* @author huangjinzhou
* @date 2019-05-30 10:13
*/
public class WeChatApiAdapter implements ApiAdapter<WeChat> {
private final String openId;
public WeChatApiAdapter(String openId) {
this.openId = openId;
}
@Override
public boolean test(WeChat api) {
try {
api.userOperations().getUserProfile(openId);
return true;
} catch (Exception ignored) {
}
return false;
}
@Override
public void setConnectionValues(WeChat api, ConnectionValues values) {
WeChatUserProfile userProfile = api.userOperations().getUserProfile(openId);
if (userProfile == null) {
return;
}
values.setDisplayName(userProfile.getNickname());
values.setProviderUserId(userProfile.getOpenId());
values.setImageUrl(userProfile.getHeadImageUrl());
values.setProfileUrl(userProfile.getHeadImageUrl());
}
@Override
public UserProfile fetchUserProfile(WeChat api) {
WeChatUserProfile userProfile = api.userOperations().getUserProfile(openId);
return new UserProfileBuilder()
.setName(userProfile.getNickname())
.setId(userProfile.getOpenId())
.setUsername(userProfile.getNickname())
.setFirstName(userProfile.getNickname())
.build();
}
@Override
public void updateStatus(WeChat api, String message) {
}
}
4、实现ServiceProvider<A>
package org.springframework.social.wechat.connect;
import org.springframework.social.oauth2.AbstractOAuth2ServiceProvider;
import org.springframework.social.oauth2.OAuth2ServiceProvider;
import org.springframework.social.wechat.api.WeChat;
import org.springframework.social.wechat.api.impl.WeChatTemplate;
/**
* @author huangjinzhou
* @date 2019/5/29 21:04
*/
public class WeChatServiceProvider extends AbstractOAuth2ServiceProvider<WeChat> {
/**
* Create a new {@link OAuth2ServiceProvider}.
*
* @param appId appId
* @param appSecret appSecret
*/
public WeChatServiceProvider(String appId, String appSecret) {
super(new WeChatOAuth2Template(appId, appSecret, "https://open.weixin.qq.com/connect/qrconnect",
"https://api.weixin.qq.com/sns/oauth2/access_token",
"https://api.weixin.qq.com/sns/oauth2/refresh_token"));
}
@Override
public WeChat getApi(String accessToken) {
return new WeChatTemplate(accessToken);
}
}
5、实现ConnectionFactory<A>
package org.springframework.social.wechat.connect;
import org.springframework.social.connect.Connection;
import org.springframework.social.connect.ConnectionData;
import org.springframework.social.connect.support.OAuth2Connection;
import org.springframework.social.connect.support.OAuth2ConnectionFactory;
import org.springframework.social.oauth2.AccessGrant;
import org.springframework.social.oauth2.OAuth2Operations;
import org.springframework.social.oauth2.OAuth2ServiceProvider;
import org.springframework.social.wechat.api.WeChat;
/**
* WeChat ConnectionFactory Implementation
*
* @author huangjinzhou
* @date 2019-05-30 10:48
*/
public class WeChatConnectionFactory extends OAuth2ConnectionFactory<WeChat> {
/**
* Create a {@link OAuth2ConnectionFactory}.
*
* @param appId the application id in WeChat of the application.
* @param appSecret the application secret in WeChat of the application.
*/
public WeChatConnectionFactory(String appId, String appSecret) {
super("wechat", new WeChatServiceProvider(appId, appSecret), null);
}
/**
* Create a OAuth2-based {@link Connection} from the {@link AccessGrant} returned after {@link #getOAuthOperations() completing the OAuth2 flow}.
*
* @param accessGrant the access grant
* @return the new service provider connection
* @see OAuth2Operations#exchangeForAccess(String, String, org.springframework.util.MultiValueMap)
*/
@Override
public Connection<WeChat> createConnection(AccessGrant accessGrant) {
return new OAuth2Connection<>(getProviderId(),
extractProviderUserId(accessGrant),
accessGrant.getAccessToken(),
accessGrant.getRefreshToken(),
accessGrant.getExpireTime(),
(OAuth2ServiceProvider<WeChat>) getServiceProvider(),
new WeChatApiAdapter(((WeChatAccessGrant) accessGrant).getOpenId()));
}
/**
* Create a OAuth2-based {@link Connection} from the connection data.
*
* @param data connection data from which to create the connection
*/
@Override
public Connection<WeChat> createConnection(ConnectionData data) {
return new OAuth2Connection<>(data, (OAuth2ServiceProvider<WeChat>) getServiceProvider(),
new WeChatApiAdapter(data.getProviderUserId()));
}
@Override
protected String extractProviderUserId(AccessGrant accessGrant) {
return ((WeChatAccessGrant) accessGrant).getOpenId();
}
}
到此,自定ServiceProvider就实现了
1、它的API调用是需要openid作参数的,所以WeChatApiAdapter还需要一个openid做构造函数入参,而其他一些标准平台是不需要的,比如facebook、linkin等。因而WeChatConnectionFactory就需要复写一些涉及ApiAdapter的方法,比如createConnection
2、认证流程发送的参数不是标准的client_id和client_secret而是appid和secret,所以需要提供自己的OAuthOperations实现(其实只要继承OAuth2Template,然后复写关于client_id和client_secret构造部分就好了),github里的WeChatTemplate是抄别人的
啊! 烂尾了,虽然整篇都烂,算自己的笔记吧