@@ -20,12 +20,53 @@ | |||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | |||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> | |||
<java.version>1.8</java.version> | |||
<spring.social.version>1.1.6.RELEASE</spring.social.version> | |||
</properties> | |||
<dependencies> | |||
<dependency> | |||
<groupId>org.springframework.boot</groupId> | |||
<artifactId>spring-boot-starter</artifactId> | |||
<artifactId>spring-boot-starter-web</artifactId> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.springframework.boot</groupId> | |||
<artifactId>spring-boot-starter-jdbc</artifactId> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.springframework.boot</groupId> | |||
<artifactId>spring-boot-starter-security</artifactId> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.springframework.social</groupId> | |||
<artifactId>spring-social-config</artifactId> | |||
<version>${spring.social.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.springframework.social</groupId> | |||
<artifactId>spring-social-core</artifactId> | |||
<version>${spring.social.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.springframework.social</groupId> | |||
<artifactId>spring-social-security</artifactId> | |||
<version>${spring.social.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.springframework.social</groupId> | |||
<artifactId>spring-social-web</artifactId> | |||
<version>${spring.social.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.springframework.boot</groupId> | |||
<artifactId>spring-boot-configuration-processor</artifactId> | |||
<optional>true</optional> | |||
</dependency> | |||
<dependency> | |||
@@ -33,6 +74,27 @@ | |||
<artifactId>spring-boot-starter-test</artifactId> | |||
<scope>test</scope> | |||
</dependency> | |||
<dependency> | |||
<groupId>mysql</groupId> | |||
<artifactId>mysql-connector-java</artifactId> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.projectlombok</groupId> | |||
<artifactId>lombok</artifactId> | |||
<optional>true</optional> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.google.guava</groupId> | |||
<artifactId>guava</artifactId> | |||
</dependency> | |||
<dependency> | |||
<groupId>cn.hutool</groupId> | |||
<artifactId>hutool-all</artifactId> | |||
</dependency> | |||
</dependencies> | |||
<build> | |||
@@ -1,7 +1,9 @@ | |||
package com.xkcoding.social; | |||
import com.xkcoding.social.config.SocialProperties; | |||
import org.springframework.boot.SpringApplication; | |||
import org.springframework.boot.autoconfigure.SpringBootApplication; | |||
import org.springframework.boot.context.properties.EnableConfigurationProperties; | |||
/** | |||
* <p> | |||
@@ -0,0 +1,32 @@ | |||
package com.xkcoding.social.config; | |||
import org.springframework.social.security.SocialAuthenticationFilter; | |||
import org.springframework.social.security.SpringSocialConfigurer; | |||
/** | |||
* <p> | |||
* 继承默认的社交登录配置,加入自定义的后处理逻辑 | |||
* </p> | |||
* | |||
* @package: com.xkcoding.social.config | |||
* @description: 继承默认的社交登录配置,加入自定义的后处理逻辑 | |||
* @author: yangkai.shen | |||
* @date: Created in 2019-02-21 14:01 | |||
* @copyright: Copyright (c) 2019 | |||
* @version: V1.0 | |||
* @modified: yangkai.shen | |||
*/ | |||
public class DemoSocialConfigure extends SpringSocialConfigurer { | |||
private String filterProcessUrl; | |||
public DemoSocialConfigure(String filterProcessUrl) { | |||
this.filterProcessUrl = filterProcessUrl; | |||
} | |||
@Override | |||
protected <T> T postProcess(T object) { | |||
SocialAuthenticationFilter socialAuthenticationFilter = (SocialAuthenticationFilter) super.postProcess(object); | |||
socialAuthenticationFilter.setFilterProcessesUrl(filterProcessUrl); | |||
return (T) socialAuthenticationFilter; | |||
} | |||
} |
@@ -0,0 +1,34 @@ | |||
package com.xkcoding.social.config; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import org.springframework.security.core.authority.AuthorityUtils; | |||
import org.springframework.security.core.userdetails.UsernameNotFoundException; | |||
import org.springframework.security.crypto.password.PasswordEncoder; | |||
import org.springframework.social.security.SocialUser; | |||
import org.springframework.social.security.SocialUserDetails; | |||
import org.springframework.social.security.SocialUserDetailsService; | |||
import org.springframework.stereotype.Service; | |||
/** | |||
* <p> | |||
* 根据userId获取用户信息 | |||
* </p> | |||
* | |||
* @package: com.xkcoding.social.config | |||
* @description: 根据userId获取用户信息 | |||
* @author: yangkai.shen | |||
* @date: Created in 2019-02-21 14:41 | |||
* @copyright: Copyright (c) 2019 | |||
* @version: V1.0 | |||
* @modified: yangkai.shen | |||
*/ | |||
@Service | |||
public class DemoUserDetailService implements SocialUserDetailsService { | |||
@Autowired | |||
private PasswordEncoder passwordEncoder; | |||
@Override | |||
public SocialUserDetails loadUserByUserId(String userId) throws UsernameNotFoundException { | |||
return new SocialUser(userId, passwordEncoder.encode("123456"), AuthorityUtils.commaSeparatedStringToAuthorityList("xxx")); | |||
} | |||
} |
@@ -0,0 +1,34 @@ | |||
package com.xkcoding.social.config; | |||
import lombok.Data; | |||
/** | |||
* <p> | |||
* QQ 配置 | |||
* </p> | |||
* | |||
* @package: com.xkcoding.social.config | |||
* @description: QQ 配置 | |||
* @author: yangkai.shen | |||
* @date: Created in 2019-02-21 14:20 | |||
* @copyright: Copyright (c) 2019 | |||
* @version: V1.0 | |||
* @modified: yangkai.shen | |||
*/ | |||
@Data | |||
public class QQProperties { | |||
/** | |||
* 第三方应用标识 | |||
*/ | |||
private String providerId = "qq"; | |||
/** | |||
* clientId | |||
*/ | |||
private String clientId; | |||
/** | |||
* clientSecret | |||
*/ | |||
private String clientSecret; | |||
} |
@@ -0,0 +1,67 @@ | |||
package com.xkcoding.social.config; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import org.springframework.boot.context.properties.EnableConfigurationProperties; | |||
import org.springframework.context.annotation.Bean; | |||
import org.springframework.context.annotation.Configuration; | |||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; | |||
import org.springframework.security.crypto.encrypt.Encryptors; | |||
import org.springframework.security.crypto.password.PasswordEncoder; | |||
import org.springframework.social.UserIdSource; | |||
import org.springframework.social.config.annotation.EnableSocial; | |||
import org.springframework.social.config.annotation.SocialConfigurerAdapter; | |||
import org.springframework.social.connect.ConnectionFactoryLocator; | |||
import org.springframework.social.connect.UsersConnectionRepository; | |||
import org.springframework.social.connect.jdbc.JdbcUsersConnectionRepository; | |||
import org.springframework.social.security.AuthenticationNameUserIdSource; | |||
import org.springframework.social.security.SpringSocialConfigurer; | |||
import javax.sql.DataSource; | |||
/** | |||
* <p> | |||
* 社交登录配置 | |||
* </p> | |||
* | |||
* @package: com.xkcoding.social.config | |||
* @description: 社交登录配置 | |||
* @author: yangkai.shen | |||
* @date: Created in 2019-02-21 13:52 | |||
* @copyright: Copyright (c) 2019 | |||
* @version: V1.0 | |||
* @modified: yangkai.shen | |||
*/ | |||
@Configuration | |||
@EnableSocial | |||
@EnableConfigurationProperties({SocialProperties.class}) | |||
public class SocialConfig extends SocialConfigurerAdapter { | |||
@Autowired | |||
private DataSource dataSource; | |||
@Autowired | |||
private SocialProperties socialProperties; | |||
@Override | |||
public UsersConnectionRepository getUsersConnectionRepository(ConnectionFactoryLocator connectionFactoryLocator) { | |||
JdbcUsersConnectionRepository jdbcUsersConnectionRepository = new JdbcUsersConnectionRepository(dataSource, connectionFactoryLocator, Encryptors | |||
.noOpText()); | |||
jdbcUsersConnectionRepository.setTablePrefix("demo_"); | |||
return jdbcUsersConnectionRepository; | |||
} | |||
@Bean | |||
public SpringSocialConfigurer springSocialConfigurer() { | |||
DemoSocialConfigure demoSocialConfigure = new DemoSocialConfigure(socialProperties.getFilterProcessUrl()); | |||
return demoSocialConfigure; | |||
} | |||
@Bean | |||
public PasswordEncoder passwordEncoder() { | |||
return new BCryptPasswordEncoder(); | |||
} | |||
@Override | |||
public UserIdSource getUserIdSource() { | |||
return new AuthenticationNameUserIdSource(); | |||
} | |||
} |
@@ -0,0 +1,25 @@ | |||
package com.xkcoding.social.config; | |||
import lombok.Data; | |||
import org.springframework.boot.context.properties.ConfigurationProperties; | |||
/** | |||
* <p> | |||
* 社交登录配置类 | |||
* </p> | |||
* | |||
* @package: com.xkcoding.social.config | |||
* @description: 社交登录配置类 | |||
* @author: yangkai.shen | |||
* @date: Created in 2019-02-21 14:12 | |||
* @copyright: Copyright (c) 2019 | |||
* @version: V1.0 | |||
* @modified: yangkai.shen | |||
*/ | |||
@Data | |||
@ConfigurationProperties(prefix = "demo.social") | |||
public class SocialProperties { | |||
private String filterProcessUrl = "/auth"; | |||
private QQProperties qq = new QQProperties(); | |||
} |
@@ -0,0 +1,24 @@ | |||
package com.xkcoding.social.qq.api; | |||
/** | |||
* <p> | |||
* 获取QQ用户信息接口 | |||
* </p> | |||
* | |||
* @package: com.xkcoding.social.qq.api | |||
* @description: 获取QQ用户信息接口 | |||
* @author: yangkai.shen | |||
* @date: Created in 2019-02-21 10:52 | |||
* @copyright: Copyright (c) 2019 | |||
* @version: V1.0 | |||
* @modified: yangkai.shen | |||
*/ | |||
public interface QQ { | |||
/** | |||
* 获取QQ用户信息 | |||
* @return QQ用户信息 | |||
*/ | |||
QQUserInfo getUserInfo(); | |||
} |
@@ -0,0 +1,82 @@ | |||
package com.xkcoding.social.qq.api; | |||
import lombok.Data; | |||
/** | |||
* <p> | |||
* QQ用户信息 | |||
* </p> | |||
* | |||
* @package: com.xkcoding.social.qq.api | |||
* @description: QQ用户信息 | |||
* @author: yangkai.shen | |||
* @date: Created in 2019-02-21 10:52 | |||
* @copyright: Copyright (c) 2019 | |||
* @version: V1.0 | |||
* @modified: yangkai.shen | |||
*/ | |||
@Data | |||
public class QQUserInfo { | |||
/** | |||
* QQ唯一标识 | |||
*/ | |||
private String openId; | |||
/** | |||
* 返回码 | |||
*/ | |||
private String ret; | |||
/** | |||
* 如果ret<0,会有相应的错误信息提示,返回数据全部用UTF-8编码。 | |||
*/ | |||
private String msg; | |||
/** | |||
* 用户在QQ空间的昵称。 | |||
*/ | |||
private String nickname; | |||
/** | |||
* 大小为30×30像素的QQ空间头像URL。 | |||
*/ | |||
private String figureurl; | |||
/** | |||
* 大小为50×50像素的QQ空间头像URL。 | |||
*/ | |||
private String figureurl_1; | |||
/** | |||
* 大小为100×100像素的QQ空间头像URL。 | |||
*/ | |||
private String figureurl_2; | |||
/** | |||
* 大小为40×40像素的QQ头像URL。 | |||
*/ | |||
private String figureurl_qq_1; | |||
/** | |||
* 大小为100×100像素的QQ头像URL。需要注意,不是所有的用户都拥有QQ的100x100的头像,但40x40像素则是一定会有。 | |||
*/ | |||
private String figureurl_qq_2; | |||
/** | |||
* 性别。 如果获取不到则默认返回"男" | |||
*/ | |||
private String gender; | |||
/** | |||
* 是否黄钻 | |||
*/ | |||
private String is_yellow_vip; | |||
/** | |||
* 是否会员 | |||
*/ | |||
private String vip; | |||
/** | |||
* 黄钻等级 | |||
*/ | |||
private String yellow_vip_level; | |||
/** | |||
* 会员等级 | |||
*/ | |||
private String level; | |||
/** | |||
* 是否年费黄钻 | |||
*/ | |||
private String is_yellow_year_vip; | |||
} |
@@ -0,0 +1,78 @@ | |||
package com.xkcoding.social.qq.api.impl; | |||
import cn.hutool.core.util.StrUtil; | |||
import cn.hutool.json.JSONUtil; | |||
import com.xkcoding.social.qq.api.QQ; | |||
import com.xkcoding.social.qq.api.QQUserInfo; | |||
import lombok.extern.slf4j.Slf4j; | |||
import org.springframework.social.oauth2.AbstractOAuth2ApiBinding; | |||
import org.springframework.social.oauth2.TokenStrategy; | |||
import org.springframework.util.MultiValueMap; | |||
/** | |||
* <p> | |||
* 获取QQ用户信息接口实现 | |||
* </p> | |||
* | |||
* @package: com.xkcoding.social.qq.api.impl | |||
* @description: 获取QQ用户信息接口实现 | |||
* @author: yangkai.shen | |||
* @date: Created in 2019-02-21 10:57 | |||
* @copyright: Copyright (c) 2019 | |||
* @version: V1.0 | |||
* @modified: yangkai.shen | |||
*/ | |||
@Slf4j | |||
public class QQImpl extends AbstractOAuth2ApiBinding implements QQ { | |||
private String clientId; | |||
private String openId; | |||
/** | |||
* 获取 openId,文档:http://wiki.connect.qq.com/%E8%8E%B7%E5%8F%96%E7%94%A8%E6%88%B7openid_oauth2-0 | |||
*/ | |||
private static final String URL_FOR_OPENID = "https://graph.qq.com/oauth2.0/me?access_token=%s"; | |||
/** | |||
* 获取用户信息 URL,文档:http://wiki.connect.qq.com/get_user_info | |||
*/ | |||
private static final String URL_FOR_USER_INFO = "https://graph.qq.com/user/get_user_info?oauth_consumer_key=%sopenid=%s"; | |||
/** | |||
* {@link TokenStrategy#ACCESS_TOKEN_PARAMETER} 将 token 封装为参数传递,key为 access_token <br/> | |||
* {@link TokenStrategy#OAUTH_TOKEN_PARAMETER} 将 token 封装为参数传递,key为 oauth_token <br/> | |||
* {@link TokenStrategy#AUTHORIZATION_HEADER} 将 token 封装为header传递,key为 Authorization,value为 "Bearer token" 格式<br/> | |||
* | |||
* @param accessToken token 由{@link org.springframework.social.oauth2.OAuth2Template#exchangeForAccess(String, String, MultiValueMap)} 返回 | |||
* @param clientId 从配置文件获取 | |||
*/ | |||
public QQImpl(String accessToken, String clientId) { | |||
super(accessToken, TokenStrategy.ACCESS_TOKEN_PARAMETER); | |||
this.clientId = clientId; | |||
// 根据 token 获取 openId | |||
String url = String.format(URL_FOR_OPENID, accessToken); | |||
String result = getRestTemplate().getForObject(url, String.class); | |||
// result 格式:callback( {"client_id":"YOUR_APPID","openid":"YOUR_OPENID"} ); 文档:http://wiki.connect.qq.com/%E8%8E%B7%E5%8F%96%E7%94%A8%E6%88%B7openid_oauth2-0 | |||
log.error("【QQImpl】url = {} ,result = {}", url, result); | |||
this.openId = StrUtil.subBetween(result, "\"openid\":\"", "\"}"); | |||
} | |||
/** | |||
* 获取QQ用户信息 | |||
* | |||
* @return QQ用户信息 | |||
*/ | |||
@Override | |||
public QQUserInfo getUserInfo() { | |||
String url = String.format(URL_FOR_USER_INFO, clientId, openId); | |||
String result = getRestTemplate().getForObject(url, String.class); | |||
log.error("【QQImpl】url = {} ,result = {}", url, result); | |||
QQUserInfo userInfo = JSONUtil.toBean(result, QQUserInfo.class, true); | |||
userInfo.setOpenId(openId); | |||
return userInfo; | |||
} | |||
} |
@@ -0,0 +1,39 @@ | |||
package com.xkcoding.social.qq.config; | |||
import com.xkcoding.social.config.QQProperties; | |||
import com.xkcoding.social.config.SocialProperties; | |||
import com.xkcoding.social.qq.connect.QQConnectionFactory; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | |||
import org.springframework.context.annotation.Configuration; | |||
import org.springframework.core.env.Environment; | |||
import org.springframework.social.config.annotation.ConnectionFactoryConfigurer; | |||
import org.springframework.social.config.annotation.SocialConfigurerAdapter; | |||
/** | |||
* <p> | |||
* QQ社交登录自动装配类 | |||
* </p> | |||
* | |||
* @package: com.xkcoding.social.qq.config | |||
* @description: QQ社交登录自动装配类 | |||
* @author: yangkai.shen | |||
* @date: Created in 2019-02-21 14:08 | |||
* @copyright: Copyright (c) 2019 | |||
* @version: V1.0 | |||
* @modified: yangkai.shen | |||
*/ | |||
@Configuration | |||
@ConditionalOnProperty(prefix = "demo.social.qq", name = "clientId") | |||
public class QQAutoConfig extends SocialConfigurerAdapter { | |||
@Autowired | |||
private SocialProperties socialProperties; | |||
@Override | |||
public void addConnectionFactories(ConnectionFactoryConfigurer connectionFactoryConfigurer, Environment environment) { | |||
QQProperties qq = socialProperties.getQq(); | |||
QQConnectionFactory qqConnectionFactory = new QQConnectionFactory(qq.getProviderId(), qq.getClientId(), qq.getClientSecret()); | |||
connectionFactoryConfigurer.addConnectionFactory(qqConnectionFactory); | |||
super.addConnectionFactories(connectionFactoryConfigurer, environment); | |||
} | |||
} |
@@ -0,0 +1,59 @@ | |||
package com.xkcoding.social.qq.connect; | |||
import com.xkcoding.social.qq.api.QQ; | |||
import com.xkcoding.social.qq.api.QQUserInfo; | |||
import org.springframework.social.connect.ApiAdapter; | |||
import org.springframework.social.connect.ConnectionValues; | |||
import org.springframework.social.connect.UserProfile; | |||
/** | |||
* <p> | |||
* QQ 用户适配器 | |||
* </p> | |||
* | |||
* @package: com.xkcoding.social.qq.connect | |||
* @description: QQ 用户适配器 | |||
* @author: yangkai.shen | |||
* @date: Created in 2019-02-21 13:46 | |||
* @copyright: Copyright (c) 2019 | |||
* @version: V1.0 | |||
* @modified: yangkai.shen | |||
*/ | |||
public class QQAdapter implements ApiAdapter<QQ> { | |||
/** | |||
* 测试api是否可用,默认返回true | |||
*/ | |||
@Override | |||
public boolean test(QQ api) { | |||
return true; | |||
} | |||
/** | |||
* 将自己实现的获取用户信息,转换为spring social返回的用户信息 | |||
*/ | |||
@Override | |||
public void setConnectionValues(QQ api, ConnectionValues values) { | |||
QQUserInfo userInfo = api.getUserInfo(); | |||
values.setDisplayName(userInfo.getNickname()); | |||
values.setImageUrl(userInfo.getFigureurl_qq_1()); | |||
values.setProviderUserId(userInfo.getOpenId()); | |||
values.setProfileUrl(null); | |||
} | |||
/** | |||
* 用户主页,QQ默认没有用户主页 | |||
*/ | |||
@Override | |||
public UserProfile fetchUserProfile(QQ api) { | |||
return UserProfile.EMPTY; | |||
} | |||
/** | |||
* 更新用户状态,空实现 | |||
*/ | |||
@Override | |||
public void updateStatus(QQ api, String message) { | |||
// do nothing | |||
} | |||
} |
@@ -0,0 +1,24 @@ | |||
package com.xkcoding.social.qq.connect; | |||
import com.xkcoding.social.qq.api.QQ; | |||
import org.springframework.social.connect.support.OAuth2ConnectionFactory; | |||
/** | |||
* <p> | |||
* QQ 连接工厂 | |||
* </p> | |||
* | |||
* @package: com.xkcoding.social.qq.connect | |||
* @description: QQ 连接工厂 | |||
* @author: yangkai.shen | |||
* @date: Created in 2019-02-21 13:50 | |||
* @copyright: Copyright (c) 2019 | |||
* @version: V1.0 | |||
* @modified: yangkai.shen | |||
*/ | |||
public class QQConnectionFactory extends OAuth2ConnectionFactory<QQ> { | |||
public QQConnectionFactory(String providerId, String clientId, String clientSecret) { | |||
super(providerId, new QQServiceProvider(clientId, clientSecret), new QQAdapter()); | |||
} | |||
} |
@@ -0,0 +1,68 @@ | |||
package com.xkcoding.social.qq.connect; | |||
import com.google.common.base.Charsets; | |||
import com.google.common.base.Splitter; | |||
import lombok.extern.slf4j.Slf4j; | |||
import org.springframework.http.converter.StringHttpMessageConverter; | |||
import org.springframework.social.oauth2.AccessGrant; | |||
import org.springframework.social.oauth2.OAuth2Template; | |||
import org.springframework.util.MultiValueMap; | |||
import org.springframework.web.client.RestTemplate; | |||
import java.util.Map; | |||
/** | |||
* <p> | |||
* ①获取授权码code ②根据code换取令牌accessToken | |||
* </p> | |||
* | |||
* @package: com.xkcoding.social.qq.connect | |||
* @description: ①获取授权码code ②根据code换取令牌accessToken | |||
* @author: yangkai.shen | |||
* @date: Created in 2019-02-21 11:22 | |||
* @copyright: Copyright (c) 2019 | |||
* @version: V1.0 | |||
* @modified: yangkai.shen | |||
*/ | |||
@Slf4j | |||
public class QQOauth2Template extends OAuth2Template { | |||
public QQOauth2Template(String clientId, String clientSecret, String authorizeUrl, String accessTokenUrl) { | |||
super(clientId, clientSecret, authorizeUrl, accessTokenUrl); | |||
// 默认不携带 client_id 和 client_secret,根据文档需要携带这两个参数 | |||
setUseParametersForClientAuthentication(true); | |||
} | |||
/** | |||
* 根据code换取access_token,父类默认处理的是json格式,但是根据QQ文档,格式不是json格式,需要我们重写,自行处理 | |||
* | |||
* @param accessTokenUrl 换取 access_token 的 URL 地址 | |||
* @param parameters 参数 | |||
* @return {@link AccessGrant} | |||
*/ | |||
@Override | |||
protected AccessGrant postForAccessGrant(String accessTokenUrl, MultiValueMap<String, String> parameters) { | |||
String result = getRestTemplate().getForObject(accessTokenUrl, String.class, parameters); | |||
// result 格式:access_token=FE04************************CCE2&expires_in=7776000&refresh_token=88E4************************BE14,文档:http://wiki.connect.qq.com/%E4%BD%BF%E7%94%A8authorization_code%E8%8E%B7%E5%8F%96access_token | |||
log.error("【QQOauth2Template】url = {} ,result = {}", accessTokenUrl, result); | |||
Map<String, String> data = Splitter.on("&").withKeyValueSeparator("=").split(result); | |||
String accessToken = data.get("access_token"); | |||
Long expiresIn = Long.valueOf(data.get("expires_in")); | |||
String refreshToken = data.get("refresh_token"); | |||
return new AccessGrant(accessToken, null, refreshToken, expiresIn); | |||
} | |||
/** | |||
* 父类提供的方法消息转化器没有提供 String 格式的转换,所以这里需要额外加一个 | |||
*/ | |||
@Override | |||
protected RestTemplate createRestTemplate() { | |||
RestTemplate restTemplate = super.createRestTemplate(); | |||
restTemplate.getMessageConverters().add(new StringHttpMessageConverter(Charsets.UTF_8)); | |||
return restTemplate; | |||
} | |||
} |
@@ -0,0 +1,42 @@ | |||
package com.xkcoding.social.qq.connect; | |||
import com.xkcoding.social.qq.api.QQ; | |||
import com.xkcoding.social.qq.api.impl.QQImpl; | |||
import org.springframework.social.oauth2.AbstractOAuth2ServiceProvider; | |||
/** | |||
* <p> | |||
* 定义获取授权码地址和获取令牌地址,同时定义QQImpl | |||
* </p> | |||
* | |||
* @package: com.xkcoding.social.qq.connect | |||
* @description: 定义获取授权码地址和获取令牌地址,同时定义QQImpl | |||
* @author: yangkai.shen | |||
* @date: Created in 2019-02-21 13:28 | |||
* @copyright: Copyright (c) 2019 | |||
* @version: V1.0 | |||
* @modified: yangkai.shen | |||
*/ | |||
public class QQServiceProvider extends AbstractOAuth2ServiceProvider<QQ> { | |||
private String clientId; | |||
/** | |||
* 获取授权码的地址,文档:http://wiki.connect.qq.com/%E4%BD%BF%E7%94%A8authorization_code%E8%8E%B7%E5%8F%96access_token | |||
*/ | |||
private static final String URL_FOR_CODE = "https://graph.qq.com/oauth2.0/authorize"; | |||
/** | |||
* 获取token的地址,文档:http://wiki.connect.qq.com/%E4%BD%BF%E7%94%A8authorization_code%E8%8E%B7%E5%8F%96access_token | |||
*/ | |||
private static final String URL_FOR_ACCESS_TOKEN = "https://graph.qq.com/oauth2.0/token"; | |||
public QQServiceProvider(String clientId, String clientSecret) { | |||
super(new QQOauth2Template(clientId, clientSecret, URL_FOR_CODE, URL_FOR_ACCESS_TOKEN)); | |||
this.clientId = clientId; | |||
} | |||
@Override | |||
public QQ getApi(String accessToken) { | |||
return new QQImpl(accessToken, clientId); | |||
} | |||
} |
@@ -0,0 +1,17 @@ | |||
server: | |||
port: 8080 | |||
servlet: | |||
context-path: /demo | |||
demo: | |||
social: | |||
filterProcessUrl: /auth | |||
qq: | |||
providerId: qq | |||
clientId: | |||
clientSecret: | |||
spring: | |||
datasource: | |||
url: jdbc:mysql://127.0.0.1:3306/spring-boot-demo?useUnicode=true&characterEncoding=UTF-8&useSSL=false&autoReconnect=true&failOverReadOnly=false&serverTimezone=GMT%2B8 | |||
username: root | |||
password: root | |||
driver-class-name: com.mysql.cj.jdbc.Driver |