PC端实现微信扫码登入功能

微信注册应用
首先需要在微信开放平台进行注册(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版权协议,转载请附上原文出处链接和本声明。