⒈编写QQ用户对应的数据结构

  1 package cn.coreqi.social.qq.entities;
  2
  3 /**
  4  * 封装QQ的用户信息
  5  */
  6 public class QQUserInfo {
  7
  8     /**
  9      * 返回码
 10      */
 11     private String ret;
 12     /**
 13      * 如果ret<0,会有相应的错误信息提示,返回数据全部用UTF-8编码。
 14      */
 15     private String msg;
 16     /**
 17      *
 18      */
 19     private String openId;
 20     /**
 21      * 不知道什么东西,文档上没写,但是实际api返回里有。
 22      */
 23     private String is_lost;
 24     /**
 25      * 省(直辖市)
 26      */
 27     private String province;
 28     /**
 29      * 市(直辖市区)
 30      */
 31     private String city;
 32     /**
 33      * 出生年月
 34      */
 35     private String year;
 36     /**
 37      * 用户在QQ空间的昵称。
 38      */
 39     private String nickname;
 40     /**
 41      * 大小为30×30像素的QQ空间头像URL。
 42      */
 43     private String figureurl;
 44     /**
 45      * 大小为50×50像素的QQ空间头像URL。
 46      */
 47     private String figureurl_1;
 48     /**
 49      * 大小为100×100像素的QQ空间头像URL。
 50      */
 51     private String figureurl_2;
 52     /**
 53      * 大小为40×40像素的QQ头像URL。
 54      */
 55     private String figureurl_qq_1;
 56     /**
 57      * 大小为100×100像素的QQ头像URL。需要注意,不是所有的用户都拥有QQ的100×100的头像,但40×40像素则是一定会有。
 58      */
 59     private String figureurl_qq_2;
 60     /**
 61      * 性别。 如果获取不到则默认返回”男”
 62      */
 63     private String gender;
 64     /**
 65      * 标识用户是否为黄钻用户(0:不是;1:是)。
 66      */
 67     private String is_yellow_vip;
 68     /**
 69      * 标识用户是否为黄钻用户(0:不是;1:是)
 70      */
 71     private String vip;
 72     /**
 73      * 黄钻等级
 74      */
 75     private String yellow_vip_level;
 76     /**
 77      * 黄钻等级
 78      */
 79     private String level;
 80     /**
 81      * 标识是否为年费黄钻用户(0:不是; 1:是)
 82      */
 83     private String is_yellow_year_vip;
 84
 85
 86     public String getRet() {
 87         return ret;
 88     }
 89
 90     public void setRet(String ret) {
 91         this.ret = ret;
 92     }
 93
 94     public String getMsg() {
 95         return msg;
 96     }
 97
 98     public void setMsg(String msg) {
 99         this.msg = msg;
100     }
101
102     public String getOpenId() {
103         return openId;
104     }
105
106     public void setOpenId(String openId) {
107         this.openId = openId;
108     }
109
110     public String getIs_lost() {
111         return is_lost;
112     }
113
114     public void setIs_lost(String is_lost) {
115         this.is_lost = is_lost;
116     }
117
118     public String getProvince() {
119         return province;
120     }
121
122     public void setProvince(String province) {
123         this.province = province;
124     }
125
126     public String getCity() {
127         return city;
128     }
129
130     public void setCity(String city) {
131         this.city = city;
132     }
133
134     public String getYear() {
135         return year;
136     }
137
138     public void setYear(String year) {
139         this.year = year;
140     }
141
142     public String getNickname() {
143         return nickname;
144     }
145
146     public void setNickname(String nickname) {
147         this.nickname = nickname;
148     }
149
150     public String getFigureurl() {
151         return figureurl;
152     }
153
154     public void setFigureurl(String figureurl) {
155         this.figureurl = figureurl;
156     }
157
158     public String getFigureurl_1() {
159         return figureurl_1;
160     }
161
162     public void setFigureurl_1(String figureurl_1) {
163         this.figureurl_1 = figureurl_1;
164     }
165
166     public String getFigureurl_2() {
167         return figureurl_2;
168     }
169
170     public void setFigureurl_2(String figureurl_2) {
171         this.figureurl_2 = figureurl_2;
172     }
173
174     public String getFigureurl_qq_1() {
175         return figureurl_qq_1;
176     }
177
178     public void setFigureurl_qq_1(String figureurl_qq_1) {
179         this.figureurl_qq_1 = figureurl_qq_1;
180     }
181
182     public String getFigureurl_qq_2() {
183         return figureurl_qq_2;
184     }
185
186     public void setFigureurl_qq_2(String figureurl_qq_2) {
187         this.figureurl_qq_2 = figureurl_qq_2;
188     }
189
190     public String getGender() {
191         return gender;
192     }
193
194     public void setGender(String gender) {
195         this.gender = gender;
196     }
197
198     public String getIs_yellow_vip() {
199         return is_yellow_vip;
200     }
201
202     public void setIs_yellow_vip(String is_yellow_vip) {
203         this.is_yellow_vip = is_yellow_vip;
204     }
205
206     public String getVip() {
207         return vip;
208     }
209
210     public void setVip(String vip) {
211         this.vip = vip;
212     }
213
214     public String getYellow_vip_level() {
215         return yellow_vip_level;
216     }
217
218     public void setYellow_vip_level(String yellow_vip_level) {
219         this.yellow_vip_level = yellow_vip_level;
220     }
221
222     public String getLevel() {
223         return level;
224     }
225
226     public void setLevel(String level) {
227         this.level = level;
228     }
229
230     public String getIs_yellow_year_vip() {
231         return is_yellow_year_vip;
232     }
233
234     public void setIs_yellow_year_vip(String is_yellow_year_vip) {
235         this.is_yellow_year_vip = is_yellow_year_vip;
236     }
237 }

