准备工作
互联的方式有多种,包含了微信,qq,github,gitee等等。
平常自己设计一个网站都要设计一个登录注册的网页给用户去进行登录注册使用。我们可以使用更加加单的方式方便用户去登录注册。不要让用户去自己输入信息,可以通过认证授权这样的方式去让用户更加方便快捷的去登陆注册。
首先,你需要去一个网站。qq互联的官方网站。
qq互联
我个人进入界面是这个样式。因为我已经创建过应用了,并且已经审核通过了。
如果你没有创建过应用的话,你需要去创建应用。
注意下面的填写信息。你需要注意你的网站在这里已经上线了。并且可以访问到。不然审核不给通过。还有这个回调地址填什么,第一下填写的话,你可能不知道填写什么。其实很简单。就是你请求这个互联网站后,它还会给你返回一个url,这个url 格式如下。
https://www.daodaozi.xyz/oauth/login/qq?#access_token=…&expires_in=…
?前面的是你的回调地址,并且将来你请求qq互联后,返回回调会跳转到这里。?后面是携带的一些参数。其中这个access_token是最重要的。他是全局唯一的接口调用access_Token。Access Token 是一种用于访问资源 API 的认证模式,可以是 Opaque Access Token 或 JWT Access Token。后面请求后端获取用户信息需要用到。
还有网站图标一定要按照标准的来,你可以去网上随便找一种做这种图片的网站,是可以的。但是随便一张图片是通过不了的。主办单位就写自己买服务器的提供方,比如说阿里云这样的、备案地址也要写。
后面据没有什么需要注意的地方了。这里审核通过后,就可以写代码并进行调试了。
前后端代码
前端
在此之前你需要引入qq互联的一段sdk代码。是cdn的方式
在vue项目的index.html里面。
下面这个data-appid就是你成功创建应用后申请到的appid。
data-redirecturl就是上面说的回调地址。
<script src="https://connect.qq.com/qc_jssdk.js" data-appid="" data-redirecturi=""></script>
vue前端这样写。首先用户点击你提供的qq登录的按钮,然后触发一个方法。
这个方法demo如下。btnId也是你的appid
qqLogin(){QC.Login.showPopup({btnId: '',redirectURI: ''})},
当用户点击这个按钮触发这个方法后会跳到一个qq登录页面。就是我们常见到的。这里用户可以去进行授权登录。通道url可以看到它是一个oauth2的方式。
vue再给他写一个回调页面。这是我写的一个回调后的页面。一个动画页面。
<template><div class="boxLoading"></div>
</template>
<script>
import QC from 'QC';
export default{data(){return{};},created() {this.doPostForLogin()},methods:{doPostForLogin(){let this_ = this// 反正这里需要再定义一个变量保存目前这个指向,过了下面这个QC.Login.check this就变了if (QC.Login.check()) {// 该处的openId,accessToken就是后台需要的参数了,后台可以通过这些参数获取临时登录凭证,然后就是自己的逻辑了QC.Login.getMe(function (openId, accessToken) {let params = {openId: openId,accessToken: accessToken} this_.$http.post("你的后端地址", params).then(res => {//请求后端的处理返回响应。请求后端是为了授权获取用户登录信息,包括头像昵称等。if(res.code!=200){this_.$message({message: res.message,type: "error",});return}if (!this_.$common.isEmpty(res.data)) {// debuggerif(res.data.isBoss||res.data.isAdmin){// debuggerlocalStorage.setItem("adminToken", res.data.accessToken);// this.$router.push("/")this_.$store.commit("loadCurrentAdmin", res.data);}else{}this_.$store.commit("loadCurrentUser", res.data);localStorage.setItem("userToken", res.data.accessToken);this_.account = "";this_.password = "";}swal({title: '登录成功!',imageUrl : "https://pic1.zhimg.com/v2-0c908ca2b357aa4aba20fe354697abbc_r.jpg",animation: 'pop',timer: 2000,})setTimeout(function(){window.location.href = "/"},3000)}).catch((error) => {this_.$message({message: error.message,type: "error"});})})// 指定接口访问失败的接收函数,f为失败返回Response对象}},}
}</script>
<style scoped>
.boxLoading { width: 50px;height: 50px;margin: auto;position: absolute;left: 0;right: 0;top: 0;bottom: 0;
}
.boxLoading:before {content: '';width: 50px;height: 5px;background: #fff;opacity: 0.7;position: absolute;top: 59px;left: 0;border-radius: 50%;animation: shadow .5s linear infinite;
}
.boxLoading:after {content: '';width: 50px;height: 50px;background: #e04960;animation: animate .5s linear infinite;position: absolute;top: 0;left: 0;border-radius: 3px;
}
@keyframes animate {17% {border-bottom-right-radius: 3px;}25% {transform: translateY(9px) rotate(22.5deg);}50% {transform: translateY(18px) scale(1, .9) rotate(45deg);border-bottom-right-radius: 40px;}75% {transform: translateY(9px) rotate(67.5deg);}100% {transform: translateY(0) rotate(90deg);}
}
@keyframes shadow {0%, 100% {transform: scale(1, 1);}50% {transform: scale(1.2, 1);}
}</style>
swal他是这个sweetalert消息提示组件。想用的话自己可以去了解下使用方式。
还有就是这个QC你需要注意一下。就是这里可能需要在vue里面配置一下。
前端差不多就这些。
来看后端代码
后端
后端请求demo
封装一个Vo实体类 用来接收前端实体参数
package com.Id.poetry.vo;import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;import javax.validation.constraints.NotBlank;@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@ApiModel(description = "qq登录授权")
public class QQLoginVo {@NotBlank(message = "openId不能为空")@ApiModelProperty(name = "openId", value = "qq openId", required = true, dataType = "String")private String openId;@NotBlank(message = "accessToken不能为空")@ApiModelProperty(name = "accessToken", value = "qq accessToken", required = true, dataType = "String")private String accessToken;}
还有一个封装可以获取qq登录用户信息的实体类。
package com.Id.poetry.vo;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;@Data
@AllArgsConstructor
@NoArgsConstructor
public class QQUserInfoVo {private String openId;private String nickname;private String gender;private String province;private String city;private String figureurl;
}
package com.Id.poetry.controller;import cn.hutool.core.date.DateTime;
import com.Id.poetry.config.PoetryResult;
import com.Id.poetry.entity.User;
import com.Id.poetry.service.UserService;
import com.Id.poetry.utils.CommonConst;
import com.Id.poetry.utils.PoetryCache;
import com.Id.poetry.vo.QQLoginVo;
import com.Id.poetry.vo.QQUserInfoVo;
import com.Id.poetry.vo.UserVO;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.util.DigestUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;import java.util.Date;
import java.util.Objects;
import java.util.UUID;@RestController
@Slf4j
public class AuthLoginController {@Autowiredprivate UserService userService;@Value("${qq.appId}")private String appId;@PostMapping("user/login/qq")public PoetryResult<?> authLoginQQ(@RequestBody QQLoginVo qqLoginVo) {System.out.println(qqLoginVo);String userInfoUrl = "https://graph.qq.com/user/get_user_info?access_token=" + qqLoginVo.getAccessToken() +"&oauth_consumer_key=" + appId +"&openid=" + qqLoginVo.getOpenId();
// 创建一个RestTemplate对象,用来请求地址RestTemplate restTemplate = new RestTemplate();// 这个userVo是我要保存到后端数据库所用的实体类(用户基本信息封装)UserVO userVO = new UserVO();LambdaQueryWrapper<User> userLambdaQueryWrapper = new LambdaQueryWrapper<>();
//加密用户openId,openid是qq登录的唯一标识String openIdSercret = DigestUtils.md5DigestAsHex(qqLoginVo.getOpenId().getBytes());User user = new User();userLambdaQueryWrapper.eq(User::getOpenId, openIdSercret);User one = userService.getOne(userLambdaQueryWrapper);Integer userId = null;String userToken = CommonConst.USER_ACCESS_TOKEN + UUID.randomUUID().toString().replaceAll("-", "");
//如果用户已经存在if (one != null) {userId = one.getId();if (!one.getUserStatus()) {return PoetryResult.fail("账号被冻结");}if (one.getUserType() == 0) {
// 说明bossString uuid = UUID.randomUUID().toString().replaceAll("-", "");String adminToken = CommonConst.ADMIN_ACCESS_TOKEN + uuid;PoetryCache.put(adminToken, one, CommonConst.TOKEN_EXPIRE);}
// 直接将数据库保存的用户信息取到封装返回就可以了userVO.setId(one.getId());userVO.setAvatar(one.getAvatar());userVO.setUsername(one.getUsername());userVO.setGender(one.getGender());userVO.setUserType(one.getUserType());if(one.getUserType()==0){userVO.setIsBoss(true);}else if (one.getUserType()==1){userVO.setIsAdmin(true);}if(StringUtils.hasText(one.getIntroduction())){userVO.setIntroduction(one.getIntroduction());}PoetryCache.put(userToken, one, CommonConst.TOKEN_EXPIRE);} else {
// 没有存在过的话就去获取用户信息(restTemplate获取用户信息)ResponseEntity<String> userInfo = restTemplate.getForEntity(userInfoUrl, String.class);String body = userInfo.getBody();
// 这里这个QQUserInfo可封装我们从qq授权服务器获取用户信息的字段QQUserInfoVo qqUserInfoVo = JSON.parseObject(body, QQUserInfoVo.class);user.setUsername(qqUserInfoVo.getNickname());user.setOpenId(openIdSercret);user.setAvatar(qqUserInfoVo.getFigureurl());user.setCreateTime(new DateTime().toLocalDateTime());String gender = qqUserInfoVo.getGender();if (gender.equals("男")) {
// 我这样简单设置了,你可以去做个常量。user.setGender(1);} else {user.setGender(0);}
// 将信息保存到数据库userService.save(user);userId = user.getId();userVO.setAvatar(qqUserInfoVo.getFigureurl());userVO.setUsername(qqUserInfoVo.getNickname());if (qqUserInfoVo.getGender().equals("男")) {userVO.setGender(1);} else {userVO.setGender(0);}PoetryCache.put(userToken, user, CommonConst.TOKEN_EXPIRE);
//后面是我自己网站的一些逻辑操作。}PoetryCache.put(userToken, one, CommonConst.TOKEN_EXPIRE);PoetryCache.put(CommonConst.USER_TOKEN + user.getId(), userToken, CommonConst.TOKEN_EXPIRE);userVO.setId(userId);userVO.setAccessToken(userToken);// 需要给到前台数据,就是将用户的数据返回一些,比如头像这些,还有登录成功后的token
// 所以return PoetryResult.success(userVO);}}
这些里面是有一些我自己的逻辑代码的。自己可以根据需要参考。
没有很认真的封装。但是功能都可以。自己可以去优化实现。
这个逻辑是可以改的。比如说每次qq用户登录,你去获取他的数据保存也是可以的。我这里只获取了一次就永久保存。但是没有什么影响,因为openid是唯一的。
调试互联可能也是一个重要的点
调试互联
就是说你的网站必须是https形式的填写在互联应用上了,你如何本地调试呢?
就是说回调地址是https://www…这种形式,你如何把它弄到本地调试呢?
当然是通过去修改本地dns了。
C:\Windows\System32\drivers\etc 这里路径下面的host文件。加入这样类似的一段。让我们在线上网站域名映射到我们的本地ip地址。
还有一点,就是vue配置这里涉及内网穿透的这么一个知识点配置。
disableHostCheck: true //本地调试域名内网穿透
devServer.disableHostCheck 配置项用于配置是否关闭用于 DNS 重绑定的 HTTP 请求的 HOST 检查。 DevServer 默认只接受来自本地的请求,关闭后可以接受来自任何 HOST 的请求。 它通常用于搭配 --host 0.0.0.1 使用,因为你想要其它设备访问你本地的服务,但访问时是直接通过 IP 地址访问而不是 HOST 访问,所以需要关闭 HOST 检查。反正你需要配置这个参数,否则将来回调的时候是回调不到的。
之前自己调试做的笔记。
给它保存了。
启动vue和后端。
比如我这个vue项目要现在所占的端口是81,我就这样请求。是没有问题的。
然后呢,请求qq登录。
给www.daodaozi.xyz后面加个80。并且要把https改为http这样才可以。给它改了。灵活的调试、
成功!
线上网站