申请开通QQ互联登录
- 文档地址: 【QQ登录】OAuth2.0开发文档.
- 申请应用 地址
注意事项:
- 在网站首页加入QQ登录的按钮(下面会说具体怎么加)
- 网站信息和备案信息一致
模式
本文使用的是 server-side模式 又称Web Server Flow; 适用于需要从web server访问的应用,例如Web/wap网站。
其授权验证流程示意图如下(图片来源:OAuth2.0协议草案V21的4.1节 )
对于应用而言,需要进行两步:
- 获取Authorization Code;
- 通过Authorization Code获取Access Token
引入资源
在public.index.html中引入js
<script type="text/javascript" src="http://qzonestyle.gtimg.cn/qzone/openapi/qc_loader.js" data-callback="true"
data-appid="APPID" data-redirecturi="回调地址" charset="utf-8">
</script>
其中data-callback="true"这个参数的作用是在在授权完成后会自动帮你关掉回调页面.问什么需要关掉页面.下面会说
添加按钮和函数
- 添加按钮
<span>Server-side模式</span> <img src="../../assets/image/bt_blue_76X24.png" @click="doQQLogin()" >
- 添加函数 点击按钮携带相关参数跳转到QQ互联的授权页,授权完成后会拿到授权码回调到回调页面,
doQQLogin() {
//获取QQ连接
getqqLoginInfo().then((res) => {
window.location.href =
"https://graph.qq.com/oauth2.0/authorize?response_type=code&client_id=" +
res.data.qqAppId +
"&redirect_uri=" +
res.data.qqRedirectUri +
"&state=" +
res.data.state;
});
},
回调页面
这个页面是在页面QQ登录完成后由腾讯帮你调用的接口,会在接口后面拼接参数?code=XXXXXXXXXXXXXXXXXXX,,这个页面的作用是:在接收到code之后将code发送到后台,后台接收到之后,会去QQ互联换取token,注意一点就是这个code是一次性,用完即焚,获取到token之后根据token和AppId可以用户信息,这样就可以执行登录了,登录完成后跳转到首页.
<template>
</template>
<script>
import { qqCallback } from "@/api/index";
export default {
name: "qqCallback",
mounted() {
var accessToken = this.$utils.getUrlKey("access_token");
var code = this.$utils.getUrlKey("code");
//client_side模式
if (accessToken && typeof accessToken != "undefined") {
console.log("client_side模式:" + accessToken);
if (QC.Login.check()) {
this.$message.info("qq成功登录");
this.$store.dispatch("user/qqlogin", accessToken).then(() => {
this.$router.push("/");
});
}
}
//server-side模式
if (code && typeof code != "undefined") {
console.log("server-side模式:" + code);
this.$store.dispatch("user/qqCodelogin", code).then(() => {
this.$router.push("/");
});
}
},
};
</script>
<style>
</style>
后台接口
rest接口
/**
* desc: 前端获取到QQ互联回调的授权码时,将授权码传到后台,获取access_token,并验证token执行登录,返回当前系统的token.
*
* @return cn.com.zsw.gblog.vo.R
* @author: shiwangZhou
* @date: 2020-09-23 8:40
* @param: code QQ互联回调的授权码
*/
@GetMapping("qq/token")
public R getAccessTokenByCode(String code) {
String accessTokenByCode = oauthClient.getAccessTokenByCode(qqAppId, appKey, code, qqRedirectUri);
log.info("请求access_token返回结果:" + accessTokenByCode);
GetAccessTokenDTO accessTokenDTO = UrlUtils.paramToDTO(accessTokenByCode, GetAccessTokenDTO.class);
String token = callback(accessTokenDTO.getAccess_token());
Assert.notBlank(token, "未能获取到token,登录失败");
return R.SUCCESS.data(token);
}
private String callback(String accessToken) {
String openIdStr = oauthClient.getOpenId(accessToken);
log.info("请求openID返回结果:" + openIdStr);
String substring = openIdStr.substring(10, openIdStr.length() - 4);
GetOpenIdRes openIdRes = JSON.parseObject(substring, GetOpenIdRes.class);
GetUsrInfoRes userInfo = oauthClient.getUserInfo(accessToken, openIdRes.getClient_id(), openIdRes.getOpen_id());
ThirdPartUser thirdPartUser = thirdPartUserService.getOne(Wrappers.<ThirdPartUser>lambdaQuery().eq(ThirdPartUser::getOpenId,
openIdRes.getOpen_id()));
GbUser qwe = userService.getOne(Wrappers.<GbUser>lambdaQuery().eq(GbUser::getUsername, "qwe"));
UserDetails userDetails = userDetailsService.loadUserByUsername(qwe.getUsername());
if (thirdPartUser == null) {
thirdPartUser = new ThirdPartUser();
thirdPartUser.setAvatar(userInfo.getAvatar());
thirdPartUser.setGender("男".equalsIgnoreCase(userInfo.getGender()) ? 1 : 0);
thirdPartUser.setId(IdUtil.createSnowflake(1, 1).nextId());
thirdPartUser.setNickname(userInfo.getNickname());
thirdPartUser.setUserId(qwe.getId());
thirdPartUser.setOpenId(openIdRes.getOpen_id());
thirdPartUser.setUserInfo(JSON.toJSONString(userInfo));
thirdPartUserService.save(thirdPartUser);
} else {
userDetails = userDetailsService.loadUserByUsername(qwe.getUsername());
UsernamePasswordAuthenticationToken authenticationToken =
new UsernamePasswordAuthenticationToken(userDetails.getUsername(), userDetails.getPassword(), userDetails.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
}
String id = IdUtil.createSnowflake(1, 1).nextIdStr();
redisCache.setCacheObject(SysConstants.REDIS_JWT_TOKEN_ID + id, JSON.toJSONString(userDetails), 2, TimeUnit.HOURS);
return id;
}
总结:
- code模式由后端调用,相对于简化模式更加安全
- 简化模式对接起来更加简单
代码地址:
本文由博客一文多发平台 OpenWrite 发布!