使用SpringSocial开发QQ登录
⒈编写QQ用户对应的数据结构
package cn.coreqi.social.qq.entities; /**
* 封装QQ的用户信息
*/
public class QQUserInfo { /**
* 返回码
*/
private String ret;
/**
* 如果ret<0,会有相应的错误信息提示,返回数据全部用UTF-8编码。
*/
private String msg;
/**
*
*/
private String openId;
/**
* 不知道什么东西,文档上没写,但是实际api返回里有。
*/
private String is_lost;
/**
* 省(直辖市)
*/
private String province;
/**
* 市(直辖市区)
*/
private String city;
/**
* 出生年月
*/
private String year;
/**
* 用户在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的100×100的头像,但40×40像素则是一定会有。
*/
private String figureurl_qq_2;
/**
* 性别。 如果获取不到则默认返回”男”
*/
private String gender;
/**
* 标识用户是否为黄钻用户(0:不是;1:是)。
*/
private String is_yellow_vip;
/**
* 标识用户是否为黄钻用户(0:不是;1:是)
*/
private String vip;
/**
* 黄钻等级
*/
private String yellow_vip_level;
/**
* 黄钻等级
*/
private String level;
/**
* 标识是否为年费黄钻用户(0:不是; 1:是)
*/
private String is_yellow_year_vip; public String getRet() {
return ret;
} public void setRet(String ret) {
this.ret = ret;
} public String getMsg() {
return msg;
} public void setMsg(String msg) {
this.msg = msg;
} public String getOpenId() {
return openId;
} public void setOpenId(String openId) {
this.openId = openId;
} public String getIs_lost() {
return is_lost;
} public void setIs_lost(String is_lost) {
this.is_lost = is_lost;
} public String getProvince() {
return province;
} public void setProvince(String province) {
this.province = province;
} public String getCity() {
return city;
} public void setCity(String city) {
this.city = city;
} public String getYear() {
return year;
} public void setYear(String year) {
this.year = year;
} public String getNickname() {
return nickname;
} public void setNickname(String nickname) {
this.nickname = nickname;
} public String getFigureurl() {
return figureurl;
} public void setFigureurl(String figureurl) {
this.figureurl = figureurl;
} public String getFigureurl_1() {
return figureurl_1;
} public void setFigureurl_1(String figureurl_1) {
this.figureurl_1 = figureurl_1;
} public String getFigureurl_2() {
return figureurl_2;
} public void setFigureurl_2(String figureurl_2) {
this.figureurl_2 = figureurl_2;
} public String getFigureurl_qq_1() {
return figureurl_qq_1;
} public void setFigureurl_qq_1(String figureurl_qq_1) {
this.figureurl_qq_1 = figureurl_qq_1;
} public String getFigureurl_qq_2() {
return figureurl_qq_2;
} public void setFigureurl_qq_2(String figureurl_qq_2) {
this.figureurl_qq_2 = figureurl_qq_2;
} public String getGender() {
return gender;
} public void setGender(String gender) {
this.gender = gender;
} public String getIs_yellow_vip() {
return is_yellow_vip;
} public void setIs_yellow_vip(String is_yellow_vip) {
this.is_yellow_vip = is_yellow_vip;
} public String getVip() {
return vip;
} public void setVip(String vip) {
this.vip = vip;
} public String getYellow_vip_level() {
return yellow_vip_level;
} public void setYellow_vip_level(String yellow_vip_level) {
this.yellow_vip_level = yellow_vip_level;
} public String getLevel() {
return level;
} public void setLevel(String level) {
this.level = level;
} public String getIs_yellow_year_vip() {
return is_yellow_year_vip;
} public void setIs_yellow_year_vip(String is_yellow_year_vip) {
this.is_yellow_year_vip = is_yellow_year_vip;
}
}
⒉编写一个QQ API接口用于获取QQ用户信息
package cn.coreqi.social.qq.api; import cn.coreqi.social.qq.entities.QQUserInfo; public interface QQ {
/**
* 返回QQ中的用户信息
* @return
*/
QQUserInfo getUserInfo();
}
⒊编写一个QQ API接口实现
package cn.coreqi.social.qq.api.impl; import cn.coreqi.social.qq.api.QQ;
import cn.coreqi.social.qq.entities.QQUserInfo;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.lang.StringUtils;
import org.springframework.social.oauth2.AbstractOAuth2ApiBinding;
import org.springframework.social.oauth2.TokenStrategy; import java.io.IOException; /**
* 获取用户信息
* 不能声明为单例,因为每个用户的验证是不同的
*/
public class QQImpl extends AbstractOAuth2ApiBinding implements QQ { private static final String URL_GET_OPENID = "https://graph.qq.com/oauth2.0/me?access_token=%s"; //获取openid的请求地址
private static final String URL_GET_USERINFO = "https://graph.qq.com/user/get_user_info?oauth_consumer_key=%s&openid=%s"; //获取用户信息的请求地址 private String appid; //申请QQ登录成功后,分配给应用的appid
private String openid; //用户的ID,与QQ号码一一对应。 private ObjectMapper objectMapper = new ObjectMapper(); //用于序列化Json数据 public QQImpl(String accessToken,String appid){
super(accessToken, TokenStrategy.ACCESS_TOKEN_PARAMETER); //将token作为查询参数
this.appid = appid; String url = String.format(URL_GET_OPENID,accessToken); //拼接成最终的openid的请求地址
String result = getRestTemplate().getForObject(url,String.class); System.out.println(result); this.openid = StringUtils.substringBetween(result,"\"openid\":\"","\"}"); } @Override
public QQUserInfo getUserInfo() {
String url = String.format(URL_GET_USERINFO,appid,openid); ////拼接成最终的获取用户信息的请求地址
String result = getRestTemplate().getForObject(url,String.class);
System.out.println(result);
QQUserInfo userInfo = null;
try {
userInfo = objectMapper.readValue(result,QQUserInfo.class);
userInfo.setOpenId(openid);
return userInfo;
} catch (Exception e) {
throw new RuntimeException("获取用户信息失败",e);
}
}
}
⒋编写QQ OAuth2认证流程模板类。
package cn.coreqi.social.qq.connect; import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.nio.charset.Charset; public class QQOAuth2Template extends OAuth2Template { private Logger logger = LoggerFactory.getLogger(getClass()); public QQOAuth2Template(String clientId, String clientSecret, String authorizeUrl, String accessTokenUrl) {
super(clientId, clientSecret, authorizeUrl, accessTokenUrl);
setUseParametersForClientAuthentication(true);
} @Override
protected AccessGrant postForAccessGrant(String accessTokenUrl, MultiValueMap<String, String> parameters) {
String responseStr = getRestTemplate().postForObject(accessTokenUrl, parameters, String.class); logger.info("获取accessToke的响应:"+responseStr); String[] items = StringUtils.splitByWholeSeparatorPreserveAllTokens(responseStr, "&"); String accessToken = StringUtils.substringAfterLast(items[0], "=");
Long expiresIn = new Long(StringUtils.substringAfterLast(items[1], "="));
String refreshToken = StringUtils.substringAfterLast(items[2], "="); return new AccessGrant(accessToken, null, refreshToken, expiresIn);
} @Override
protected RestTemplate createRestTemplate() {
RestTemplate restTemplate = super.createRestTemplate();
restTemplate.getMessageConverters().add(new StringHttpMessageConverter(Charset.forName("UTF-8")));
return restTemplate;
}
}
⒌编写QQ的OAuth2流程处理器的提供器
package cn.coreqi.social.qq.connect; import cn.coreqi.social.qq.api.QQ;
import cn.coreqi.social.qq.api.impl.QQImpl;
import org.springframework.social.oauth2.AbstractOAuth2ServiceProvider; /**
* 泛型是API接口的类型
*/
public class QQServiceProvider extends AbstractOAuth2ServiceProvider<QQ> { private static final String URL_AUTHORIZE = "https://graph.qq.com/oauth2.0/authorize"; //获取授权码地址
private static final String URL_ACCESS_TOKEN = "https://graph.qq.com/oauth2.0/token"; //获取用户令牌地址 private String appId; public QQServiceProvider(String appId,String appSecret) {
super(new QQOAuth2Template(appId,appSecret,URL_AUTHORIZE,URL_ACCESS_TOKEN));
this.appId = appId;
} @Override
public QQ getApi(String accessToken) {
return new QQImpl(accessToken,appId);
}
}
⒍编写QQ API适配器,将从QQ API拿到的用户数据模型转换为Spring Social的标准用户数据模型。
package cn.coreqi.social.qq.connect; import cn.coreqi.social.qq.api.QQ;
import cn.coreqi.social.qq.entities.QQUserInfo;
import org.springframework.social.connect.ApiAdapter;
import org.springframework.social.connect.ConnectionValues;
import org.springframework.social.connect.UserProfile; import java.io.IOException; /**
* 泛型是指当前API适配器适配API的类型是什么
*/
public class QQAdapter implements ApiAdapter<QQ> { /**
* 用来测试当前的API是否可用
* @param qq
* @return
*/
@Override
public boolean test(QQ qq) {
return true;
} /**
* 将服务提供商个性化的用户信息映射到ConnectionValues标准的数据化结构上
* @param qq
* @param connectionValues
*/
@Override
public void setConnectionValues(QQ qq, ConnectionValues connectionValues) {
QQUserInfo userInfo = qq.getUserInfo();
connectionValues.setDisplayName(userInfo.getNickname()); //显示的用户名称
connectionValues.setImageUrl(userInfo.getFigureurl_qq_1()); //用户的头像
connectionValues.setProfileUrl(null); //个人主页
connectionValues.setProviderUserId(userInfo.getOpenId()); //QQ的唯一标识
} /**
* 和上面的方法类似
* @param qq
* @return
*/
@Override
public UserProfile fetchUserProfile(QQ qq) {
return null;
} /**
*
* @param qq
* @param s
*/
@Override
public void updateStatus(QQ qq, String s) { }
}
⒎创建QQ连接工厂
package cn.coreqi.social.qq.connect; import cn.coreqi.social.qq.api.QQ;
import org.springframework.social.connect.support.OAuth2ConnectionFactory; public class QQConnectionFactory extends OAuth2ConnectionFactory<QQ> { /**
*
* @param providerId 我们给服务提供商的唯一标识
* @param appId 服务提供商给的AppId
* @param appSecret 服务提供商给的App密码
*/
public QQConnectionFactory(String providerId,String appId,String appSecret) {
super(providerId, new QQServiceProvider(appId,appSecret), new QQAdapter());
}
}
⒏创建UserConnection数据表
create table UserConnection (userId varchar(255) not null,
providerId varchar(255) not null,
providerUserId varchar(255),
`rank` int not null,
displayName varchar(255),
profileUrl varchar(512),
imageUrl varchar(512),
accessToken varchar(512) not null,
secret varchar(512),
refreshToken varchar(512),
expireTime bigint,
primary key (userId, providerId, providerUserId));
create unique index UserConnectionRank on UserConnection(userId, providerId, `rank`);
⒐为用户服务类实现SocialUserDetailsService ,用于从数据库中通过QQ Id 拿到业务系统用户
/**
*
*/
package cn.coreqi.security; import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
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.Component; /**
* @author fanqi
*
*/
@Component
public class MyUserDetailsService implements UserDetailsService, SocialUserDetailsService { private Logger logger = LoggerFactory.getLogger(getClass()); @Autowired
private PasswordEncoder passwordEncoder; /*
* (non-Javadoc)
*
* @see org.springframework.security.core.userdetails.UserDetailsService#
* loadUserByUsername(java.lang.String)
*/
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
logger.info("表单登录用户名:" + username);
return buildUser(username);
} @Override
public SocialUserDetails loadUserByUserId(String userId) throws UsernameNotFoundException {
logger.info("设计登录用户Id:" + userId);
return buildUser(userId);
} private SocialUserDetails buildUser(String userId) {
// 根据用户名查找用户信息
//根据查找到的用户信息判断用户是否被冻结
String password = passwordEncoder.encode("123456");
logger.info("数据库密码是:"+password);
return new SocialUser(userId, password,
true, true, true, true,
AuthorityUtils.commaSeparatedStringToAuthorityList("admin"));
} }
⒑创建QQ登陆配置类
package cn.coreqi.social.qq.connect; import org.springframework.boot.autoconfigure.social.SocialAutoConfigurerAdapter;
import org.springframework.context.annotation.Configuration;
import org.springframework.social.connect.ConnectionFactory; /**
* QQ登录配置
*/
@Configuration
public class QQAutoConfig extends SocialAutoConfigurerAdapter {
@Override
protected ConnectionFactory<?> createConnectionFactory() {
String providerId = "qq"; //第三方id,用来决定发起第三方登录的url,默认是weixin
String appId = "";
String appSecret = "";
return new QQConnectionFactory(providerId, appId, appSecret);
}
}
⒒自定义我们自己的SpringSocial配置
package cn.coreqi.social.config; import org.springframework.social.security.SocialAuthenticationFilter;
import org.springframework.social.security.SpringSocialConfigurer; public class CoreqiSpringSocialConfig extends SpringSocialConfigurer { /**
*
* @param object
* @param <T>
* @return
*/
@Override
protected <T> T postProcess(T object) {
SocialAuthenticationFilter filter = (SocialAuthenticationFilter)super.postProcess(object);
filter.setFilterProcessesUrl("/coreqi/auth");
return (T) filter;
}
}
SpringSocialConfigurer 会在 configure方法中声明一个 SocialAuthenticationFilter,我们可以继承SpringSocialConfigurer达到自定义我们的SpringSocial配置需求。 ⒓声明一个SpringSocial的配置类
package cn.coreqi.social.config; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.encrypt.Encryptors;
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.ConnectionSignUp;
import org.springframework.social.connect.UsersConnectionRepository;
import org.springframework.social.connect.jdbc.JdbcUsersConnectionRepository;
import org.springframework.social.connect.web.ProviderSignInUtils;
import org.springframework.social.security.SpringSocialConfigurer; import javax.sql.DataSource; @Configuration
@EnableSocial
public class SocialConfig extends SocialConfigurerAdapter { @Autowired
private DataSource dataSource; @Autowired(required = false)
private ConnectionSignUp connectionSignUp; /**
*
* @param connectionFactoryLocator 作用是去根据条件去查找应该用那个connectionFactory,因为系统中可能有很多的connectionFactory。
* @return
*/
@Override
public UsersConnectionRepository getUsersConnectionRepository(ConnectionFactoryLocator connectionFactoryLocator) {
//第三个参数的作用是把插入到数据库的数据进行加解密
JdbcUsersConnectionRepository jdbcUsersConnectionRepository = new JdbcUsersConnectionRepository(dataSource,connectionFactoryLocator, Encryptors.noOpText());
//jdbcUsersConnectionRepository.setTablePrefix(); //设置数据表的前缀
if(connectionSignUp != null){
jdbcUsersConnectionRepository.setConnectionSignUp(connectionSignUp);
}
return jdbcUsersConnectionRepository;
} /**
* 声明后还需要加在SpringSecurity过滤器链上
* @return
*/
@Bean
public SpringSocialConfigurer coreqiSocialSecurityConfig(){
CoreqiSpringSocialConfig config = new CoreqiSpringSocialConfig();
config.signupUrl("/registry"); //当从业务系统中无法找到OAuth快捷登陆的用户,那么将用户引导到注册页面中
return config;
} //1.注册过程中如何拿到SpringSocial信息
//2.注册完成后如何把业务系统的用户ID传给SpringSocial
@Bean
public ProviderSignInUtils providerSignInUtils(ConnectionFactoryLocator connectionFactoryLocator){
return new ProviderSignInUtils(connectionFactoryLocator,getUsersConnectionRepository(connectionFactoryLocator));
}
}
⒔应用我们的过滤器配置
package cn.coreqi.config; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.social.security.SpringSocialConfigurer; public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private SpringSocialConfigurer coreqiSocialSecurityConfig;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.apply(coreqiSocialSecurityConfig);
}
}
⒕
package cn.coreqi.social.qq.connect; import org.springframework.social.connect.Connection;
import org.springframework.social.connect.ConnectionSignUp;
import org.springframework.stereotype.Component; /**
* 当没有从数据库中查找到第三方登录的用户,那么将执行ConnectionSignUp的execute方法生成新的用户id并存储到数据库中
*/
@Component
public class CoreqiConnectionSignUp implements ConnectionSignUp {
@Override
public String execute(Connection<?> connection) {
return connection.getDisplayName();
}
}
使用SpringSocial开发QQ登录的更多相关文章
- Spring Security构建Rest服务-1000-使用SpringSocial开发第三方登录之大白话OAuth协议
OAuth协议简介 OAuth协议要解决的问题 OAuth协议中的各种角色 OAuth协议运行流程 OAuth协议,在网上也看了一些资料,意思就是给你颁发一个临时的通行证,你拿着这个通行证可以访 ...
- 使用SpringSocial开发微信登录
⒈编写微信用户对应的数据结构 package cn.coreqi.social.weixin.entities; /** * 微信用户实体类 */ public class WeixinUserInf ...
- Spring Security构建Rest服务-0102-Spring Social开发第三方登录之qq登录
图一 基于SpringSocial实现qq登录,要走一个OAuth流程,拿到服务提供商qq返回的用户信息. 由上篇介绍的可知,用户信息被封装在了Connection里,所以最终要拿到Connectio ...
- PHP实现QQ登录的开发教程
第三方登录,就是使用大家比较熟悉的比如QQ.微信.微博等第三方软件登录自己的网站,这可以免去注册账号.快速留住用户的目的,免去了相对复杂的注册流程.下边就给大家讲一下怎么使用PHP开发QQ登录的功能. ...
- QQ登录接口(第三方登录接口)
CI框架 QQ接口(第三方登录接口PHP版) 本帖内容较多,大部分都是源码,要修改的地方只有一个,其他只要复制过去,就可以完美运行.本帖主要针对CI框架,不用下载SDK,按我下面的步骤,建文件,复制代 ...
- QQ登录功能之如何获取用于本地测试的APPID
本文主要说明一下开发者如何在QQ互联创建测试应用,从而分配给我们一套APP ID和APP KEY,在我们平时学习的时候使用. 一.QQ互联注册开发者 要想使用QQ登陆的功能,首先你必须是腾讯开发者.腾 ...
- 那些年,我们开发的接口之:QQ登录(OAuth2.0)
那些年,我们开发的接口之:QQ登录(OAuth2.0) 吴剑 2013-06-14 原创文章,转载必须注明出处:http://www.cnblogs.com/wu-jian 前言 开发这些年,做过很多 ...
- WPF开发实例——仿QQ登录界面
原文:WPF开发实例--仿QQ登录界面 版权声明:本文为博主原创文章,如需转载请标明转载地址 http://blog.csdn.net/u013981858 https://blog.csdn.net ...
- 第三方登录(QQ登录)开发流程详解
原文:http://www.cnblogs.com/it-cen/p/4338202.html 近排由于工作的繁忙,已经一个星期没写博文做分享了,接下来我对网站接入第三方登录----QQ登录的实现逻辑 ...
随机推荐
- python自动化开发-[第十六天]-bootstrap和django
今日概要: 1.bootstrap使用 2.栅格系统 3.orm简介 4.路由系统 5.mvc和mtv模式 6.django框架 1.bootstrap的引用方式 1.Bootstrap 专门构建了免 ...
- netty的对象传输
pom <!-- https://mvnrepository.com/artifact/io.netty/netty-all --> <dependency> <grou ...
- Kafka权威指南 读书笔记之(三)Kafka 生产者一一向 Kafka 写入数据
不管是把 Kafka 作为消息队列.消息总线还是数据存储平台来使用 ,总是需要有一个可以往 Kafka 写入数据的生产者和一个从 Kafka 读取数据的消费者,或者一个兼具两种角色的应用程序. 开发者 ...
- eclipse+tomcat+maven+springmvc+mybatis+mysql集成WebService插件(Axis2+CXF)
$1 环境介绍 $1.1 Eclipse Java EE IDE for Web Developers:Neon.2 Release (4.6.2) $1.2 Maven:3.3.9 $1.3 Spr ...
- 转---变量LEGB规则
Python 变量作用域的规则是 LEGB LEGB含义解释: L -- Local(function):函数内的名字空间 E -- Enclosing function locals:外部嵌套函数的 ...
- spring注解第05课 FactoryBean
1.工厂bean调用 @Configuration public class MainConfig2 {/** * 使用Spring提供的 FactoryBean(工厂Bean); * 1).默认获取 ...
- [译]Nuget.Server
原文 NuGet.Server是一个包,可用于使一个ASP.NET应用host一个package feed . 使用VS创建一个新的空WEB应用,添加Nuget.Server包. 配置应用的Packa ...
- HTTP访问控制(CORS)
当一个资源从与该资源本身所在的服务器不同的域或端口请求一个资源时,资源会发起一个跨域 HTTP 请求. 比如,站点 http://domain-a.com 的某 HTML 页面通过 <img ...
- luogu 2827 蚯蚓 单调队列/优先队列
易知可利用优先队列选取最大值: 但是通过分析可知,先取出的蚯蚓分开后仍然要比后分的长,所以可直接利用单调队列找队头即可,分三个单调队列,分别找未切割,切割,切割2三种情况 #include<bi ...
- dubbo 初探
dubbo官网:http://dubbo.io Dubbo背景和简介(摘自 http://blog.csdn.net/noaman_wgs/article/details/70214612) Dubb ...