JAVA微信扫码登录

1、说明

本帖有两种微信扫码的方式。根据测试公众号进行测试,所以不用担心没有公众号。
因为涉及到微信回调,所以要走本地的话需要进行内网穿透,本贴不包含内网穿透教学,很简单,请自行百度。

2、测试号配置

测试号地址:https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login
扫码登录后,进行配置。
在这里插入图片描述在这里插入图片描述
![在这里插入图片描述](https://img-blog.csdnimg.cn/60452a52382c4871b0a379aadbcaaf9a.png

3、代码

3.1 依赖

		<dependency>
            <groupId>com.github.binarywang</groupId>
            <artifactId>weixin-java-mp</artifactId>
            <version>4.3.0</version>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.7.0</version>
        </dependency>

3.2 配置文件

#APPID
wx.login.appid=wx9e******e7
#secret
wx.login.appsecret=17bb2e***********ec0467
wx.login.token=weixin
#回调地址(内网穿透地址)
wx.login.server=http://elwin.*************.com
#获取二维码
wx.login.qrCodeUrl: https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=TOKEN
#基础接口的token
wx.login.tokenUrl: https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
#获取openId
wx.login.openIdUrl: https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=APPSECRET&code=CODE&grant_type=authorization_code
#获取用户
wx.login.userInfoUrl: https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN
#通过ticket获取二维码
wx.login.showqrcode: https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=TICKET

3.3 公共类

@Component
public class WxLoginConfig {

    @Value("${wx.login.appid}")
    private String appId;        //公众号标识

    @Value("${wx.login.appsecret}")
    private String appSecret;    //公众号密码

    @Value("${wx.login.server}")
    private String server;        //服务器域名地址,用于微信服务器回调。
    
    @Value("${wx.login.qrCodeUrl}")
    private String qrCodeUrl;    //获取code接口

    @Value("${wx.login.tokenUrl}")
    private String tokenUrl;    //获取token接口

    @Value("${wx.login.openIdUrl}")
    private String openIdUrl;   //获取openid接口

    @Value("${wx.login.userInfoUrl}")
    private String userInfoUrl;   //获取用户信息接口

    @Value("${wx.login.token}")
    private String token;

    @Value("${wx.login.showqrcode}") //通过ticket获取二维码
    private String showQrCode;
    
 }

3.4 方式一

这种方式其实就是生一个二维码,二维码的内容是一个url。url的核心是REDIRECT_URL即回调地址。扫码之后,微信会根据REDIRECT_URL进行回调。

@Controller
@RequestMapping("/wx/login")
public class WxLoginController {

	@Autowired
	private WxLoginConfig wxLoginConfig;
	@Autowired
	private RestTemplate restTemplate;

	//生成二维码
	@GetMapping(value = "/qrCode")
	public void getQrCode(@RequestParam(value = "isRememberMe",required = false,defaultValue = "1")String isRememberMe, @RequestParam(value = "sign") String sign, HttpServletRequest request, HttpServletResponse response) throws IOException {
		String url = wxLoginConfig.getQrCodeUrl().replace("APPID",wxLoginConfig.getAppId())
				.replace("REDIRECT_URL",wxLoginConfig.getServer() + "/wx/login/callback")
				.replace("STATE","自定义传参");
		//生成二维码的,扫描后跳转上面的REDIRECT_URL地址
		QrCodeUtil.generate(url, 300, 300, "jpg", response.getOutputStream());
	}

	//回调
	@RequestMapping(value = "/callback")
	@ResponseBody
	public String pcCallback(String code, String state) throws Exception {
		String openId = getOpenId(code);
		System.out.println(openId);
		//获取微信用户信息
		getUserInfo(openId);
		//业务处理...
		return "登录成功";
	}

	/**
	 *	获取openId
	 * @param code
	 * @return
	 */
	private String getOpenId(String code){
		String url = wxLoginConfig.getOpenIdUrl().replace("APPID", wxLoginConfig.getAppId())
				.replace("APPSECRET", wxLoginConfig.getAppSecret()).replace("CODE",code);
		ResponseEntity<String> responseEntity = restTemplate.getForEntity(url, String.class);
		String body = responseEntity.getBody();
		JSONObject object = JSONObject.parseObject(body);
		return object.getString("openid");
	}

	/**
	 * 获取用户信息
	 * @param openId
	 * @return
	 */
	private JSONObject getUserInfo(String openId){
		//从微信上中拉取用户信息
		String url = wxLoginConfig.getUserInfoUrl().replace("ACCESS_TOKEN",getAccessToken()).replace("OPENID",openId);
		ResponseEntity<String> forEntity = restTemplate.getForEntity(url, String.class);
		String result = forEntity.getBody();
		JSONObject jsonObject = JSONObject.parseObject(result);
		return jsonObject;
	}
}

3.5 方式二

  • 先进行认证,提交
    在这里插入图片描述

  • 然后获取 access_token

  • 拿到 ticket

  • 生成二维码

  • 扫码后,根据上图中token验证的接口地址进行回调

@Controller
@RequestMapping("/wx/login")
public class WxLoginController {

	@Autowired
	private WxLoginConfig wxLoginConfig;
	@Autowired
	private RestTemplate restTemplate;

	/**
	 * pc点击微信登录,生成登录二维码
	 * @throws Exception
	 */
	@GetMapping(value = "/pcQrCode")
	@ResponseBody
	public void wxLoginPage(HttpServletRequest request, HttpServletResponse response) throws Exception {
		try {
			//获取token
			String accessToken =getAccessToken();
			//获取 ticket
			String ticket = getTicket(accessToken);
			//获取二维码
			String qrCodeUrl = wxLoginConfig.getShowQrCode().replace("TICKET",ticket);
			ResponseEntity<byte[]> forEntity = restTemplate.getForEntity(qrCodeUrl, byte[].class);
			byte[] body1 = forEntity.getBody();
			response.getOutputStream().write(body1);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 *  获取accessToken
	 * @return
	 */
	public String getAccessToken(){

		//根据appid和appsecret获取access_token
		wxLoginConfig.getTokenUrl();
		String url = wxLoginConfig.getTokenUrl().replace("APPID", wxLoginConfig.getAppId()).replace("APPSECRET", wxLoginConfig.getAppSecret());
		HttpHeaders headers = new HttpHeaders();
		ResponseEntity<JSONObject> responseEntity = restTemplate.exchange(url, HttpMethod.GET, new HttpEntity<>(null, headers), JSONObject.class);
		JSONObject object = responseEntity.getBody();
		String accessToken = "";
		accessToken = object.getString("access_token");
		return accessToken ;
	}

	/**
	 * 获取ticket
	 * @param accessToken
	 * @return
	 */
	public String getTicket(String accessToken){
		//请求地址
		String getQrCodeUrl = wxLoginConfig.getQrCodeUrl().replace("TOKEN", accessToken);

		HttpHeaders headers = new HttpHeaders();
		//参数设置
		Map<String, Object> map = new HashMap<>();
		//二维码的过期时间,单位为秒,最大2592000(即30天)不填,则默认有效期为60秒。
		map.put("expire_seconds", "604800");
		//二维码类型,QR_SCENE为临时的整型参数值,QR_STR_SCENE为临时的字符串参数值,QR_LIMIT_SCENE为永久的整型参数值,QR_LIMIT_STR_SCENE为永久的字符串参数值
		map.put("action_name", "QR_LIMIT_STR_SCENE");
		Map<String, Object> innerThenMap = new HashMap<>();
		//扫码回调时自定义要传输的数据
		innerThenMap.put("scene_str","elwin123");
		Map<String, Object> innerMap = new HashMap<>();
		innerMap.put("scene",innerThenMap);
		//二维码详细信息
		map.put("action_info", innerMap);
		// 组装请求体
		HttpEntity<Map<String, Object>> sendMap =
				new HttpEntity<Map<String, Object>>(map, headers);
		ResponseEntity<String> responseEntity = restTemplate.postForEntity(getQrCodeUrl, sendMap, String.class);
		String body = responseEntity.getBody();
		JSONObject jsonObject = JSONObject.parseObject(body);
		String ticket = jsonObject.getString("ticket");
		return ticket;
	}


	@RequestMapping("/checkSign")
	public String checkSign ( HttpServletRequest request) throws Exception {

		//获取微信请求参数
		String signature = request.getParameter("signature");
		String timestamp = request.getParameter("timestamp");
		String nonce = request.getParameter("nonce");
		String echostr = request.getParameter ("echostr");
		//参数排序。 token 就要换成自己实际写的 token
		String[] params = new String[]{timestamp, nonce, "weixin"};
		Arrays.sort(params);
		//拼接
		String paramstr = params[0] + params[1] + params[2];
		//加密
		//获取 shal 算法封装类
		MessageDigest Sha1Dtgest = MessageDigest.getInstance("SHA-1");
		//进行加密
		byte[] digestResult = Sha1Dtgest.digest(paramstr.getBytes("UTF-8"));
		//拿到加密结果
		String mysignature = bytes2HexString(digestResult);
		mysignature = mysignature.toLowerCase(Locale.ROOT);
		//是否正确
		boolean signsuccess = mysignature.equals(signature);
		//逻辑处理
		if (signsuccess && echostr != null) {
			//验证签名,接入服务器
			return echostr;
		} else {
			//接入成功后,下次回调过来就可以进行正常业务处理
			return callback(request);
		}
	}

	private String bytes2HexString(byte... bytes) {
		char[] hexChars = new char[bytes.length * 2];
		char[] hexArray = "0123456789ABCDEF".toCharArray();
		for (int j = 0; j < bytes.length; j++) {
			int v = bytes[j] & 0xFF;
			hexChars[j * 2] = hexArray[v >>> 4];
			hexChars[j * 2 + 1] = hexArray[v & 0x0F];
		}
		return new String(hexChars);
	}

	/**
	 *  回调业务处理
	 * @param request
	 * @return
	 * @throws Exception
	 */
	private String callback(HttpServletRequest request) throws Exception{
		//解析
		WxMpXmlMessage message= WxMpXmlMessage.fromXml(request.getInputStream());//获取消息流,并解析xml
		String messageType=message.getMsgType();								//消息类型
		String messageEvent=message.getEvent();								    //消息事件
		String openId =message.getFromUser();									//发送者帐号
		String touser=message.getToUser();										//开发者微信号
		String text=message.getContent();										//文本消息  文本内容
		String eventKey=message.getEventKey();									//二维码参数

		if(messageType.equals("event")){
			//获取微信用户信息
			JSONObject userInfo = this.getUserInfo(openId);
			//根据不同的回调事件处理各自的业务
			switch (messageEvent){
				case "SCAN": //扫码
					System.out.println("扫码");
					//业务处理...
					return "result";
					
				case "subscribe": //关注公众号
					System.out.println("关注公众号");
					//业务处理...
					return "result";
					
				case "unsubscribe": //取消关注公众号
					System.out.println("取消关注公众号");
					//业务处理...
					return "result";
			}
		}
		return "111";
	}

	/**
	 * 获取用户信息
	 * @param openId
	 * @return
	 */
	private JSONObject getUserInfo(String openId){
		//从微信上中拉取用户信息
		String url = wxLoginConfig.getUserInfoUrl().replace("ACCESS_TOKEN",getAccessToken()).replace("OPENID",openId);
		ResponseEntity<String> forEntity = restTemplate.getForEntity(url, String.class);
		String result = forEntity.getBody();
		JSONObject jsonObject = JSONObject.parseObject(result);
		return jsonObject;
	}

}

4、参考地址:

5、问题

5.1 {“errcode”:40125,“errmsg”:“invalid appsecret rid: 6368b9e5-0555a81b-3c7588c4”}

检查appid和appsecret是不是配置文件没写对

5.2 redirect_uri域名与后台配置不一致,错误码10003

在这里插入图片描述

回调地址没对

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值