微信注册应用
首先需要在微信开放平台进行注册(https://open.weixin.qq.com/),并认证一个网站应用。
注册步骤可参考:https://blog.csdn.net/qq_36014509/article/details/88996562
微信二维码API对接文档:https://developers.weixin.qq.com/doc/oplatform/Website_App/WeChat_Login/Wechat_Login.html


前端二维码展示
<div id="bodyDiv">
<button onclick="wxLogin()" class="layui-btn layui-btn-normal layui-btn-fluid" style="box-shadow: 0 1px 6px #1E9FFF;border-radius: 4px;">
<img src="${path}/img/wx.png" style="width: 18px;">
使用微信登录
</button>
</div>
页面需要引入JS文件(支持https):
http://res.wx.qq.com/connect/zh_CN/htmledition/js/wxLogin.js
生成微信二维码
//用的layUI的弹出框
var wxLoginSoleCodeGetTimer ;
function wxLogin(){
//背景添加高斯模糊
$("#bodyDiv").addClass("vague");
//显示微信扫码登录
layer.open({
type: 1,
closeBtn:1,
resize:false,
skin: 'wxClass',
title:['微信扫码登录'],
area: ['340px', '485px'],
content: '<div id="wx-qrcode" style="margin:20px;"></div>',
cancel: function(index, layero){
//删除背景添加高斯模糊
$("#bodyDiv").removeClass("vague");
//停止定时器
window.clearInterval(wxLoginSoleCodeGetTimer);
}
});
//调用方法生成二维码
createQRCode();
}
微信二维码进行倒计时
function createQRCode(){
//获取当前时间戳准备存入redis
var soleCode= Date.parse(new Date());
//把当前时间戳传到后台
$.get("${path}/login/wxLoginSole.do",{soleCode:soleCode},function(data){
if(data.code == "success"){
//实例微信登录二维码JS对象
var obj = new WxLogin({
self_redirect:false,
id:"wx-qrcode", //这个id为弹出框的样式id,主要是为了让生产的二维码嵌套在弹出框中
appid: "wx31bb558f766faae6",
scope: "snsapi_login",
redirect_uri: "http://localhost/login/wxLoginOAuth", //扫码后调用的后台方法
state: soleCode
})
//设置定时器每秒请求后台获取redis中时间戳做对比
wxLoginSoleCodeGetTimer = window.setInterval("getWxLoginSoleCode('"+soleCode+"')",1000);
}else{
layer.alert('出错了,请重试!', {icon: 2});
}
});
}
把时间戳存入redis
//获取前端生成的时间戳存进redis
@ResponseBody
@RequestMapping("/wxLoginSole")
public Object wxLoginSole(HttpServletRequest request,HttpServletResponse response){
Map<String,Object> result = new HashMap<String,Object>();
//取出时间戳
String soleCode = request.getParameter("soleCode");
if(StringUtils.isNotBlank(soleCode)){
//选择redis数据库1
boolean resultSetDb = redisUtil.select(1)
if(resultSetDb){
//将guid存在redis里,保存时间十分钟
boolean resultSet = redisUtil.set(soleCode ,"1",600);
if(resultSet){
result .put("code", "success");
}else{
result .put("code", "failed");
}
}else{
result .put("code", "failed");
}
}else{
result .put("code", "failed");
}
return result;
}
定时请求redis中的时间戳,看二维码是否超时
//定时请求微信登录数据
function getWxLoginSoleCode(soleCode){
$.get("${path}/login/getWxLoginSoleCode.do", {soleCode:soleCode}, function(data) {
if (data.code == 'success') {
if(data.info == 'stop'){
//二维码已过期,停止定时器且提示二维码已过期
window.clearInterval(wxLoginSoleCodeGetTimer);
//先删除微信二维码元素
$("#wx-qrcode").empty();
//自定义元素覆盖微信二维码元素
$("#wx-qrcode").append('<div style="text-align: center;padding-top: 120px;"><p>二维码已失效</p><button style="margin-top: 5px;" id="retrieve-qrcode" type="button" class="layui-btn layui-btn-normal">重新获取二维码</button></div>');
}
}else{
//后台异常或没有soleCode参数,停止定时器
window.clearInterval(wxLoginSoleCodeGetTimer);
}
});
}
//前端定时请求该方法获取redis中存入的时间戳
@ResponseBody
@RequestMapping("/getWxLoginSoleCode")
public Object getWxLoginSoleCode(HttpServletRequest request,HttpServletResponse response){
JSONObject jsonObj = new JSONObject();
try {
//取出请求中带有的时间戳
String soleCode= request.getParameter("soleCode");
if(StringUtils.isNotBlank(soleCode)){
//选择redisDb6
boolean resultSetDb = redisUtil.setDb(6);
if(resultSetDb){
//定时器每秒获取一次redis中的时间戳
String resultGet = (String)redisUtil.get(soleCode);
if(StringUtils.isBlank(resultGet)){
//当值为空已找不到时,说明二维码已到十分钟已过期,返回给前端停止定时器
jsonObj.put("info", "stop");
jsonObj.put("code", "success");
}else{
//否则继续等待
jsonObj.put("code", "success");
}
}
}else{
jsonObj.put("code", "failed");
}
} catch (Exception e) {
jsonObj.put("code", "failed");
e.printStackTrace();
}
return jsonObj;
}
//重新获取二维码
$(document).on("click", "#retrieve-qrcode", function(){
//先删除自己元素
$("#wx-qrcode").empty();
//重新加上二维码
createQRCode();
});

后端代码进行验证

返回说明
正确的返回:
{
"access_token":"ACCESS_TOKEN",
"expires_in":7200,
"refresh_token":"REFRESH_TOKEN",
"openid":"OPENID",
"scope":"SCOPE",
"unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL"
}
错误返回样例:
{"errcode":40029,"errmsg":"invalid code"}

后端登入操作
//微信登录鉴权及跳转
@ResponseBody
@RequestMapping("/wxLoginOAuth")
public Object wxLoginOAuth(HttpServletRequest request,HttpServletResponse response){
Map<String,Object> result = new HashMap<String,Object>();
// 第三方发起微信授权登录请求,微信用户允许授权第三方应用后,微信会拉起应用或重定向到第三方网站,并且带上授权临时票据code参数;
String code = request.getParameter("code");
//前端扫码登入传来的时间戳
String state = request.getParameter("state");
//用户拒绝授权时code为空,微信也不会请求该接口。为了防止非法请求,若code为空则返回
if(StringUtils.isBlank(code)){
result.put("code","failed");
result.put("msg","登入请求被拒绝!");
return result;
}
//请求state为空则不正常,直接返回。否则去redis验证是否正确合法
if(StringUtils.isBlank(state)){
result.put("code","failed");
result.put("msg","请求状态异常!");
return result;
}else{
//选择redis数据库6
boolean resultSetDb = redisUtil.setDb(6);
if(resultSetDb){
//根据state判断redis中是否存在该state。若没值说明二维码已过期或者请求不合法
boolean resultHasKey = redisUtil.hasKey(state);
//微信账号和系统账号绑定时会用到
redisUtil.set("code",code,600); //在redis缓存中保留十分钟
if(!resultHasKey){
result.put("code","failed");
result.put("msg","请求超时或异常,请重新扫码登入!");
return result;
}
}else{
result.put("code","failed");
result.put("msg","请求超时或异常,请重新扫码登入!");
return result;
}
}
//请求微信通过code获取access_token
String accessToken = getInfo("https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code");
//将请求结果字符串转json
JSONObject accessTokenJson = JSONObject.fromObject(accessToken);
String access_token = (String)accessTokenJson.get("access_token");//接口调用凭证,登录后右上角展示数据需要该值去获取
String openid = (String)accessTokenJson.get("openid");//授权用户唯一标识
String unionid = (String)accessTokenJson.get("unionid");//用户统一标识(微信登录不需要该字段,但后面如果拓展其他功能可能需要)
if(StringUtils.isNotBlank(openid) && StringUtils.isNotBlank(unionid) && StringUtils.isNotBlank(access_token)){
//跟据授权标识查询用户
User user = userService.selectByOpenid(openid);
//若根据openid查不到用户,则说明没有绑定系统账号,此时跳转到绑定账号页面。
if(user == null){
//跳转微信账号绑定页面,同时把时间戳参数state带过去
response.sendRedirect("/wxbindlogin.html?key="+state);
}else{//否则已绑定系统账号,可以直接登录
//以下为登录
User userNew = new User();
userNew.setAccount(user.getAccount());
loginService.updatePWD(userNew);
//根据access_token和openid获取用户昵称和头像用作右上角显示
String wxInfo = getInfo("https://api.weixin.qq.com/sns/userinfo?access_token="+access_token+"&openid="+openid);
//将请求结果字符串转json
JSONObject wxInfoJson = JSONObject.fromObject(wxInfo);
String nickname = (String)wxInfoJson.get("nickname");//普通用户昵称
String headimgurl = (String)wxInfoJson.get("headimgurl");//用户头像,最后一个数值代表正方形头像大小(有0、46、64、96、132数值可选,0代表640*640正方形头像),用户没有头像时该项为空
HttpSession session = request.getSession();
session.setAttribute("wxusername", nickname);
session.setAttribute("wxuserimg", headimgurl);
response.sendRedirect("/index3.jsp");
}
//不管有没有绑定账号或登录成功,state已完成鉴权任务,所以删除redis中的state信息
redisUtil.setDb(6);
redisUtil.del(state);
}else{
response.sendRedirect("/login.html");
return;
}
} catch (Exception e) {
e.printStackTrace();
try {
response.sendRedirect("/login.html");
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
}
//调用接口
private String getInfo(String URL) {
// 创建Httpclient对象
CloseableHttpClient httpclient = HttpClients.createDefault();
CloseableHttpResponse response = null;
String resultString = null;
try {
// 创建uri
URIBuilder builder = new URIBuilder(URL);
URI uri = builder.build();
// 创建http GET请求
HttpGet httpGet = new HttpGet(uri);
// 执行请求
response = httpclient.execute(httpGet);
// 判断返回状态是否为200
if (response.getStatusLine().getStatusCode() == 200) {
resultString = EntityUtils.toString(response.getEntity(),"UTF-8");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (response != null) {
response.close();
}
httpclient.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return resultString;
}
版权声明:本文为fffvdgjvbsfkb123456原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。