⒉编写一个QQ API接口用于获取QQ用户信息

 1 package cn.coreqi.social.qq.api;
 2
 3 import cn.coreqi.social.qq.entities.QQUserInfo;
 4
 5 public interface QQ {
 6     /**
 7      * 返回QQ中的用户信息
 8      * @return
 9      */
10     QQUserInfo getUserInfo();
11 }

⒊编写一个QQ API接口实现

 1 package cn.coreqi.social.qq.api.impl;
 2
 3 import cn.coreqi.social.qq.api.QQ;
 4 import cn.coreqi.social.qq.entities.QQUserInfo;
 5 import com.fasterxml.jackson.databind.ObjectMapper;
 6 import org.apache.commons.lang.StringUtils;
 7 import org.springframework.social.oauth2.AbstractOAuth2ApiBinding;
 8 import org.springframework.social.oauth2.TokenStrategy;
 9
10 import java.io.IOException;
11
12 /**
13  * 获取用户信息
14  * 不能声明为单例,因为每个用户的验证是不同的
15  */
16 public class QQImpl extends AbstractOAuth2ApiBinding implements QQ {
17
18     private static final String URL_GET_OPENID = "https://graph.qq.com/oauth2.0/me?access_token=%s";    //获取openid的请求地址
19     private static final String URL_GET_USERINFO = "https://graph.qq.com/user/get_user_info?oauth_consumer_key=%s&openid=%s";   //获取用户信息的请求地址
20
21     private String appid;   //申请QQ登录成功后,分配给应用的appid
22     private String openid;  //用户的ID,与QQ号码一一对应。
23
24     private ObjectMapper objectMapper = new ObjectMapper(); //用于序列化Json数据
25
26     public QQImpl(String accessToken,String appid){
27         super(accessToken, TokenStrategy.ACCESS_TOKEN_PARAMETER);   //将token作为查询参数
28         this.appid = appid;
29
30         String url = String.format(URL_GET_OPENID,accessToken); //拼接成最终的openid的请求地址
31         String result = getRestTemplate().getForObject(url,String.class);
32
33         System.out.println(result);
34
35         this.openid = StringUtils.substringBetween(result,"\"openid\":\"","\"}");
36
37     }
38
39     @Override
40     public QQUserInfo getUserInfo() {
41         String url = String.format(URL_GET_USERINFO,appid,openid);  拼接成最终的获取用户信息的请求地址
42         String result = getRestTemplate().getForObject(url,String.class);
43         System.out.println(result);
44         QQUserInfo userInfo = null;
45         try {
46             userInfo =  objectMapper.readValue(result,QQUserInfo.class);
47             userInfo.setOpenId(openid);
48             return userInfo;
49         } catch (Exception e) {
50             throw new RuntimeException("获取用户信息失败",e);
51         }
52     }
53 }

