微信扫码登录网站
代码篇
maven
<!--XML解析-->
<dependency>
<groupId>org.dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>2.1.1</version>
</dependency>
<!--websocket-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
<version>2.3.3.RELEASE</version>
</dependency>
application.yml
#微信配置
wechat:
appId: wx5**********756
appSecret: 21ca23************70403
token: 1******a
sceneId: 1****
accessTokenUrl: https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s
ticketUrl: https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=%s
qrCodeUrl: https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=%s
shortUrl: https://api.weixin.qq.com/cgi-bin/shorturl?access_token=%s
userInfoUrl: https://api.weixin.qq.com/cgi-bin/user/info?access_token=%s&openid=%s&lang=zh_CN
WeChatProperties.java
package com.qcn.common.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* 读取微信配置文件
*/
@Component
@ConfigurationProperties("wechat")
public class WeChatProperties {
/**
* 开发者id
*/
private String appId;
/**
* 开发者密码
*/
private String appSecret;
/**
* 微信公众号校验自定义设置的token
*/
private String token;
/**
* 临时二维码场景值id,32位非0整型
*/
private Integer sceneId;
/**
* 获取accessToken的url
*/
private String accessTokenUrl;
/**
* 获取ticket的url
*/
private String ticketUrl;
/**
* 获取二维码url
*/
private String qrCodeUrl;
/**
* 长链接转短连接url
*/
private String shortUrl;
/**
* 获取用户信息
*/
private String userInfoUrl;
public String getAppId() {
return appId;
}
public void setAppId(String appId) {
this.appId = appId;
}
public String getAppSecret() {
return appSecret;
}
public void setAppSecret(String appSecret) {
this.appSecret = appSecret;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public String getAccessTokenUrl() {
return accessTokenUrl;
}
public void setAccessTokenUrl(String accessTokenUrl) {
this.accessTokenUrl = accessTokenUrl;
}
public String getTicketUrl() {
return ticketUrl;
}
public void setTicketUrl(String ticketUrl) {
this.ticketUrl = ticketUrl;
}
public String getQrCodeUrl() {
return qrCodeUrl;
}
public void setQrCodeUrl(String qrCodeUrl) {
this.qrCodeUrl = qrCodeUrl;
}
public String getShortUrl() {
return shortUrl;
}
public void setShortUrl(String shortUrl) {
this.shortUrl = shortUrl;
}
public String getUserInfoUrl() {
return userInfoUrl;
}
public void setUserInfoUrl(String userInfoUrl) {
this.userInfoUrl = userInfoUrl;
}
public Integer getSceneId() {
return sceneId;
}
public void setSceneId(Integer sceneId) {
this.sceneId = sceneId;
}
}
SHA1.java
package com.qcn.framework.api.wechat;
import java.security.MessageDigest;
/**
* 加密
*/
public class SHA1 {
private static final char[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5',
'6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
private static String getFormattedText(byte[] bytes) {
int len = bytes.length;
StringBuilder buf = new StringBuilder(len * 2);
for (int j = 0; j < len; j++) {
buf.append(HEX_DIGITS[(bytes[j] >> 4) & 0x0f]);
buf.append(HEX_DIGITS[bytes[j] & 0x0f]);
}
return buf.toString();
}
public static String encode(String str) {
if (str == null) {
return null;
}
try {
MessageDigest messageDigest = MessageDigest.getInstance("SHA1");
messageDigest.update(str.getBytes());
return getFormattedText(messageDigest.digest());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
WeChatUtils.java
package com.qcn.framework.api.wechat;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.net.ConnectException;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.net.URLConnection;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class WeChatUtils {
private static final Logger log = LoggerFactory.getLogger(WeChatUtils.class);
/**
* 向指定 URL 发送GET方法的请求
* @param url 发送请求的完整URL,应该是 http://127.0.0.1?name1=value1&name2=value2 的形式
* @return
*/
public static String sendGet(String url)
{
StringBuilder result = new StringBuilder();
BufferedReader in = null;
try
{
log.info("开始get请求: {}", url);
URL realUrl = new URL(url);
URLConnection connection = realUrl.openConnection();
connection.setRequestProperty("accept", "*/*");
connection.setRequestProperty("connection", "Keep-Alive");
connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
connection.connect();
in = new BufferedReader(new InputStreamReader(connection.getInputStream(), "UTF-8"));
String line;
while ((line = in.readLine()) != null)
{
result.append(line);
}
log.info("get请求成功 : {}", result);
}
catch (ConnectException e)
{
log.error("调用WeChatUtils.sendGet ConnectException, url=" + url, e);
}
catch (SocketTimeoutException e)
{
log.error("调用WeChatUtils.sendGet SocketTimeoutException, url=" + url, e);
}
catch (IOException e)
{
log.error("调用WeChatUtils.sendGet IOException, url=" + url, e);
}
catch (Exception e)
{
log.error("调用WeChatUtils.sendGet Exception, url=" + url, e);
}
finally
{
try
{
if (in != null)
{
in.close();
}
}
catch (Exception ex)
{
log.error("调用in.close Exception, url=" + url, ex);
}
}
return result.toString();
}
/**
* 向指定 URL 发送POST方法的请求
*
* @param url 发送请求的 URL
* @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
* @return 所代表远程资源的响应结果
*/
public static String sendPost(String url, String param)
{
PrintWriter out = null;
BufferedReader in = null;
StringBuilder result = new StringBuilder();
try
{
String urlNameString = url;
log.info("开始post请求: {}", urlNameString);
URL realUrl = new URL(urlNameString);
URLConnection conn = realUrl.openConnection();
conn.setRequestProperty("accept", "*/*");
conn.setRequestProperty("connection", "Keep-Alive");
conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
conn.setRequestProperty("Accept-Charset", "utf-8");
conn.setRequestProperty("contentType", "utf-8");
conn.setDoOutput(true);
conn.setDoInput(true);
out = new PrintWriter(conn.getOutputStream());
out.print(param);
out.flush();
in = new BufferedReader(new InputStreamReader(conn.getInputStream(), "utf-8"));
String line;
while ((line = in.readLine()) != null)
{
result.append(line);
}
log.info("post请求成功 : {}", result);
}
catch (ConnectException e)
{
log.error("调用WeChatUtils.sendPost ConnectException, url=" + url + ",param=" + param, e);
}
catch (SocketTimeoutException e)
{
log.error("调用WeChatUtils.sendPost SocketTimeoutException, url=" + url + ",param=" + param, e);
}
catch (IOException e)
{
log.error("调用WeChatUtils.sendPost IOException, url=" + url + ",param=" + param, e);
}
catch (Exception e)
{
log.error("调用WeChatUtils.sendPost Exception, url=" + url + ",param=" + param, e);
}
finally
{
try
{
if (out != null)
{
out.close();
}
if (in != null)
{
in.close();
}
}
catch (IOException ex)
{
log.error("调用in.close Exception, url=" + url + ",param=" + param, ex);
}
}
return result.toString();
}
/**
* xml解析
* @param request
* @return
* @throws Exception
*/
public static Map<String, String> parseXml(HttpServletRequest request) throws Exception {
Map<String, String> map = new HashMap<>();
InputStream inputStream = request.getInputStream();
SAXReader reader = new SAXReader();
Document document = reader.read(inputStream);
Element root = document.getRootElement();
List<Element> elementList = root.elements();
for (Element e : elementList)
map.put(e.getName(), e.getText());
inputStream.close();
return map;
}
/**
* 校验签名
* @param signature
* @param timestamp
* @param nonce
* @param token
* @return
*/
public static boolean checkSignature(String signature, String timestamp, String nonce, String token) {
String[] str = new String[]{token, timestamp, nonce};
//排序
Arrays.sort(str);
//拼接字符串
StringBuffer buffer = new StringBuffer();
for (int i = 0; i < str.length; i++) {
buffer.append(str[i]);
}
//进行sha1加密
String temp = SHA1.encode(buffer.toString());
//与微信提供的signature进行匹对
return signature.equals(temp);
}
}
WebSocketConfig.java
package com.qcn.framework.api.websocket;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
/**
* 配置websocket
*/
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
WebSocketService.java
package com.qcn.framework.api.websocket;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
/**
* webSocket服务
*/
@Component
@ServerEndpoint(value = "/webSocket/{socketKey}")
public class WebSocketService {
private static Logger log = LoggerFactory.getLogger(WebSocketService.class);
private static int onlineCount = 0;
private static Map<String, WebSocketService> clients = new ConcurrentHashMap<>();
private Session session;
private String socketKey;
@OnOpen
public void onOpen(@PathParam("socketKey") String socketKey, Session session) {
this.socketKey = socketKey;
this.session = session;
WebSocketService.onlineCount++;
clients.put(socketKey, this);
log.info("【websocket有新的连接】, 总数:{}", onlineCount);
}
@OnClose
public void onClose() {
clients.remove(socketKey);
WebSocketService.onlineCount--;
log.info("【websocket连接断开】总数:{}", onlineCount);
}
@OnMessage
public void onMessage(String message) {
log.info("【websocket收到来自 {} 消息】: {}",socketKey, message);
}
@OnError
public void onError(Session session, Throwable throwable) {
log.error("【websocket发生错误】: {}",throwable.getMessage());
}
public void sendMessage(String message,String socketKey) {
String prefix=socketKey.split("_")[0];
if("admin".equals(prefix)){
//向admin端所有客户端推送消息
for (WebSocketService item : clients.values()) {
if(Objects.equals(item.socketKey.split("_")[0],"admin")){
item.session.getAsyncRemote().sendText(message);
}
}
}else{
//只向pc端扫码登录的客户端推送消息
for (WebSocketService item : clients.values()) {
if(Objects.equals(item.socketKey,socketKey)){
item.session.getAsyncRemote().sendText(message);
}
}
}
}
}
WeChatService.java
package com.qcn.framework.api.wechat;
import com.alibaba.fastjson.JSONObject;
import com.qcn.common.config.WeChatProperties;
import com.qcn.common.constant.CardConstants;
import com.qcn.common.constant.Constants;
import com.qcn.common.core.redis.RedisCache;
import com.qcn.common.utils.StringUtils;
import com.qcn.common.utils.sign.Md5Utils;
import com.qcn.framework.api.websocket.WebSocketService;
import com.qcn.framework.web.service.CardLoginService;
import com.qcn.system.domain.card.CardUser;
import com.qcn.system.service.api.ICardUserInfoService;
import com.qcn.system.service.api.ICardUserService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
/**
* 微信服务处理
*/
@Component
public class WeChatService {
private static final Logger log = LoggerFactory.getLogger(WeChatService.class);
@Autowired
private RedisCache redisCache;
@Autowired
private WeChatProperties weChatProperties;
@Autowired
private WebSocketService webSocketService;
@Autowired
private ICardUserService userService;
@Autowired
private ICardUserInfoService iCardUserInfoService;
@Autowired
private CardLoginService cardLoginService;
/**
* 获取AccessToken,写入redis,比官方失效时间expires_in提前10分钟
*
* @return
*/
public String getAccessToken() {
String accessToken = redisCache.getCacheObject(CardConstants.ACCESS_TOKEN_KEY);
if (StringUtils.isBlank(accessToken)) {
String url = String.format(weChatProperties.getAccessTokenUrl(), weChatProperties.getAppId(), weChatProperties.getAppSecret());
String result = WeChatUtils.sendGet(url);
JSONObject jsonObject = JSONObject.parseObject(result);
accessToken = jsonObject.getString("access_token");
Integer expiresIn = jsonObject.getInteger("expires_in");
redisCache.setCacheObject(CardConstants.ACCESS_TOKEN_KEY, accessToken, expiresIn - 600, TimeUnit.SECONDS);
}
return accessToken;
}
/**
* 获取二维码
*
* @return 二维码url,二维码失效时间,webSocket的socketKey
*/
public Map<String, Object> getQrCodeUrl() {
Map<String, Object> map = new HashMap<>();
String qrCodeUrl = "";
String encodeTicket = "";
//----获取的二维码ticket----
String accessToken = getAccessToken();
String url = String.format(weChatProperties.getTicketUrl(), accessToken);
//{"expire_seconds": 604800, "action_name": "QR_SCENE", "action_info": {"scene": {"scene_id": 123}}}
JSONObject params = new JSONObject();
params.put("expire_seconds", 300);//二维码有效时间(秒),最大不能超过30天
params.put("action_name", "QR_SCENE");//二维码类型,QR_SCENE为临时的整型参数值
JSONObject action_info = new JSONObject();//二维码详细信息
JSONObject scene = new JSONObject();
scene.put("scene_id", weChatProperties.getSceneId());//场景值ID
action_info.put("scene", scene);
params.put("action_info", action_info);
String result = WeChatUtils.sendPost(url, params.toJSONString());
JSONObject jsonObject = JSONObject.parseObject(result);
String ticket = jsonObject.getString("ticket");
//---凭借此ticket可以在有效时间内换取二维码, https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=TICKET,TICKET记得进行UrlEncode
try {
encodeTicket = URLEncoder.encode(ticket, "UTF-8");
String longUrl = String.format(weChatProperties.getQrCodeUrl(), encodeTicket);
qrCodeUrl = getShortUrl(accessToken, longUrl);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
map.put("qrCodeUrl", qrCodeUrl);
map.put("socketKey", Md5Utils.hash(encodeTicket));
map.put("expireSeconds", jsonObject.getInteger("expire_seconds"));//该二维码有效时间,以秒为单位
return map;
}
/**
* 长链接转短连接,提升扫码速度
*
* @param accessToken
* @param longUrl
* @return
*/
private String getShortUrl(String accessToken, String longUrl) {
if (StringUtils.isNotBlank(accessToken) && StringUtils.isNotBlank(longUrl)) {
String url = String.format(weChatProperties.getShortUrl(), accessToken);
JSONObject params = new JSONObject();
params.put("action", "long2short");//固定值
params.put("long_url", longUrl);
String result = WeChatUtils.sendPost(url, params.toJSONString());
JSONObject jsonObject = JSONObject.parseObject(result);
if (jsonObject.getInteger("errcode") == 0) {
return jsonObject.getString("short_url");
}
}
return longUrl;
}
/**
* 扫码登录处理
*
* @param openId
*/
public void login(String openId, String socketKey) {
if (StringUtils.isNotBlank(openId)) {
String url = String.format(weChatProperties.getUserInfoUrl(), getAccessToken(), openId);
String result = WeChatUtils.sendGet(url);
JSONObject jsonObject = JSONObject.parseObject(result);
log.info("【获取微信用户信息】 {}", jsonObject);
if (jsonObject.getInteger("subscribe").compareTo(1) == 0) {
String subscribe_scene = jsonObject.getString("subscribe_scene");
//关注的渠道来源,ADD_SCENE_SEARCH 公众号搜索(微信扫码登录用这个)
if ("ADD_SCENE_QR_CODE".equals(subscribe_scene) || "ADD_SCENE_SEARCH".equals(subscribe_scene)) {
Map<String, Object> map = new HashMap<>();
String weChatNickname = jsonObject.getString("nickname");
CardUser user = userService.loginByOpenId(openId);
String msg;
String code;
if (Objects.isNull(user)) {
log.info("【微信扫码登录】系统新用户");
code = "204";
msg = "授权成功,请绑定用户信息,以便下次直接扫码登录";
} else {
if ("0".equals(user.getStatus())) {
log.info("【微信扫码登录成功】系统老用户");
code = "200";
msg = "登录成功";
map.put(Constants.TOKEN, cardLoginService.login(user.getMobile(), "CUSTOM_LOGIN_OPENID"));
map.put("data", iCardUserInfoService.findAllInfo(user.getId()));
if (!weChatNickname.equals(user.getNickName())) {
//更新微信昵称
iCardUserInfoService.updateWeChatInfo(user.getId(), null, weChatNickname);
}
} else {
log.info("【微信扫码登录失败】系统老用户账号异常");
code = "201";
msg = "登录失败,请阅读【用户协议】24小时后提供手机号联系客服";
}
}
map.put("code", code);
map.put("msg", msg);
map.put("openId", openId);
map.put("weChatNickname", weChatNickname);
webSocketService.sendMessage(JSONObject.toJSONString(map), socketKey);
}
}
}
}
}
ApiWeChatController.java
package com.qcn.web.controller.api;
import com.qcn.common.config.WeChatProperties;
import com.qcn.common.core.domain.AjaxResult;
import com.qcn.common.utils.sign.Md5Utils;
import com.qcn.framework.api.wechat.WeChatService;
import com.qcn.framework.api.wechat.WeChatUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.util.Map;
/**
* 微信相关接口
*/
@RestController
@RequestMapping("/weChat")
public class ApiWeChatController {
private static final Logger log = LoggerFactory.getLogger(ApiWeChatController.class);
@Autowired
private WeChatProperties weChatProperties;
@Autowired
private WeChatService weChatService;
/**
* token校验
*
* @param request
* @param response
* @throws UnsupportedEncodingException
*/
@GetMapping("/msgCallback")
public void login(HttpServletRequest request, HttpServletResponse response) throws UnsupportedEncodingException {
log.debug("开始校验token……");
request.setCharacterEncoding("UTF-8");
String signature = request.getParameter("signature");//微信加密签名,signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。
String timestamp = request.getParameter("timestamp");//时间戳
String nonce = request.getParameter("nonce");//随机数
String echoStr = request.getParameter("echostr");//随机字符串
PrintWriter out = null;
try {
out = response.getWriter();
if (WeChatUtils.checkSignature(signature, timestamp, nonce, weChatProperties.getToken())) {
out.write(echoStr);//校验成功原数据返回
}
} catch (IOException e) {
e.printStackTrace();
} finally {
out.close();
}
}
/**
* 消息回调
*
* @param request
* @throws Exception
*/
@PostMapping("/msgCallback")
public void scanCodeCallback(HttpServletRequest request) throws Exception {
request.setCharacterEncoding("UTF-8");
Map<String, String> map = WeChatUtils.parseXml(request);
String openId = map.get("FromUserName");//发送方帐号(一个OpenID)
String event = map.get("Event");//事件类型,未关注:subscribe;已关注:SCAN
String eventKey = map.get("EventKey");//未关注:事件KEY值,qrscene_为前缀,后面为二维码的参数值;已关注:事件KEY值,是一个32位无符号整数,即创建二维码时的二维码scene_id
String ticket = map.get("Ticket");//二维码的ticket,可用来换取二维码图片
//不管新关注还是已关注进入,都处理微信授权登录
log.info("【扫码成功】 ticket:{}",ticket);
if ("subscribe".equals(event)) {
eventKey=eventKey.split("_")[1];
}
if(weChatProperties.getSceneId().compareTo(Integer.valueOf(eventKey))==0){
log.info("【微信消息】开始处理微信授权登录……");
weChatService.login(openId, Md5Utils.hash(ticket));
}
}
/**
* 获取二维码
* @return
*/
@GetMapping("/getQrCodeUrl")
public AjaxResult getQrCodeUrl() {
log.debug("开始获取二维码url……");
Map<String,Object> map = weChatService.getQrCodeUrl();
log.debug("获取二维码结果:" + map);
return AjaxResult.success(map);
}
}
案例-本地&生产环境部署测试
这里使用了内网穿透做本地测试
1.把内网穿透域名地址配置在微信公众号【开发】-【基本设置】,其它配置与yml文件配置一直即可
2.把HTML页面的ip地址也改为这个内网穿透域名
1.新建一个静态页面
<!DOCTYPE html>
<html xmlns:v-if="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<!-- import CSS -->
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
<style>
.body{
background-image: url("backpic.jpg");
background-repeat: no-repeat;
background-size: cover;
}
.qrcodeimg {
width: 200px;
height: 200px;
}
.maindiv {
width: 400px;
height: 400px;
margin-top: 200px;
float: right;
}
.infoinput {
width: 200px;
display: block;
}
.infoBox {
width: 220px;
height: 220px;
max-height: 220px;
}
</style>
</head>
<body class="body">
<div id="app" class="maindiv">
<div v-if="qrcodeLogin" class="infoBox">
<div class="block" v-if="qrcodeLogin">
<el-image :src="src" class="qrcodeimg">
<div slot="placeholder" class="image-slot">
加载中<span class="dot">...</span>
</div>
</el-image>
</div>
</div>
</div>
</body>
<!-- import Vue before Element -->
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<!-- import JavaScript -->
<script src="https://unpkg.com/element-ui/lib/index.js"></script>
<script src="https://cdn.bootcss.com/vue-resource/1.5.1/vue-resource.js"></script>
<script>
new Vue({
el: '#app',
data: function () {
return {
qrcodeLogin: true,
src: "",
ipaddr: "25*******e.cc",
enableSSL: ""
}
},
mounted: function () {
console.log("页面加载完成!");
this.getQrcode();
},
methods: {
getQrcode(){//获取带参二维码,并设置到页面
console.log("设置二维码");
//请求二维码
const that = this;
this.$http.get("http" + that.enableSSL + "://" + that.ipaddr + "/weChat/getQrCodeUrl").then(function(data){
console.log("function1:返回结果");
console.log(data.body.data);
//二维码图片地址
that.src = data.body.data.qrCodeUrl;
//ticket
that.websocketLink(data.body.data.socketKey);
},function(res){
console.log("function2");
console.log(res);
});
},
websocketLink(userId) {
var that = this;
if ("WebSocket" in window) {
console.log("您的浏览器支持 WebSocket!");
// 打开一个 web socket
var ws = new WebSocket("ws" + that.enableSSL + "://" + that.ipaddr + "/webSocket/" + userId);
ws.onopen = function () {
// Web Socket 已连接上,使用 send() 方法发送数据
ws.send("pc端请求扫码登录,socketKey:" + userId);
console.log("数据发送中...");
};
ws.onmessage = function (evt) {
console.log(evt.data);
var received_msg = JSON.parse(evt.data);
if (received_msg.code == 200) {
alert(received_msg.message);
console.log(evt.data);
} else if (received_msg.code == 201) {
console.log(received_msg.message);
} else {
console.log("socket通道校验");
}
};
ws.onclose = function () {
// 关闭 websocket
console.log("连接已关闭...");
};
} else {
// 浏览器不支持 WebSocket
console("您的浏览器不支持 WebSocket!");
}
}
}
})
</script>
</html>
2.修改yml配置文件
spring:
resources:
static-locations: classpath:static/
3.本地项目端口改为80,运行项目
4.访问本地,扫码测试
线上部署
nginx
location /prod-api/{
proxy_pass http://localhost:7002/;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
如果是域名是https请求,就一定要将login_vue.html的WebSocket连接改为wss:
部署后访问测试即可
详细讲解篇
官方文档
先吐槽一下,做过微信对接的人,看微信文档应都是口吐芬芳吧…………………………
获取access_token
https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Get_access_token.html
生成带参数的二维码
https://developers.weixin.qq.com/doc/offiaccount/Account_Management/Generating_a_Parametric_QR_Code.html
长链接转短连接
https://developers.weixin.qq.com/doc/offiaccount/Account_Management/URL_Shortener.html
接收事件推送-扫描带参数二维码事件
https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Receiving_event_pushes.html
微信公众平台接口调试工具
http://mp.weixin.qq.com/debug/
微信公众号配置
微信扫码登录有两种方式:
1.微信扫一扫直接登录(这里是通过微信开放平台实现,请看另一篇博文)
2.微信扫一扫关注公众号登录(这里通过微信公众号实现,就是这篇博文的实现方式)
正确配置最终图:
一步一步操作:
服务器配置:
- 启用
- 修改配置
- URL:项目的部署地址+controller的token校验方法路由,也就是token校验的完整访问路径
- Token:自定义字符,例如mytoken,一定要与项目yml配置保持一致即可
- EncodingAESKey:用默认的随机生成即可
- 加解密方式:明文(在熟练之后再改为加密吧)
测试
由于买的域名已被上线项目占用,所以买了内网穿透natapp 官网,可以直接在本地测试
获取二维码:http://你的域名/weChat/getQrCodeUrl
展示二维码,微信客户端扫一扫,关注公众号微信回调
至此,微信扫描微信公众号二维码实现登录已全部完成