1.web端向服务器发送请求获取二维码,服务器同时返回webSocket id
参考:JAVA获取小程码_qq_43019699的博客-CSDN博客
注意:本次返回返回的二维码,同时将webSocke ID一同给前端。
/**
* 获取二维码
* @return 二维码字符串
*/
public Map getQRCode() {
Map map = new HashMap();
String webSocketId = String.valueOf(System.currentTimeMillis());
String accessToken = getCustomerAccessToken();
String postUrl ="https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token="+accessToken;
JSONObject jsonObject = new JSONObject();
jsonObject.put("scene", webSocketId); //将webSocketId 作为参数传给扫码跳转后页面
jsonObject.put("page", "pages/weChat_login/weChat_login"); //扫码后跳转页面
jsonObject.put("check_path", false); //是否检查跳转页面存不存在
jsonObject.put("env_version", "develop"); //版本
byte[] string = null;
try {
string = sendPostJsonImge(postUrl, jsonObject.toJSONString());
} catch (IOException e) {
logger.info(String.valueOf("获取二维码时出现异常"));
}
String base64Str = Base64.getEncoder().encodeToString(string);
map.put("base64Str",base64Str);
map.put("webSocketId",webSocketId);
return map;
}
2.前端接收到WebSocket ID时,开启WebSocket
web端代码
// 获取二维码
getSignQRCode() {
ApiService.get(
guaranteeLoginApi.getSignQRCode.url,
{},
ApiService.createBasicHeaders()
).then(response => {
console.log(response);
if (response.code === ResponseType.SUCCESS.code) {
// const buffer = Buffer.from(response.data.base64Str, "base64");
this.dataBuffer = "data:image/png;base64," + response.data.base64Str;
this.webSocketId = response.data.webSocketId;
this.initWebSocket();
} else {
this.$message.error(response.msg);
}
});
},
initWebSocket() {
const wsurl = process.env.VUE_APP_WEBSOCKET_URL + this.webSocketId;
this.websock = new WebSocket(wsurl);
console.log(this.websock);
this.websock.onopen = this.websocketonopen;
this.websock.onerror = this.websocketonerror;
this.websock.onmessage = this.websocketonmessage;
this.websock.onclose = this.websocketclose;
},
websocketonopen() {
console.log("推送服务连接成功");
// this.websocketsend('连接进行中', 'INITIAL');
},
websocketonerror() {
// 错误
// info(this, '推送服务连接失败');
console.log("推送服务连接失败");
},
websocketonmessage(evt) {
// 数据接收
console.log(evt);
const msg = eval("(" + evt.data + ")");
console.log(msg);
const params = {
username: msg.unionid,
password: msg.unionid,
loginType: "web",
authType: "4"
};
this.Login(params);
},
3.手机端扫码跳转页面,并且获取到WebSocket ID
微信小程序页面代码
<template>
<!-- <view class="content" :style="{backgroundImage: `url(${baseImgUrl}/static/img/login_b.png)`}"> -->
<!-- 标头 -->
<view class="content_header">
<view class="avatar">
<image style="width: 84rpx; height: 84rpx;" src="../../static/img/u192.png"></image>
</view>
<text style="font-size: 45rpx; color: #FFFFFF;margin-left: 20rpx;">xx服务平台</text>
</view>
<view class="wx_w">
<view class="wx_w_v">
<text class="szc-fw-5 szc-fs-30">申请获取以下权限</text>
</view>
<view class="wx_w_v">
<text class="szc-fc-999 szc-fs-28">获取你的公开信息(昵称,头像等)</text>
</view>
</view>
<!-- 微信登陆 -->
<view class="wx_all">
<button class="wx_v" @click.stop="$noMultipleClicks(wxLogin)" plain>
<text class="szc-fs-30 szc-fc-white szc-fw-5">授权登陆</text>
</button>
</view>
<!-- 弹出确认 -->
<uni-popup ref="popup" type="success" :animation="false" :isMaskClick="false">
<view class="popup_c">
<view class="popup_c_t">
<view class="t-icon-a-16x16_wancheng" style="width: 160rpx;height: 160rpx;"></view>
<text class="szc-fs-36" style="margin-top: 20px;">授权成功</text>
</view>
<view class="popup_btn">
<navigator open-type="exit" target="miniProgram" style="width: 550rpx;" class="popup_btn2 flex-center">确认</navigator>
</view>
</view>
</uni-popup>
</view>
</template>
<script>
import { setAccessToken } from '../../service/auth.js'
import ValidateUtil from '@/public/js/ValidateUtil.js'
export default {
data() {
return {
noClick: true,
baseImgUrl: this.BASE_IMG_URL,
code: '',
scene: ''
};
},
onLoad (query) {
// scene 需要使用 decodeURIComponent 才能获取到生成二维码时传入的 scene,webS
this.scene = decodeURIComponent(query.scene);
this.wxAuthorize();
},
methods: {
// 获取用户信息
wxAuthorize() {
var _this = this
uni.login({
provider: 'weixin',
onlyAuthorize: true,
success(res) {
if (res.code) {
_this.code = res.code;
}
}
})
},
// 微信登陆
wxLogin() {
uni.showLoading({
title: "授权登录中",
mask: true
});
this.WechatLogin();
},
WechatLogin() {
var _this = this
uni.getUserProfile({
desc: '微信登录获取用户数据',
success: async (obj) => {
uni.setStorageSync('userInfo', JSON.stringify(obj.userInfo))
// 调用 action ,请求登录接口
const url = '/weChatApplet/getWxUserInfo';
const query = {
requestCode: _this.code,
webSocketId: _this.scene
}
_this.$ApiService.get(url, query, {}).then(res => {
if (res.data.code == '0000') {
uni.hideLoading();
this.open();
} else {
uni.hideLoading();
uni.showToast({
icon: 'none',
title: res.data.msg
});
}
}).catch(err => {
uni.hideLoading();
uni.showToast({
icon: 'none',
title: err
});
});
},
fail: (err) => {
uni.showToast({
title: '登录已取消',
icon: 'error',
mask: true,
});
},
complete: () => {}
});
},
open() {
this.$refs.popup.open('success');
}
}
}
</script>
<style lang="scss" scoped>
.content {
background-size: 100% 450rpx;
background-repeat: no-repeat;
}
.avatar {
background-color: #FFFFFF;
border-radius: 10rpx;
width: 96rpx;
height: 96rpx;
display: flex;
align-items: center;
justify-content: center;
}
.content_header {
width: 100%;
height: 38vh;
display: flex;
justify-content: center;
align-items: center;
}
.wx_w {
width: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.wx_w_v {
width: 85%;
height: 5vh;
}
.wx_all {
width: 100%;
height: 12vh;
display: flex;
justify-content: center;
align-items: center;
}
.wx_v {
display: flex;
align-items: center;
justify-content: center;
height: 80rpx;
width: 85%;
background-color: #339800;
border-width: 0;
border-radius: 50rpx;
}
</style>
4.点击【授权登录】之后调用后台,获取用户信息,再通过webSocket将用户信息发送到web端
/**
* 微信扫码获取用户信息
* @param code
* @param id
* @return
*/
@Override
public ResponseEntity getWxUserInfo(String code, String id) {
WxAuthorizeResponse wxWebAuthorizeResponse = null;
BeanCopyUtils beanCopyUtils = SpringUtil.getBean(BeanCopyUtils.class);
try {
//通过 code,appid,appSecret获取 openid 和 unionid
wxWebAuthorizeResponse = callApiForDecrypt(code,appid,appSecret);
}catch (Exception e){
log.error("微信登录凭证校验异常!appId:{},code:{}", clientAppid, code, e);
SysExceptionUtil.insert(e);
throw new SystemServiceException(SystemStatus.CHECK_ERROR_EXCEPTION).reason("存在问题");
}
// 根据openid获取数据库中的用户信息
CustomerUsers customerUsers = customerUsersMapper.findByApOpenId(wxWebAuthorizeResponse.getOpenid());
if(customerUsers == null){
throw new SystemServiceException(SystemStatusCode.PARAM_ERROR_EXCEPTION).reason("您尚未绑定微信账号。");
}
CustomerUsersRespVo customerUsersRespVo = beanCopyUtils.convertTo(customerUsers, CustomerUsersRespVo.class);
//向webSocket发送用户信息
try {
WebSocketServer webSocketServer = SpringUtil.getBean("webSocketServer", WebSocketServer.class);
webSocketServer.sendMessage(id, customerUsersRespVo);
} catch (Exception e) {
log.error("WebSocket消息提醒异常,消息内容为:{}", customerUsersRespVo, e);
return ResponseEntity.Failure(BaseStatus.SERVICE_FAILURE, "未拿到webSocket中的uuid");
}
return ResponseEntity.Success(null).OK();
}
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
/**
* @description: WebSocketServer
* @author:
* @create:
**/
@Slf4j
//@ConditionalOnClass(value = WebSocketConfig.class)
@ServerEndpoint("/ws/{id}")
@Component
public class WebSocketServer {
/**
* 客户端ID
*/
private String id = "";
/**
* 与某个客户端的连接会话,需要通过它来给客户端发送数据
*/
private Session session;
/**
* 用来存储当前在线的客户端(此map线程安全)
*/
private static ConcurrentHashMap<String, WebSocketServer> webSocketMap = new ConcurrentHashMap<>();
/**
* 连接建立成功后调用
*/
@OnOpen
public void onOpen(@PathParam(value = "id") String id, Session session) {
this.session = session;
// 接收到发送消息的客户端编号
this.id = id;
// 加入map中
webSocketMap.put(id, this);
log.info("用户连接:{}", this.id);
}
/**
* 连接关闭时调用
*/
@OnClose
public void onClose() {
// 从map中删除
webSocketMap.remove(this);
log.info("用户退出:{}", this.id);
}
/**
* 收到客户端消息后调用
*
* @param message 客户端发送过来的消息<br/>
* 消息格式:内容 - 表示群发,内容|X - 表示发给id为X的客户端
* @param session 用户信息
*/
@OnMessage
public void onMessage(String message, Session session) {
log.info("用户消息:{},报文:{}", this.id, message);
String[] messages = message.split("[|]");
try {
if (messages.length > 1) {
sendToUser(messages[0], messages[1]);
} else {
sendToAll(messages[0]);
}
} catch (IOException e) {
log.error(e.getMessage(), e);
}
}
/**
* 发生错误时回调
*
* @param session 用户信息
* @param error 错误
*/
@OnError
public void onError(Session session, Throwable error) {
log.error("用户错误:{},原因:{}", this.id, error.getMessage());
error.printStackTrace();
}
/**
* 推送信息给指定ID客户端
*
* @param message 客户端发来的消息
* @param id 客户端ID
*/
public void sendToUser(String message, String id) throws IOException {
if (StringUtils.isBlank(message)) {
log.error("发送报文为空!id:{}", id);
return;
}
log.info("发送消息到:{},报文:{}", id, message);
if (StringUtils.isNotBlank(id) && webSocketMap.containsKey(id)) {
webSocketMap.get(id).sendMessage(message);
} else {
log.error("用户{}不在线!", id);
}
}
/**
* 群送发送信息给所有人
*
* @param message 要发送的消息
*/
public void sendToAll(String message) throws IOException {
if (StringUtils.isBlank(message)) {
log.error("群发报文为空!");
return;
}
log.info("群发消息到所有客户端,消息:{}", message);
for (String key : webSocketMap.keySet()) {
webSocketMap.get(key).sendMessage(message);
}
}
/**
* 发送消息
*
* @param message 要发送的消息
*/
private void sendMessage(String message) throws IOException {
this.session.getBasicRemote().sendText(message);
}
/**
* 发送消息 消息内容为json格式
*
* @param id
* @param msgObj
*/
public void sendMessage(String id, Object msgObj) {
String message = null;
if (msgObj instanceof String) {
message = String.valueOf(msgObj);
} else {
message = JSON.toJSONString(msgObj);
}
try {
this.sendToUser(message, id);
} catch (IOException e) {
log.error("websocket发送消息失败!id:{},message:{}", id, message, e);
}
}
}