⒋编写QQ OAuth2认证流程模板类。

 1 package cn.coreqi.social.qq.connect;
 2
 3 import org.apache.commons.lang.StringUtils;
 4 import org.slf4j.Logger;
 5 import org.slf4j.LoggerFactory;
 6 import org.springframework.http.converter.StringHttpMessageConverter;
 7 import org.springframework.social.oauth2.AccessGrant;
 8 import org.springframework.social.oauth2.OAuth2Template;
 9 import org.springframework.util.MultiValueMap;
10 import org.springframework.web.client.RestTemplate;
11 import java.nio.charset.Charset;
12
13 public class QQOAuth2Template extends OAuth2Template {
14
15     private Logger logger = LoggerFactory.getLogger(getClass());
16
17     public QQOAuth2Template(String clientId, String clientSecret, String authorizeUrl, String accessTokenUrl) {
18         super(clientId, clientSecret, authorizeUrl, accessTokenUrl);
19         setUseParametersForClientAuthentication(true);
20     }
21
22     @Override
23     protected AccessGrant postForAccessGrant(String accessTokenUrl, MultiValueMap<String, String> parameters) {
24         String responseStr = getRestTemplate().postForObject(accessTokenUrl, parameters, String.class);
25
26         logger.info("获取accessToke的响应:"+responseStr);
27
28         String[] items = StringUtils.splitByWholeSeparatorPreserveAllTokens(responseStr, "&");
29
30         String accessToken = StringUtils.substringAfterLast(items[0], "=");
31         Long expiresIn = new Long(StringUtils.substringAfterLast(items[1], "="));
32         String refreshToken = StringUtils.substringAfterLast(items[2], "=");
33
34         return new AccessGrant(accessToken, null, refreshToken, expiresIn);
35     }
36
37     @Override
38     protected RestTemplate createRestTemplate() {
39         RestTemplate restTemplate = super.createRestTemplate();
40         restTemplate.getMessageConverters().add(new StringHttpMessageConverter(Charset.forName("UTF-8")));
41         return restTemplate;
42     }
43 }

⒌编写QQ的OAuth2流程处理器的提供器

 1 package cn.coreqi.social.qq.connect;
 2
 3 import cn.coreqi.social.qq.api.QQ;
 4 import cn.coreqi.social.qq.api.impl.QQImpl;
 5 import org.springframework.social.oauth2.AbstractOAuth2ServiceProvider;
 6
 7 /**
 8  * 泛型是API接口的类型
 9  */
10 public class QQServiceProvider extends AbstractOAuth2ServiceProvider<QQ> {
11
12     private static final String URL_AUTHORIZE = "https://graph.qq.com/oauth2.0/authorize";  //获取授权码地址
13     private static final String URL_ACCESS_TOKEN = "https://graph.qq.com/oauth2.0/token";   //获取用户令牌地址
14
15     private String appId;
16
17
18     public QQServiceProvider(String appId,String appSecret) {
19         super(new QQOAuth2Template(appId,appSecret,URL_AUTHORIZE,URL_ACCESS_TOKEN));
20         this.appId = appId;
21     }
22
23     @Override
24     public QQ getApi(String accessToken) {
25         return new QQImpl(accessToken,appId);
26     }
27 }

⒍编写QQ API适配器,将从QQ API拿到的用户数据模型转换为Spring Social的标准用户数据模型。

 1 package cn.coreqi.social.qq.connect;
 2
 3 import cn.coreqi.social.qq.api.QQ;
 4 import cn.coreqi.social.qq.entities.QQUserInfo;
 5 import org.springframework.social.connect.ApiAdapter;
 6 import org.springframework.social.connect.ConnectionValues;
 7 import org.springframework.social.connect.UserProfile;
 8
 9 import java.io.IOException;
10
11 /**
12  *  泛型是指当前API适配器适配API的类型是什么
13  */
14 public class QQAdapter implements ApiAdapter<QQ> {
15
16     /**
17      * 用来测试当前的API是否可用
18      * @param qq
19      * @return
20      */
21     @Override
22     public boolean test(QQ qq) {
23         return true;
24     }
25
26     /**
27      * 将服务提供商个性化的用户信息映射到ConnectionValues标准的数据化结构上
28      * @param qq
29      * @param connectionValues
30      */
31     @Override
32     public void setConnectionValues(QQ qq, ConnectionValues connectionValues) {
33         QQUserInfo userInfo = qq.getUserInfo();
34         connectionValues.setDisplayName(userInfo.getNickname());  //显示的用户名称
35         connectionValues.setImageUrl(userInfo.getFigureurl_qq_1()); //用户的头像
36         connectionValues.setProfileUrl(null);   //个人主页
37         connectionValues.setProviderUserId(userInfo.getOpenId());   //QQ的唯一标识
38     }
39
40     /**
41      * 和上面的方法类似
42      * @param qq
43      * @return
44      */
45     @Override
46     public UserProfile fetchUserProfile(QQ qq) {
47         return null;
48     }
49
50     /**
51      *
52      * @param qq
53      * @param s
54      */
55     @Override
56     public void updateStatus(QQ qq, String s) {
57
58     }
59 }

⒎创建QQ连接工厂

 1 package cn.coreqi.social.qq.connect;
 2
 3 import cn.coreqi.social.qq.api.QQ;
 4 import org.springframework.social.connect.support.OAuth2ConnectionFactory;
 5
 6 public class QQConnectionFactory extends OAuth2ConnectionFactory<QQ> {
 7
 8     /**
 9      *
10      * @param providerId    我们给服务提供商的唯一标识
11      * @param appId 服务提供商给的AppId
12      * @param appSecret 服务提供商给的App密码
13      */
14     public QQConnectionFactory(String providerId,String appId,String appSecret) {
15         super(providerId, new QQServiceProvider(appId,appSecret), new QQAdapter());
16     }
17 }

⒏创建UserConnection数据表

 1 create table UserConnection (userId varchar(255) not null,
 2     providerId varchar(255) not null,
 3     providerUserId varchar(255),
 4     `rank` int not null,
 5     displayName varchar(255),
 6     profileUrl varchar(512),
 7     imageUrl varchar(512),
 8     accessToken varchar(512) not null,
 9     secret varchar(512),
10     refreshToken varchar(512),
11     expireTime bigint,
12     primary key (userId, providerId, providerUserId));
13 create unique index UserConnectionRank on UserConnection(userId, providerId, `rank`);

⒐为用户服务类实现SocialUserDetailsService ,用于从数据库中通过QQ Id 拿到业务系统用户

 1 /**
 2  *
 3  */
 4 package cn.coreqi.security;
 5
 6 import org.slf4j.Logger;
 7 import org.slf4j.LoggerFactory;
 8 import org.springframework.beans.factory.annotation.Autowired;
 9 import org.springframework.security.core.authority.AuthorityUtils;
