cn.bestwu.security.oauth2.social.provider.SocialAdapter Maven / Gradle / Ivy
package cn.bestwu.security.oauth2.social.provider;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.OAuth2Request;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;
import javax.annotation.PostConstruct;
import java.util.*;
/**
* 社交账号登录适配
* 默认已适配:QQ/微信/微博/腾讯微博
*
* @param 系统用户
*/
public abstract class SocialAdapter {
/**
* 微信 provider id grantType
*/
public static final String WEIXIN = "weixin";
/**
* QQ provider id grantType
*/
public static final String QQ = "qq";
/**
* 腾讯微博 provider id grantType
*/
public static final String TQQ = "tqq";
/**
* 微博 provider id grantType
*/
public static final String WEIBO = "weibo";
protected final Log logger = LogFactory.getLog(getClass());
/**
* 支持的社交类型
*/
private final Set supportProviders;
private final RestTemplate restTemplate;
/**
* 是否自动注册
*/
private boolean autoLogon;
/**
* QQ appid
*/
private String qqAppId;
/**
* 是否自动刷新用户资料
*/
private boolean refreshUserProfile;
//--------------------------------------------
@Autowired
private SocialProperties socialProperties;
public SocialAdapter() {
this.restTemplate = createRestTemplate();
this.supportProviders = new HashSet<>(4);
this.supportProviders.add(WEIXIN);
this.supportProviders.add(QQ);
this.supportProviders.add(TQQ);
this.supportProviders.add(WEIBO);
}
@PostConstruct
public void init() {
this.qqAppId = socialProperties.getQqAppKey();
this.autoLogon = socialProperties.isAutoLogon();
this.refreshUserProfile = socialProperties.isRefreshUserProfile();
}
//--------------------------------------------
/**
* @return 是否自动刷新用户资料
*/
public boolean isRefreshUserProfile() {
return refreshUserProfile;
}
/**
* @return 是否自动注册
*/
public boolean isAutoLogon() {
return autoLogon;
}
//--------------------------------------------
/**
* 查找系统中是否存在注册的社交账号
*
* @param pid 社交账号类型
* @param puid 社交账号用户ID
* @return 注册的账号
*/
public abstract SocialId findByPidAndPuid(String pid, String puid);
/**
* 刷新用户资料
*
* @param user 系统用户
* @param userProfile 社交用户
*/
public abstract void saveUserProfile(T user, UserProfile userProfile);
/**
* @param userProfile 用户资料,注意处理imageHref
* @return 注册到系统的社交账号
*/
public abstract SocialId signup(UserProfile userProfile);
/**
* @param user user
* @return UserDetails
*/
public abstract UserDetails loadUserDetailsByUser(T user);
/**
* 解析扩展资料
*
* @param pid 社交账号提供方
* @param originalUserProfile 原始资料
* @return 扩展资料
*/
public abstract Map findUserProfileExtra(String pid, Map originalUserProfile);
//--------------------------------------------
protected RestTemplate createRestTemplate() {
RestTemplate restTemplate = new RestTemplate();
MappingJackson2HttpMessageConverter jackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();
List supportedMediaTypes = new ArrayList<>(jackson2HttpMessageConverter.getSupportedMediaTypes());
supportedMediaTypes.add(MediaType.TEXT_PLAIN);
supportedMediaTypes.add(MediaType.TEXT_HTML);
jackson2HttpMessageConverter.setSupportedMediaTypes(supportedMediaTypes);
restTemplate.setMessageConverters(Collections.singletonList(jackson2HttpMessageConverter));
return restTemplate;
}
/**
* @param user 系统用户
* @param clientId clientId
* @param scope scope
* @return OAuth2Authentication
*/
public OAuth2Authentication createOAuth2Authentication(T user, String clientId, Set scope) {
OAuth2Request request = new OAuth2Request(null, clientId, null, true, scope,
null, null, null, null);
UserDetails userDetails = loadUserDetailsByUser(user);
return new OAuth2Authentication(request, new UsernamePasswordAuthenticationToken(userDetails, "N/A", userDetails.getAuthorities()));
}
/**
* 验证社交账号social_token
*
* @param pid 社交账号提供方
* @param puid 社交账号用户id
* @param social_token 社交账号access_token
* @return 社交账号用户资料
* @throws SocialException social_token不正确抛出异常
*/
public Map validateSocialTokenAndGetUserOriginalProfile(String pid, String puid, String social_token) throws SocialException {
Map originalProfile = null;
switch (pid) {
case WEIXIN:
originalProfile = restTemplate.getForObject("https://api.weixin.qq.com/sns/userinfo?access_token={0}&openid={1}&lang=zh_CN", Map.class, social_token, puid);
if (logger.isDebugEnabled()) {
logger.debug(social_token);
logger.debug(originalProfile);
}
if (originalProfile.get("errcode") != null) {
throw new SocialException((String) originalProfile.get("errmsg"));
}
break;
case TQQ://腾讯微博资料 scope:get_info
originalProfile = restTemplate.getForObject("https://graph.qq.com/user/get_info?access_token={0}&oauth_consumer_key={1}&openid={2}&format=json", Map.class, social_token, qqAppId, puid);
if (logger.isDebugEnabled()) {
logger.debug(social_token);
logger.debug(originalProfile);
}
if (0 != (int) originalProfile.get("ret")) {
throw new SocialException((String) originalProfile.get("msg"));
} else {
originalProfile = (Map) originalProfile.get("data");
}
break;
case QQ://腾讯QQ资料 scope:get_user_info
originalProfile = restTemplate.getForObject("https://graph.qq.com/user/get_user_info?access_token={0}&oauth_consumer_key={1}&openid={2}", Map.class, social_token, qqAppId, puid);
if (logger.isDebugEnabled()) {
logger.debug(social_token);
logger.debug(originalProfile);
}
if (0 != (int) originalProfile.get("ret")) {
throw new SocialException((String) originalProfile.get("msg"));
}
break;
case WEIBO:
originalProfile = restTemplate.getForObject("https://api.weibo.com/2/users/show.json?access_token={0}&uid={1}", Map.class, social_token, puid);
if (logger.isDebugEnabled()) {
logger.debug(social_token);
logger.debug(originalProfile);
}
if (originalProfile.get("error_code") != null) {
throw new SocialException((String) originalProfile.get("error"));
}
break;
}
return originalProfile;
}
/**
* @param pid 社交账号提供方
* @param puid 社交账号用户id
* @param originalUserProfile 原始社交账号资料
* @return UserProfile
*/
public UserProfile originalProfile2UserProfile(String pid, String puid, Map originalUserProfile) {
UserProfile userProfile = new UserProfile(pid, puid);
switch (pid) {
case WEIXIN:
userProfile.setNickname((String) originalUserProfile.get("nickname"));
userProfile.setSex(getSex(originalUserProfile));
userProfile.setImageHref((String) originalUserProfile.get("headimgurl"));
break;
case TQQ:
userProfile.setNickname((String) originalUserProfile.get("nick"));
userProfile.setSex(getSex(originalUserProfile));
String head = (String) originalUserProfile.get("head");
if (StringUtils.hasText(head)) {
userProfile.setImageHref(head + "/100");
}
break;
case QQ:
userProfile.setNickname((String) originalUserProfile.get("nickname"));
String qqgender = (String) originalUserProfile.get("gender");
Sex qqsex;
if (qqgender == null) {
qqsex = Sex.UNKNOWN;
} else {
switch (qqgender) {
case "男":
qqsex = Sex.MALE;
break;
case "女":
qqsex = Sex.FEMALE;
break;
default:
qqsex = Sex.UNKNOWN;
break;
}
}
userProfile.setSex(qqsex);
String figureurl_qq = (String) originalUserProfile.get("figureurl_qq_2");
if (!StringUtils.hasText(figureurl_qq)) {
figureurl_qq = (String) originalUserProfile.get("figureurl_qq_1");
}
userProfile.setImageHref(figureurl_qq);
break;
case WEIBO:
userProfile.setNickname((String) originalUserProfile.get("screen_name"));
String gender = (String) originalUserProfile.get("gender");
Sex sex;
if (gender == null) {
sex = Sex.UNKNOWN;
} else {
switch (gender) {
case "m":
sex = Sex.MALE;
break;
case "f":
sex = Sex.FEMALE;
break;
default:
case "n":
sex = Sex.UNKNOWN;
break;
}
}
userProfile.setSex(sex);
userProfile.setImageHref((String) originalUserProfile.get("avatar_large"));
break;
}
Map userProfileExtra = findUserProfileExtra(pid, originalUserProfile);
userProfile.setExtra(userProfileExtra);
return userProfile;
}
/**
* 解析性别
*
* @param originalUserProfile 原始用户资料
* @return 性别
*/
private Sex getSex(Map originalUserProfile) {
Integer originalSex = (Integer) originalUserProfile.get("sex");
Sex sex;
if (originalSex == null) {
sex = Sex.UNKNOWN;
} else {
switch (originalSex) {
case 1:
sex = Sex.MALE;
break;
case 2:
sex = Sex.FEMALE;
break;
default:
case 0:
sex = Sex.UNKNOWN;
break;
}
}
return sex;
}
//--------------------------------------------
/**
* @param grantType grantType
* @return 是否支持指定 grantType的登录
*/
public boolean support(String grantType) {
return supportProviders.contains(grantType.toLowerCase());
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy