springboot -- 实现微信扫码登录

请注册服务号(订阅号没权限),没服务号,无法认证的直接看下一步,使用测试号

dame 链接:https://pan.baidu.com/s/1ocOUoCGBIpFeyyiA88eysQ
提取码:ywkq

微信公众平台

https://mp.weixin.qq.com/cgi-bin/home?t=home/index&lang=zh_CN&token=1247726583

开发 --> 基本配置
在这里插入图片描述
设置 --> 公众号设置
我这里是订阅号,没有设置 网站授权回调的权限,菜单也没有,红框处,必须企业认证
在这里插入图片描述

使用测试号(测试号也是订阅号,也没权限,这里我们自己生成二维码登录直接获取用户信息)

测试号注册: https://mp.weixin.qq.com/debug/cgi-bin/sandboxinfo?action=showinfo&t=sandbox/index
记住这个:
在这里插入图片描述
找到网页授权:
在这里插入图片描述
回调域名(这里我使用了外网穿透工具)(后面不要加 /接口 的东西,前面也不要加 http://

在这里插入图片描述
外网穿透工具
教程: https://blog.csdn.net/qq_41463655/article/details/92846613
在这里插入图片描述

开始实现,项目结构

在这里插入图片描述

pom.xml

有用没用,依赖加上

		<!-- qq wx 联合登陆需要的依赖 commons-io, commons-lang3,httpclient,fastjson-->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-io</artifactId>
            <version>1.3.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.38</version>
        </dependency>



       <!-- 热部署 -->
        <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-devtools</artifactId>
            <optional>true</optional>
        </dependency> 
        
        <!-- thymeleaf 模版   -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        
		 <!-- web -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

配置文件(这里需要 http:// )

### QQ
constants.qqAppId=101513767
constants.qqAppSecret=b1d978cefcf405388893d8e686d307b0
constants.qqRedirectUrl=http://127.0.0.1:8080/QQLogin


### 微信
constants.weCatAppId=wxa997b110821cbfd6
constants.weCatAppSecret=a5cee82a40995afc5bc14d17058908c1
constants.weCatRedirectUrl=http://192.168.1.2

qq ,wx 登陆常量配置类(读取配置文件数据)

package com.example.demo.entity;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;

/**
 * qq ,wx 登陆常量配置类
 */
@Configuration
public class Constants {

	@Value("${constants.qqAppId}")
	private String qqAppId;

	@Value("${constants.qqAppSecret}")
	private String qqAppSecret;

	@Value("${constants.qqRedirectUrl}")
	private String qqRedirectUrl;

	@Value("${constants.weCatAppId}")
	private String weCatAppId;

	@Value("${constants.weCatAppSecret}")
	private String weCatAppSecret;

	@Value("${constants.weCatRedirectUrl}")
	private String weCatRedirectUrl;

  //自行 set  get
}

二维码生成 qrcode.js下载

下载 .zip的

下载地址: http://davidshimjs.github.io/qrcodejs/

放到项目里

html页面

引入qrcode.js
引入jquery.js

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>微信登录二维码</title>
<script src="http://libs.baidu.com/jquery/2.1.4/jquery.min.js"></script>
<script src="/js/qrcode.js"></script>
</head>

 <script type="text/javascript">
 
/*  $(function(){
	  var div1 = document.getElementById("div1");
	  alert(div1);
 }) */
 var c = null;
 //监听
 function getInfoJson(){
 	$.ajax({
         url: "/getInfoJson",
         type: "get",
         success: function (data) {
        	 if(data != "no"){
        		 $("#userinfo").html(data);
        		 //登录成功,取消监听
        		 clearInterval(c);
             }
         }
 	});
 }
 
    function wechatLogin(){
    	$.ajax({
            url: "/wxLoginPage",
            type: "POST",
            success: function (data) {
            	$("#sessionId").val(data.sessionId);
            	//生成二维码
            	var qrcode = new QRCode(document.getElementById("code"), {
                    width : 200,
                    height : 200
                });
                qrcode.makeCode(data.uri);      
                //监听是否成功登录(每秒执行一次 getInfoJson方法)
                c  = setInterval(getInfoJson,1000);
            }
    	});
    }
</script>
<body>
<input type="button" value="微信登录" th:onclick="wechatLogin()" />
<input type="hidden" id="sessionId"/>
<br/><br/><br/><br/>
<div id="code"></div> 
<div id="userinfo"></div> 
</body>
</html>

实现层

import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.xml.transform.Source;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.http.converter.ByteArrayHttpMessageConverter;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.ResourceHttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter;
import org.springframework.http.converter.xml.SourceHttpMessageConverter;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;

import com.alibaba.fastjson.JSONObject;
import com.example.demo.entity.Constants;

/**
 * 微信登录
 * 
 * @author wangsong
 * @date 2019年6月18日 下午8:04:15
 */
@Controller
public class WeCatController {

	/**
	 * wx :读取Appid相关配置信息静态类
	 */
	@Autowired
	private Constants constants;

	/**
	 * 用户信息临时保存处
	 */
	private static Object quert;

	/**
	 * 微信登录页
	 */
	@GetMapping("/wxLogin")
	public String login() {
		return "wxLogin";
	}

	/**
	 * pc点击微信登录,生成登录二维码
	 * 
	 * @author wangsong
	 * @date 2019年6月19日 下午5:58:56
	 * @param request
	 * @return
	 * @throws Exception
	 */
	@RequestMapping(value = "/wxLoginPage", method = RequestMethod.POST)
	@ResponseBody
	public Map<String, String> wxLoginPage(HttpServletRequest request) throws Exception {

		String sessionId = request.getSession().getId();
		// 设置redirect_uri和state=sessionId以及测试号信息,返回授权url
		String uri = this.getAuthorizationUrl("pc", sessionId);
		Map<String, String> data = new HashMap<String, String>();
		data.put("sessionId", sessionId);
		// 用来前端生成二维码
		data.put("uri", uri);
		// 生成二维码清除上一个用户的数据
		quert = null;
		return data;
	}

	/**
	 * 扫描二维码授权成功,取到code,设置的回调方法
	 * 
	 * @author wangsong
	 * @date 2019年6月19日 下午5:58:36
	 * @param code
	 * @param state
	 * @param request
	 * @param response
	 * @return
	 * @throws Exception
	 */
	@RequestMapping(value = "/pcAuth")
	@ResponseBody
	public String pcCallback(String code, String state, HttpServletRequest request, HttpServletResponse response,
			HttpSession session) throws Exception {
		// 根据code获取access_token和openId,不懂看微信文档
		String result = this.getAccessToken(code);
		JSONObject jsonObject = JSONObject.parseObject(result);
		// String refresh_token = jsonObject.getString("refresh_token");
		String access_token = jsonObject.getString("access_token");
		String openId = jsonObject.getString("openId");
		// 授权成功 --> 根据token和openId获取微信用户信息,不懂看我上一篇文章开始分享的链接
		JSONObject infoJson = this.getUserInfo(access_token, openId);
		if (infoJson != null) {
			infoJson.put("openId", openId);
		}
		// 登录成功保存用户数据
		quert = infoJson;
		return "登录成功";
	}

	/**
	 * 检测登录状态(获取用户信息) 每秒被调用一次,
	 * 
	 * @param 登录成功,立马得到用户信息返回前台,并取消监听
	 * 
	 * @author wangsong
	 * @return
	 * @date 2019年6月19日 下午8:18:38
	 */
	@RequestMapping(value = "/getInfoJson")
	@ResponseBody
	public String getInfoJson(HttpSession session) {
		System.out.println("666");
		if (quert == null) {
			return "no";
		}
		return quert.toString();
	}

	/**
	 * 获取生成的二维码url连接
	 * 
	 * @author wangsong
	 * @date 2019年6月19日 下午5:55:36
	 * @param appid:公众号的唯一标识
	 * @param redirect_uri:授权后重定向的回调链接地址
	 * @param response_type:返回类型,填写code
	 * @param scope:应用授权作用域,snsapi_base,snsapi_userinfo
	 * @param state:非必传,重定向后会带上state参数,开发者可以填写a-zA-Z0-9的参数值,最多128字节
	 * @param wechat_redirect:无论直接打开还是做页面302重定向时候,必须带此参数
	 * @return
	 */
	public String getAuthorizationUrl(String type, String state) {
		// url
		String url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=%s&redirect_uri=%s&response_type=code&scope=%s&state=%s#wechat_redirect";
		String callbackUrl = "";
		Object urlState = "";
		try {
			if ("pc".equals(type)) {
				// pc端回调方法(没处理,我这里回调一致)
				callbackUrl = URLEncoder.encode(constants.getWeCatRedirectUrl() + "/pcAuth", "utf-8");
				urlState = state;
			} else if ("mobile".equals(type)) {
				// pc端回调方法
				callbackUrl = URLEncoder.encode(constants.getWeCatRedirectUrl() + "/pcAuth", "utf-8");
				urlState = System.currentTimeMillis();
			}
		} catch (UnsupportedEncodingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		// 权限 snsapi_userinfo snsapi_base
		String scope = "snsapi_userinfo";
		url = String.format(url, constants.getWeCatAppId(), callbackUrl, scope, urlState);
		return url;
	}

	/**
	 * 获取 token, openId(token有效期2小时)
	 * 
	 * @author wangsong
	 * @date 2019年6月19日 下午5:55:11
	 * @param code
	 * @return
	 */
	public String getAccessToken(String code) {
		String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code";
		url = String.format(url, constants.getWeCatAppId(), constants.getWeCatAppSecret(), code);
		UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url);
		URI uri = builder.build().encode().toUri();

		String resp = getRestTemplate().getForObject(uri, String.class);
		if (resp.contains("openid")) {
			JSONObject jsonObject = JSONObject.parseObject(resp);
			String access_token = jsonObject.getString("access_token");
			String openId = jsonObject.getString("openid");
			JSONObject res = new JSONObject();
			res.put("access_token", access_token);
			res.put("openId", openId);
			res.put("refresh_token", jsonObject.getString("refresh_token"));
			return res.toJSONString();
		} else {
			return null;
		}
	}

	/**
	 * 微信接口中,token和openId是一起返回,故此方法不需实现
	 * 
	 * @param accessToken
	 * @return
	 */
	public String getOpenId(String accessToken) {
		return null;
	}

	/**
	 * 获取用户信息
	 * 
	 * @param accessToken
	 * @param openId
	 * @return
	 */
	public JSONObject getUserInfo(String accessToken, String openId) {
		String url = "https://api.weixin.qq.com/sns/userinfo?access_token=%s&openid=%s&lang=zh_CN";
		url = String.format(url, accessToken, openId);
		UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url);
		URI uri = builder.build().encode().toUri();
		String resp = getRestTemplate().getForObject(uri, String.class);
		if (resp.contains("errcode")) {
			return null;
		} else {
			JSONObject data = JSONObject.parseObject(resp);
			JSONObject result = new JSONObject();
			result.put("id", data.getString("unionid"));
			result.put("sex", data.getString("sex"));
			result.put("nickName", data.getString("nickname"));
			result.put("avatar", data.getString("headimgurl"));
			return result;
		}
	}

	/**
	 * 微信的token只有2小时的有效期,过时需要重新获取,所以官方提供了,根据refresh_token
	 * 刷新获取token的方法,本项目仅仅是获取用户,信息,并将信息存入库,所以两个小时也已经足够了
	 * 
	 * @author wangsong
	 * @date 2019年6月19日 下午5:34:07
	 * @param refresh_token
	 * @return
	 */
	public String refreshToken(String refresh_token) {
		String url = "https://api.weixin.qq.com/sns/oauth2/refresh_token?appid=%s&grant_type=refresh_token&refresh_token=%s";
		url = String.format(url, constants.getWeCatAppId(), refresh_token);
		UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url);
		URI uri = builder.build().encode().toUri();
		ResponseEntity<JSONObject> resp = getRestTemplate().getForEntity(uri, JSONObject.class);
		JSONObject jsonObject = resp.getBody();
		String access_token = jsonObject.getString("access_token");
		return access_token;
	}

	/**
	 * 
	 * @author wangsong
	 * @date 2019年6月19日 下午5:35:43
	 * @return
	 */
	public static RestTemplate getRestTemplate() {// 手动添加
		SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
		requestFactory.setReadTimeout(120000);
		List<HttpMessageConverter<?>> messageConverters = new LinkedList<>();
		messageConverters.add(new ByteArrayHttpMessageConverter());
		messageConverters.add(new StringHttpMessageConverter(StandardCharsets.UTF_8));
		messageConverters.add(new ResourceHttpMessageConverter());
		messageConverters.add(new SourceHttpMessageConverter<Source>());
		messageConverters.add(new AllEncompassingFormHttpMessageConverter());
		messageConverters.add(new MappingJackson2HttpMessageConverter());
		RestTemplate restTemplate = new RestTemplate(messageConverters);
		restTemplate.setRequestFactory(requestFactory);
		return restTemplate;
	}

效果:
在这里插入图片描述
生成二维码
在这里插入图片描述
拿出手机扫一扫,登录成功返回用户数据(手机显示登录成功字样)

在这里插入图片描述

在Spring Boot和Vue中实现微信扫码登录的步骤如下: 1. 在后端(Spring Boot)中,创建一个控制器类(如WeixinController),使用@RestController和@RequestMapping注解来定义请求路径。 2. 在控制器类中,注入微信登录服务(如WeiXinService)。 3. 创建一个GET请求方法,用于生成微信扫码登录的二维码。方法的参数可以是订单ID(如createNative方法中的orderId)。 4. 在方法中调用微信登录服务的createNative方法,获取生成的二维码信息(如map)。 5. 返回一个响应对象(如R.ok().data(map)),将二维码信息作为响应数据返回给前端。 在前端(Vue)中,可以使用wx.js定义一个createNative方法,用于调用后端接口获取二维码信息。可以使用前端插件来显示二维码。 以下是一个示例代码: 后端(Spring Boot)代码: ```java @RestController @RequestMapping("/api/order/weixin") public class WeixinController { @Autowired private WeiXinService weixinPayService; @GetMapping("/createNative/{orderId}") public R createNative(@ApiParam(name="orderId",value="订单id",required=true) @PathVariable("orderId") Long orderId) { Map<String, Object> map = weixinPayService.createNative(orderId); return R.ok().data(map); } } ``` 前端(Vue)代码: ```javascript // wx.js createNative(orderId) { return request({ url: `/api/order/weixin/createNative/${orderId}`, method: 'get' }); } ``` 请注意,以上代码仅为示例,实际实现中可能需要根据具体需求进行适当的修改和调整。
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值