10 import org.springframework.security.core.userdetails.UserDetails;
11 import org.springframework.security.core.userdetails.UserDetailsService;
12 import org.springframework.security.core.userdetails.UsernameNotFoundException;
13 import org.springframework.security.crypto.password.PasswordEncoder;
14 import org.springframework.social.security.SocialUser;
15 import org.springframework.social.security.SocialUserDetails;
16 import org.springframework.social.security.SocialUserDetailsService;
17 import org.springframework.stereotype.Component;
18
19 /**
20  * @author fanqi
21  *
22  */
23 @Component
24 public class MyUserDetailsService implements UserDetailsService, SocialUserDetailsService {
25
26     private Logger logger = LoggerFactory.getLogger(getClass());
27
28     @Autowired
29     private PasswordEncoder passwordEncoder;
30
31     /*
32      * (non-Javadoc)
33      *
34      * @see org.springframework.security.core.userdetails.UserDetailsService#
35      * loadUserByUsername(java.lang.String)
36      */
37     @Override
38     public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
39         logger.info("表单登录用户名:" + username);
40         return buildUser(username);
41     }
42
43     @Override
44     public SocialUserDetails loadUserByUserId(String userId) throws UsernameNotFoundException {
45         logger.info("设计登录用户Id:" + userId);
46         return buildUser(userId);
47     }
48
49     private SocialUserDetails buildUser(String userId) {
50         // 根据用户名查找用户信息
51         //根据查找到的用户信息判断用户是否被冻结
52         String password = passwordEncoder.encode("123456");
53         logger.info("数据库密码是:"+password);
54         return new SocialUser(userId, password,
55                 true, true, true, true,
56                 AuthorityUtils.commaSeparatedStringToAuthorityList("admin"));
57     }
58
59 }

⒑创建QQ登陆配置类

 1 package cn.coreqi.social.qq.connect;
 2
 3 import org.springframework.boot.autoconfigure.social.SocialAutoConfigurerAdapter;
 4 import org.springframework.context.annotation.Configuration;
 5 import org.springframework.social.connect.ConnectionFactory;
 6
 7 /**
 8  * QQ登录配置
 9  */
10 @Configuration
11 public class QQAutoConfig extends SocialAutoConfigurerAdapter {
12     @Override
13     protected ConnectionFactory<?> createConnectionFactory() {
14         String providerId = "qq";   //第三方id,用来决定发起第三方登录的url,默认是weixin
15         String appId = "";
16         String appSecret = "";
17         return new QQConnectionFactory(providerId, appId, appSecret);
18     }
19 }

⒒自定义我们自己的SpringSocial配置

 1 package cn.coreqi.social.config;
 2
 3 import org.springframework.social.security.SocialAuthenticationFilter;
 4 import org.springframework.social.security.SpringSocialConfigurer;
 5
 6 public class CoreqiSpringSocialConfig extends SpringSocialConfigurer {
 7
 8     /**
 9      *
10      * @param object
11      * @param <T>
12      * @return
13      */
14     @Override
15     protected <T> T postProcess(T object) {
16         SocialAuthenticationFilter filter = (SocialAuthenticationFilter)super.postProcess(object);
17         filter.setFilterProcessesUrl("/coreqi/auth");
18         return (T) filter;
19     }
20 }

SpringSocialConfigurer 会在 configure方法中声明一个 SocialAuthenticationFilter,我们可以继承SpringSocialConfigurer达到自定义我们的SpringSocial配置需求。

⒓声明一个SpringSocial的配置类
 1 package cn.coreqi.social.config;
 2
 3 import org.springframework.beans.factory.annotation.Autowired;
 4 import org.springframework.context.annotation.Bean;
 5 import org.springframework.context.annotation.Configuration;
 6 import org.springframework.security.crypto.encrypt.Encryptors;
 7 import org.springframework.social.config.annotation.EnableSocial;
 8 import org.springframework.social.config.annotation.SocialConfigurerAdapter;
 9 import org.springframework.social.connect.ConnectionFactoryLocator;
