From b7be5c86db1cd5a863d55a720428afcc63420d2f Mon Sep 17 00:00:00 2001 From: "Yangkai.Shen" <237497819@qq.com> Date: Thu, 21 Feb 2019 14:48:08 +0800 Subject: [PATCH] =?UTF-8?q?:sparkles:=20spring-boot-demo-social=20?= =?UTF-8?q?=E5=8A=AA=E5=8A=9B=E4=B8=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- spring-boot-demo-social/pom.xml | 64 ++++++++++++++- .../SpringBootDemoSocialApplication.java | 2 + .../social/config/DemoSocialConfigure.java | 32 ++++++++ .../social/config/DemoUserDetailService.java | 34 ++++++++ .../xkcoding/social/config/QQProperties.java | 34 ++++++++ .../xkcoding/social/config/SocialConfig.java | 67 +++++++++++++++ .../social/config/SocialProperties.java | 25 ++++++ .../java/com/xkcoding/social/qq/api/QQ.java | 24 ++++++ .../xkcoding/social/qq/api/QQUserInfo.java | 82 +++++++++++++++++++ .../xkcoding/social/qq/api/impl/QQImpl.java | 78 ++++++++++++++++++ .../social/qq/config/QQAutoConfig.java | 39 +++++++++ .../xkcoding/social/qq/connect/QQAdapter.java | 59 +++++++++++++ .../qq/connect/QQConnectionFactory.java | 24 ++++++ .../social/qq/connect/QQOauth2Template.java | 68 +++++++++++++++ .../social/qq/connect/QQServiceProvider.java | 42 ++++++++++ .../src/main/resources/application.properties | 0 .../src/main/resources/application.yml | 17 ++++ 17 files changed, 690 insertions(+), 1 deletion(-) create mode 100644 spring-boot-demo-social/src/main/java/com/xkcoding/social/config/DemoSocialConfigure.java create mode 100644 spring-boot-demo-social/src/main/java/com/xkcoding/social/config/DemoUserDetailService.java create mode 100644 spring-boot-demo-social/src/main/java/com/xkcoding/social/config/QQProperties.java create mode 100644 spring-boot-demo-social/src/main/java/com/xkcoding/social/config/SocialConfig.java create mode 100644 spring-boot-demo-social/src/main/java/com/xkcoding/social/config/SocialProperties.java create mode 100644 spring-boot-demo-social/src/main/java/com/xkcoding/social/qq/api/QQ.java create mode 100644 spring-boot-demo-social/src/main/java/com/xkcoding/social/qq/api/QQUserInfo.java create mode 100644 spring-boot-demo-social/src/main/java/com/xkcoding/social/qq/api/impl/QQImpl.java create mode 100644 spring-boot-demo-social/src/main/java/com/xkcoding/social/qq/config/QQAutoConfig.java create mode 100644 spring-boot-demo-social/src/main/java/com/xkcoding/social/qq/connect/QQAdapter.java create mode 100644 spring-boot-demo-social/src/main/java/com/xkcoding/social/qq/connect/QQConnectionFactory.java create mode 100644 spring-boot-demo-social/src/main/java/com/xkcoding/social/qq/connect/QQOauth2Template.java create mode 100644 spring-boot-demo-social/src/main/java/com/xkcoding/social/qq/connect/QQServiceProvider.java delete mode 100644 spring-boot-demo-social/src/main/resources/application.properties create mode 100644 spring-boot-demo-social/src/main/resources/application.yml diff --git a/spring-boot-demo-social/pom.xml b/spring-boot-demo-social/pom.xml index e2bca34..b623552 100644 --- a/spring-boot-demo-social/pom.xml +++ b/spring-boot-demo-social/pom.xml @@ -20,12 +20,53 @@ UTF-8 UTF-8 1.8 + 1.1.6.RELEASE org.springframework.boot - spring-boot-starter + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-jdbc + + + + org.springframework.boot + spring-boot-starter-security + + + + org.springframework.social + spring-social-config + ${spring.social.version} + + + + org.springframework.social + spring-social-core + ${spring.social.version} + + + + org.springframework.social + spring-social-security + ${spring.social.version} + + + + org.springframework.social + spring-social-web + ${spring.social.version} + + + + org.springframework.boot + spring-boot-configuration-processor + true @@ -33,6 +74,27 @@ spring-boot-starter-test test + + + mysql + mysql-connector-java + + + + org.projectlombok + lombok + true + + + + com.google.guava + guava + + + + cn.hutool + hutool-all + diff --git a/spring-boot-demo-social/src/main/java/com/xkcoding/social/SpringBootDemoSocialApplication.java b/spring-boot-demo-social/src/main/java/com/xkcoding/social/SpringBootDemoSocialApplication.java index 3369558..4425687 100644 --- a/spring-boot-demo-social/src/main/java/com/xkcoding/social/SpringBootDemoSocialApplication.java +++ b/spring-boot-demo-social/src/main/java/com/xkcoding/social/SpringBootDemoSocialApplication.java @@ -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; /** *

diff --git a/spring-boot-demo-social/src/main/java/com/xkcoding/social/config/DemoSocialConfigure.java b/spring-boot-demo-social/src/main/java/com/xkcoding/social/config/DemoSocialConfigure.java new file mode 100644 index 0000000..6e0a930 --- /dev/null +++ b/spring-boot-demo-social/src/main/java/com/xkcoding/social/config/DemoSocialConfigure.java @@ -0,0 +1,32 @@ +package com.xkcoding.social.config; + +import org.springframework.social.security.SocialAuthenticationFilter; +import org.springframework.social.security.SpringSocialConfigurer; + +/** + *

+ * 继承默认的社交登录配置,加入自定义的后处理逻辑 + *

+ * + * @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 postProcess(T object) { + SocialAuthenticationFilter socialAuthenticationFilter = (SocialAuthenticationFilter) super.postProcess(object); + socialAuthenticationFilter.setFilterProcessesUrl(filterProcessUrl); + return (T) socialAuthenticationFilter; + } +} diff --git a/spring-boot-demo-social/src/main/java/com/xkcoding/social/config/DemoUserDetailService.java b/spring-boot-demo-social/src/main/java/com/xkcoding/social/config/DemoUserDetailService.java new file mode 100644 index 0000000..6b0bfe5 --- /dev/null +++ b/spring-boot-demo-social/src/main/java/com/xkcoding/social/config/DemoUserDetailService.java @@ -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; + +/** + *

+ * 根据userId获取用户信息 + *

+ * + * @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")); + } +} diff --git a/spring-boot-demo-social/src/main/java/com/xkcoding/social/config/QQProperties.java b/spring-boot-demo-social/src/main/java/com/xkcoding/social/config/QQProperties.java new file mode 100644 index 0000000..e04e7f3 --- /dev/null +++ b/spring-boot-demo-social/src/main/java/com/xkcoding/social/config/QQProperties.java @@ -0,0 +1,34 @@ +package com.xkcoding.social.config; + +import lombok.Data; + +/** + *

+ * QQ 配置 + *

+ * + * @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; +} diff --git a/spring-boot-demo-social/src/main/java/com/xkcoding/social/config/SocialConfig.java b/spring-boot-demo-social/src/main/java/com/xkcoding/social/config/SocialConfig.java new file mode 100644 index 0000000..ec639c4 --- /dev/null +++ b/spring-boot-demo-social/src/main/java/com/xkcoding/social/config/SocialConfig.java @@ -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; + +/** + *

+ * 社交登录配置 + *

+ * + * @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(); + } +} diff --git a/spring-boot-demo-social/src/main/java/com/xkcoding/social/config/SocialProperties.java b/spring-boot-demo-social/src/main/java/com/xkcoding/social/config/SocialProperties.java new file mode 100644 index 0000000..9f8686d --- /dev/null +++ b/spring-boot-demo-social/src/main/java/com/xkcoding/social/config/SocialProperties.java @@ -0,0 +1,25 @@ +package com.xkcoding.social.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + *

+ * 社交登录配置类 + *

+ * + * @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(); +} diff --git a/spring-boot-demo-social/src/main/java/com/xkcoding/social/qq/api/QQ.java b/spring-boot-demo-social/src/main/java/com/xkcoding/social/qq/api/QQ.java new file mode 100644 index 0000000..c9a65a6 --- /dev/null +++ b/spring-boot-demo-social/src/main/java/com/xkcoding/social/qq/api/QQ.java @@ -0,0 +1,24 @@ +package com.xkcoding.social.qq.api; + +/** + *

+ * 获取QQ用户信息接口 + *

+ * + * @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(); + +} diff --git a/spring-boot-demo-social/src/main/java/com/xkcoding/social/qq/api/QQUserInfo.java b/spring-boot-demo-social/src/main/java/com/xkcoding/social/qq/api/QQUserInfo.java new file mode 100644 index 0000000..5e25f15 --- /dev/null +++ b/spring-boot-demo-social/src/main/java/com/xkcoding/social/qq/api/QQUserInfo.java @@ -0,0 +1,82 @@ +package com.xkcoding.social.qq.api; + +import lombok.Data; + +/** + *

+ * QQ用户信息 + *

+ * + * @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; + + +} diff --git a/spring-boot-demo-social/src/main/java/com/xkcoding/social/qq/api/impl/QQImpl.java b/spring-boot-demo-social/src/main/java/com/xkcoding/social/qq/api/impl/QQImpl.java new file mode 100644 index 0000000..8b6bf7f --- /dev/null +++ b/spring-boot-demo-social/src/main/java/com/xkcoding/social/qq/api/impl/QQImpl.java @@ -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; + +/** + *

+ * 获取QQ用户信息接口实现 + *

+ * + * @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
+ * {@link TokenStrategy#OAUTH_TOKEN_PARAMETER} 将 token 封装为参数传递,key为 oauth_token
+ * {@link TokenStrategy#AUTHORIZATION_HEADER} 将 token 封装为header传递,key为 Authorization,value为 "Bearer token" 格式
+ * + * @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; + } +} diff --git a/spring-boot-demo-social/src/main/java/com/xkcoding/social/qq/config/QQAutoConfig.java b/spring-boot-demo-social/src/main/java/com/xkcoding/social/qq/config/QQAutoConfig.java new file mode 100644 index 0000000..66ae714 --- /dev/null +++ b/spring-boot-demo-social/src/main/java/com/xkcoding/social/qq/config/QQAutoConfig.java @@ -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; + +/** + *

+ * QQ社交登录自动装配类 + *

+ * + * @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); + } +} diff --git a/spring-boot-demo-social/src/main/java/com/xkcoding/social/qq/connect/QQAdapter.java b/spring-boot-demo-social/src/main/java/com/xkcoding/social/qq/connect/QQAdapter.java new file mode 100644 index 0000000..7fb4e68 --- /dev/null +++ b/spring-boot-demo-social/src/main/java/com/xkcoding/social/qq/connect/QQAdapter.java @@ -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; + +/** + *

+ * QQ 用户适配器 + *

+ * + * @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 { + /** + * 测试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 + } +} diff --git a/spring-boot-demo-social/src/main/java/com/xkcoding/social/qq/connect/QQConnectionFactory.java b/spring-boot-demo-social/src/main/java/com/xkcoding/social/qq/connect/QQConnectionFactory.java new file mode 100644 index 0000000..4ff0d19 --- /dev/null +++ b/spring-boot-demo-social/src/main/java/com/xkcoding/social/qq/connect/QQConnectionFactory.java @@ -0,0 +1,24 @@ +package com.xkcoding.social.qq.connect; + +import com.xkcoding.social.qq.api.QQ; +import org.springframework.social.connect.support.OAuth2ConnectionFactory; + +/** + *

+ * QQ 连接工厂 + *

+ * + * @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 { + + public QQConnectionFactory(String providerId, String clientId, String clientSecret) { + super(providerId, new QQServiceProvider(clientId, clientSecret), new QQAdapter()); + } +} diff --git a/spring-boot-demo-social/src/main/java/com/xkcoding/social/qq/connect/QQOauth2Template.java b/spring-boot-demo-social/src/main/java/com/xkcoding/social/qq/connect/QQOauth2Template.java new file mode 100644 index 0000000..055f847 --- /dev/null +++ b/spring-boot-demo-social/src/main/java/com/xkcoding/social/qq/connect/QQOauth2Template.java @@ -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; + +/** + *

+ * ①获取授权码code ②根据code换取令牌accessToken + *

+ * + * @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 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 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; + } +} diff --git a/spring-boot-demo-social/src/main/java/com/xkcoding/social/qq/connect/QQServiceProvider.java b/spring-boot-demo-social/src/main/java/com/xkcoding/social/qq/connect/QQServiceProvider.java new file mode 100644 index 0000000..7ee7bd7 --- /dev/null +++ b/spring-boot-demo-social/src/main/java/com/xkcoding/social/qq/connect/QQServiceProvider.java @@ -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; + +/** + *

+ * 定义获取授权码地址和获取令牌地址,同时定义QQImpl + *

+ * + * @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 { + 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); + } +} diff --git a/spring-boot-demo-social/src/main/resources/application.properties b/spring-boot-demo-social/src/main/resources/application.properties deleted file mode 100644 index e69de29..0000000 diff --git a/spring-boot-demo-social/src/main/resources/application.yml b/spring-boot-demo-social/src/main/resources/application.yml new file mode 100644 index 0000000..b5d993b --- /dev/null +++ b/spring-boot-demo-social/src/main/resources/application.yml @@ -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