思路
1、Vue前端页面获取一个公众号的二维码,不是普通二维号,是带有场景值的
2、java后端接收前端的请求,生成一个带时效性的二维码链接返回给前端
3、公众号平台配置服务器接口地址
4、接收到关注或扫码请求并相应处理
5、前端轮询状态,如果检查到验证通过进到下一页面
前端页面
vue代码
wxlogin() {
this.$showLoading();
getTempQrCode(this.token).then((res) => {
console.log("res==========",res);
this.$closeLoading();
this.imageUrl = objToStr(res.data.message);
this.loginType='wx'
setTimeout(() => {
this.check();
}, 1000);
})
},
java代码
@RequestMapping(value = "/getTempQrCode", method = RequestMethod.GET)
public Result<?> getTempQrCode(@RequestParam(name = "scene_str", required = true) String sceneStr) {
WeixinHelper.setAppId(appid);
WeixinHelper.setSecret(secret);
String result = WeixinHelper.createTempQrCode(WeixinHelper.getAccessToken(), sceneStr);
return Result.ok(result);
}
public static String createTempQrCode(String access_token, String scene_str) {
String url = "https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=" + access_token;
Map<String, Object> data = new HashMap<>();
data.put("expire_seconds", 604800);
data.put("action_name", "QR_STR_SCENE");
Map<String, Object> action_info = new HashMap<>();
Map<String, Object> scene = new HashMap<>();
scene.put("scene_str", scene_str);
action_info.put("scene", scene);
data.put("action_info", action_info);
String json = HttpUtil.createPost(url)
.header("Content-Type", "application/json")
.body(JSONUtil.toJsonStr(data))
.execute().body();
System.out.println("json = " + json);
String qrcode = (String) JSONUtil.getByPath(JSONUtil.parse(json), "ticket");
return "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=" + qrcode;
}
public static String getAccessToken() {
accessToken = getNewAccessToken();
return accessToken;
}
public static String getNewAccessToken() {
String access_token = "";
String grant_type = "client_credential";//获取access_token填写client_credential
String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=" + grant_type + "&appid=" + appId + "&secret=" + secret;
try {
URL urlGet = new URL(url);
HttpURLConnection http = (HttpURLConnection) urlGet.openConnection();
http.setRequestMethod("GET"); // 必须是get方式请求
http.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
http.setDoOutput(true);
http.setDoInput(true);
System.setProperty("sun.net.client.defaultConnectTimeout", "30000");// 连接超时30秒
System.setProperty("sun.net.client.defaultReadTimeout", "30000"); // 读取超时30秒
http.connect();
InputStream is = http.getInputStream();
int size = is.available();
byte[] jsonBytes = new byte[size];
is.read(jsonBytes);
String message = new String(jsonBytes, "UTF-8");
JSONObject demoJson = JSONObject.parseObject(message);
System.out.println("JSON字符串:" + demoJson);
access_token = demoJson.getString("access_token");
is.close();
} catch (Exception e) {
e.printStackTrace();
}
return access_token;
}
公众号配置
java提供消息接收接口代码,有两个,一个get(公众平台网址验证的时候用),一个post(消息事件触发时用)
/*
* @param signature 微信加密签名,signature结合了开发者填写的 token 参数和请求中的 timestamp 参数、nonce参数。
* @param timestamp 时间戳
* @param nonce 这是个随机数
* @param echostr 随机字符串,验证成功后原样返回
*/
@GetMapping("/wx/event")
public void get(@RequestParam(required = false) String signature,
@RequestParam(required = false) String timestamp,
@RequestParam(required = false) String nonce,
@RequestParam(required = false) String echostr,
HttpServletResponse response) throws IOException {
System.out.println("接受事件===" + echostr);
response.setCharacterEncoding("UTF-8");
response.getWriter().write(echostr);
response.getWriter().flush();
response.getWriter().close();
}
//处理微信推送事件
@PostMapping("/wx/event")
public void post(final HttpServletRequest request, HttpServletResponse response) {
System.out.println("接受事件");
try {
// 微信加密签名
final String signature = request.getParameter("signature");
// 时间戳
final String timestamp = request.getParameter("timestamp");
// 随机数
final String nonce = request.getParameter("nonce");
// 随机字符串
final String echostr = request.getParameter("echostr");
//将xml文件转成易处理的map(下方贴出)
final Map<String, String> map = oConvertUtils.parseXml(request);
//开发者微信号
final String toUserName = map.get("ToUserName");
//OpenId
final String fromUserName = map.get("FromUserName");
//消息创建时间 (整型)
final String createTime = map.get("CreateTime");
//消息类型,event
final String msgType = map.get("MsgType");
//事件类型
final String event = map.get("Event");
String scene_str = "";
String msg = "";
if ("event".equals(msgType)) {
if (event.equals("subscribe")) {
final String ticket = map.get("Ticket");
if (ticket != null) {
scene_str = map.get("EventKey").replace("qrscene_", "");
}
msg = getXmlReturnMsg(fromUserName, toUserName, (new Date()).getTime(), "欢迎您使用量子文档");
}
//注:事件类型为SCAN即已关注
else if (event.equals("SCAN")) {
final String ticket = map.get("Ticket");
if (ticket != null) {
scene_str = map.get("EventKey");
}
msg = getXmlReturnMsg(fromUserName, toUserName, (new Date()).getTime(), "您刚刚扫码登录了量子文档");
}
}
System.out.println("event:" + event);
System.out.println("场景值:" + scene_str);
System.out.println("openId:" + fromUserName);
System.out.println("ToUserName:" + toUserName);
// 如果scene_str 不为空代表用户已经扫描并且关注了公众号
if (oConvertUtils.isNotEmpty(scene_str)) {
QueryWrapper<SysUser> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("open_id", fromUserName);
SysUser sysUser = sysUserService.getOne(queryWrapper, false);
if (sysUser == null) {
sysUser = new SysUser();
sysUser.setOpenId(fromUserName);
sysUser.setUsername(fromUserName);
sysUser.setRealname(null);
String salt = oConvertUtils.randomGen(8);
String passwordEncode = PasswordUtil.encrypt(fromUserName, salt, salt);
sysUser.setSalt(salt);
sysUser.setMemberLevelId("1");
sysUser.setPassword(passwordEncode);
sysUser.setStatus(1);
sysUser.setDelFlag(CommonConstant.DEL_FLAG_0);
sysUserService.save(sysUser);
LzMessage message = new LzMessage();
message.setMsgDate(new Date());
message.setContent("欢迎您使用量子文档");
message.setFileId(null);
message.setIsRead(0);
message.setDelFlag(0);
message.setMsgType(3);
message.setFromUserId("001");
message.setFromUserName("小量子");
message.setToUserId(sysUser.getId());
lzMessageService.save(message);
}
// 将微信公众号用户ID缓存到redis中,标记用户已经扫码完成,执行登录逻辑。
redisUtil.set(scene_str, fromUserName, 60);
}
System.out.println("打印消息体=========");
System.out.println(msg);
System.out.println("=========");
response.setCharacterEncoding("UTF-8");
PrintWriter out = response.getWriter();
out.print(msg);
out.flush();
out.close();
} catch (Exception e) {
e.printStackTrace();
}
}
vue前端轮询
check() {
if (this.loginType!='wx') return false;
checkScanState(this.token).then((res) => {
console.log(res);
console.log("check....");
if (res.data.success == true) {
sessionStorage.setItem('token', res.data.result.token)
sessionStorage.setItem('userId', res.data.result.userInfo.id)
sessionStorage.setItem('userName', res.data.result.userInfo.username)
sessionStorage.setItem('realname', res.data.result.userInfo.realname)
sessionStorage.setItem('sex', res.data.result.userInfo.sex)
sessionStorage.setItem('memberLevelName', res.data.result.memberLevel.name)
sessionStorage.setItem('maxFileCount', res.data.result.memberLevel.maxFileCount)
this.loginType = "";
if (objToStr(res.data.result.userInfo.realname) == "") {
this.$showInputBox({
caption: "输入您的姓名",
inputValue: '',
callback: (data) => {
updateRealname(res.data.result.userInfo.id, data).then((res) => {
sessionStorage.setItem('realname', data)
this.$closeInputBox();
})
}
})
}
this.$router.push({
path: '/person' ,
})
} else {
setTimeout(() => {
this.check();
}, 1000);
}
})
},
后端接受前端查状态的轮询请求
@ApiOperation("检查扫码状态")
@GetMapping("/checkScanState")
public Result<JSONObject> wechatLogin(@RequestParam(name = "scene_str", required = true) String sceneStr) {
Result<JSONObject> result = new Result<JSONObject>();
Object openId = redisUtil.get(sceneStr);
if (openId == null) {
result.error500("未登入");
return result;
}
QueryWrapper<SysUser> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("open_id", openId);
SysUser sysUser = sysUserService.getOne(queryWrapper, false);
if (sysUser != null) {
String syspassword = sysUser.getPassword();
String username = sysUser.getUsername();
// 生成token
String token = JwtUtil.sign(username, syspassword);
redisUtil.del(CommonConstant.PREFIX_USER_TOKEN + token);
//清空用户登录Shiro权限缓存
redisUtil.del(CommonConstant.PREFIX_USER_SHIRO_CACHE + sysUser.getId());
//清空用户的缓存信息(包括部门信息),例如sys:cache:user::<username>
redisUtil.del(String.format("%s::%s", CacheConstant.SYS_USERS_CACHE, sysUser.getUsername()));
result = sysUserService.checkUserIsEffective(sysUser);
if(!result.isSuccess()) {
return result;
}
//用户信息
userInfo(sysUser, result);
//添加日志
sysBaseAPI.addLog("用户名: " + sysUser.getUsername() + ",登录成功!", CommonConstant.LOG_TYPE_1, null);
}
return result;
}
/**
* 用户信息
*
* @param sysUser
* @param result
* @return
*/
private Result<JSONObject> userInfo(SysUser sysUser, Result<JSONObject> result) {
String syspassword = sysUser.getPassword();
String username = sysUser.getUsername();
// 生成token
String token = JwtUtil.sign(username, syspassword);
// 设置token缓存有效时间
redisUtil.set(CommonConstant.PREFIX_USER_TOKEN + token, token);
redisUtil.expire(CommonConstant.PREFIX_USER_TOKEN + token, JwtUtil.EXPIRE_TIME*2 / 1000);
// 获取用户部门信息
JSONObject obj = new JSONObject();
List<SysDepart> departs = sysDepartService.queryUserDeparts(sysUser.getId());
obj.put("departs", departs);
if (departs == null || departs.size() == 0) {
obj.put("multi_depart", 0);
} else if (departs.size() == 1) {
sysUserService.updateUserDepart(username, departs.get(0).getOrgCode());
obj.put("multi_depart", 1);
} else {
obj.put("multi_depart", 2);
}
obj.put("token", token);
obj.put("userInfo", sysUser);
obj.put("sysAllDictItems", sysDictService.queryAllDictItems());
obj.put("memberLevel", lzMemberLevelService.getById(sysUser.getMemberLevelId()));
result.setResult(obj);
result.success("登录成功");
return result;
}