10 import org.springframework.social.connect.ConnectionSignUp;
11 import org.springframework.social.connect.UsersConnectionRepository;
12 import org.springframework.social.connect.jdbc.JdbcUsersConnectionRepository;
13 import org.springframework.social.connect.web.ProviderSignInUtils;
14 import org.springframework.social.security.SpringSocialConfigurer;
15
16 import javax.sql.DataSource;
17
18 @Configuration
19 @EnableSocial
20 public class SocialConfig extends SocialConfigurerAdapter {
21
22     @Autowired
23     private DataSource dataSource;
24
25     @Autowired(required = false)
26     private ConnectionSignUp connectionSignUp;
27
28     /**
29      *
30      * @param connectionFactoryLocator  作用是去根据条件去查找应该用那个connectionFactory,因为系统中可能有很多的connectionFactory。
31      * @return
32      */
33     @Override
34     public UsersConnectionRepository getUsersConnectionRepository(ConnectionFactoryLocator connectionFactoryLocator) {
35         //第三个参数的作用是把插入到数据库的数据进行加解密
36         JdbcUsersConnectionRepository jdbcUsersConnectionRepository = new JdbcUsersConnectionRepository(dataSource,connectionFactoryLocator, Encryptors.noOpText());
37         //jdbcUsersConnectionRepository.setTablePrefix(); //设置数据表的前缀
38         if(connectionSignUp != null){
39             jdbcUsersConnectionRepository.setConnectionSignUp(connectionSignUp);
40         }
41         return jdbcUsersConnectionRepository;
42     }
43
44     /**
45      * 声明后还需要加在SpringSecurity过滤器链上
46      * @return
47      */
48     @Bean
49     public SpringSocialConfigurer coreqiSocialSecurityConfig(){
50         CoreqiSpringSocialConfig config = new CoreqiSpringSocialConfig();
51         config.signupUrl("/registry");  //当从业务系统中无法找到OAuth快捷登陆的用户,那么将用户引导到注册页面中
52         return config;
53     }
54
55     //1.注册过程中如何拿到SpringSocial信息
56     //2.注册完成后如何把业务系统的用户ID传给SpringSocial
57     @Bean
58     public ProviderSignInUtils providerSignInUtils(ConnectionFactoryLocator connectionFactoryLocator){
59         return new ProviderSignInUtils(connectionFactoryLocator,getUsersConnectionRepository(connectionFactoryLocator));
60     }
61 }

⒔应用我们的过滤器配置
 1 package cn.coreqi.config;
 2
 3 import org.springframework.beans.factory.annotation.Autowired;
 4 import org.springframework.security.config.annotation.web.builders.HttpSecurity;
 5 import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
 6 import org.springframework.social.security.SpringSocialConfigurer;
 7
 8 public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
 9     @Autowired
10     private SpringSocialConfigurer coreqiSocialSecurityConfig;
11     @Override
12     protected void configure(HttpSecurity http) throws Exception {
13         http.apply(coreqiSocialSecurityConfig);
14     }
15 }

 1 package cn.coreqi.social.qq.connect;
 2
 3 import org.springframework.social.connect.Connection;
 4 import org.springframework.social.connect.ConnectionSignUp;
 5 import org.springframework.stereotype.Component;
 6
 7 /**
 8  * 当没有从数据库中查找到第三方登录的用户,那么将执行ConnectionSignUp的execute方法生成新的用户id并存储到数据库中
 9  */
10 @Component
11 public class CoreqiConnectionSignUp implements ConnectionSignUp {
12     @Override
13     public String execute(Connection<?> connection) {
14         return connection.getDisplayName();
15     }
16 }


转载于:https://www.cnblogs.com/fanqisoft/p/10691234.html

使用SpringSocial开发QQ登录相关推荐

  1. SpringSocial 开发 QQ 登录

    本篇文章带着大家在自己的系统中集成 QQ 第三方登录 不管是做什么操作,官方文档与手册绝对是最靠谱的.所以我们先来看一下 QQ 互联官网的文档 QQ 互联介绍 QQ 互联官网 在 QQ 互联官网上,我 ...

  2. SpringSecurity和SpringSocial实现QQ登录

    siki学院课程: http://www.sikiedu.com/course/366 (SpringSecurity和SpringSocial认证授权) http://www.sikiedu.com ...

  3. SpringSocial之QQ登录

    编写顺序为: Api 获取用户信息 Oauth2Operations 代表用户与服务提供者进行Oauth认证 ServiceProvider 服务提供商 ApiAdapter 连接统一的{@link ...

  4. SpringSocial之微信登录

    编写顺序同SpringSocial之QQ登录 创建用户信息类WeiXinUserInfo: package com.cong.security.core.social.weixin.api;impor ...

  5. spring-security学习(七)——QQ登录(上篇)

    文章目录 OAuth简介 OAuth协议要解决的问题 spring-social简介 QQ登录(实例) 1.实现Api与ServiceProvider 2.获取到用户信息之后的处理 3.一些需要的配置 ...

  6. php跳转到qq界面,PHP实现QQ登录的开原理和实现过程

    第三方登录,就是使用大家比较熟悉的比如QQ.微信.微博等第三方软件登录自己的网站,这可以免去注册账号.快速留住用户的目的,免去了相对复杂的注册流程.下边就给大家讲一下怎么使用PHP开发QQ登录的功能. ...

  7. php mysql登录实现原理_PHP实现QQ登录的开原理和实现过程

    第三方登录,就是使用大家比较熟悉的比如QQ.微信.微博等第三方软件登录自己的网站,这可以免去注册账号.快速留住用户的目的,免去了相对复杂的注册流程.下边就给大家讲一下怎么使用PHP开发QQ登录的功能. ...

  8. 使用 apifm 插件进行 Flutter 云开发——QQ一键登录/注册

    在你的 App 中集成手机QQ一键授权(注册)登录功能,达到快速注册.快速登录功能 按照本教程的操作指引,预计5分钟即可帮你实现并掌握QQ登录的实现 申请开通QQ互联 https://connect. ...

  9. Laravel第三方登录开发之实现QQ登录

    在我们的项目开发中,第三方登录可以很好的为用户提供便捷,比如微信.QQ.微博登录等等. Laravel,作为一个优雅的PHP框架,已经集成了诸多第三方登录插件. 本文,将详细论述如何在Laravel框 ...

最新文章

  1. 策略模式,状态模式,监听模式之间的区分。
  2. html input image 尺寸,HTML DOM Input Image 对象
  3. python中的及||
  4. 205. jetcache:你需要知道的小技巧
  5. 使用ML.NET模型生成器来完成图片性别识别
  6. 亿佰特电源模块:无线通信模块电平转换指南
  7. python 函数参数传递机制_Python函数参数传递机制(超级详细)
  8. Apache ab并发负载压力测试
  9. 《SQL Server企业级平台管理实践》读书笔记——SQL Server如何设置自动增长和自动收缩项...
  10. 【Zookeeper学习】Apache Zookeeper项目简介
  11. 简要说明python的缩进规则_关于python的缩进规则的知识点详解
  12. springboot 集成 log4j,log4j配置不同包不同日志输出级别(按包输出不同级别日志)
  13. Webpack中 CDN加速
  14. 论文撰写八大技巧与八大心得,一文读懂
  15. ppt学习07——动画
  16. 基于springboot的课堂考勤签到打卡小程序
  17. java植物大战僵尸小游戏
  18. 欢迎Edrp开发组第一个成员Zhuang Liu的加入!
  19. Python数据结构——二叉树排序
  20. 【ctf】ret2text

热门文章

  1. CSDN 博客板块升级规则
  2. 复习2个月拿下美团Android岗offer,还有点不容易啊!!
  3. Windows编程基础(转)
  4. html5制作颜色的诗句,有关描写色彩颜色的诗句
  5. 关于短路保护三极管对管应用电路
  6. 杰里之AD150之短路保护配置说明【篇】
  7. 煽情的儿子553=随笔
  8. cve-2021-3156 linux下sudo堆缓冲区溢出漏洞的修复尝试
  9. 调试 DWARF 和 STAB 格式
  10. 程序员,也需要培养气质啊!