首先说一下这个微信扫码登录它的方式有两种,一种是基于网页的redirect实现,一种是基于公众号推送消息实现,二者实现的效果是不一样的
基于公众号推送消息实现的扫码登录
前期准备
- 需要有自己的域名(这里你可以使用内网穿透,会生成一个自己的域名,网上一大堆,自己奥利给吧)
- 需要申请微信认证(如果你是我这种情况,那么同是天涯沦落人,往下走)
- 这一步需要具备以上条件,否则没什么用(上面没有的就继续下)
如上图,获取到appid,appsecret(项目中会用到)之后,再将服务器配置修改并启用就可以了,
*服务器配置中的token到底怎么配置
微信官方文档公众号介入指南说明(里面有一个PHP示例代码下载,其实这里不止PHP的)
上面的是微信文档说明,下面一定要注意,很关键
- 首先说一下URL,这个URL是微信服务器需要使用get方式请求的你的路径,其次它是白名单的(当你配置拦截器或过滤器或AOP的时候)。这个get请求仅仅是验证token的。
- 其次是token,这里的token可以随意填写,但是你的程序中的token必须与这个保持一致(这里就是访问你上面URL的接口中的token)
- 代码实现如下:这里的代码实现在下面贴
使用测试号
配置的话和上面一样的操作
代码实现
maven坐标
<dependency> <groupId>com.github.binarywang</groupId> <artifactId>weixin-java-open</artifactId> <version>3.8.0</version> </dependency>
配置类实现
import me.chanjar.weixin.mp.api.WxMpService; import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl; import me.chanjar.weixin.mp.config.impl.WxMpDefaultConfigImpl; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * @author xiaobo * @date 2023/4/10 */ @Configuration public class WeChatCodeConfig { @Value("${wx.appid}") private String appId; @Value("${wx.appsecret}") private String appSecret; /** * 定义WxMpService bean */ @Bean public WxMpService wxMpService() { WxMpService wxMpService = new WxMpServiceImpl(); WxMpDefaultConfigImpl config = new WxMpDefaultConfigImpl(); config.setAppId(appId); config.setSecret(appSecret); wxMpService.setWxMpConfigStorage(config); return wxMpService; } }
上面的wx.appid,wx.appsecret是定义到配置文件中的,是在前期准备的第三点中获取到的
获取登录二维码
@PostMapping("/getLoginQrCode") @ResponseBody public RespResult getLoginQrCode() throws WxErrorException, UnsupportedEncodingException { // 设置场景id,登录的时候需要根据这个获取redis中存在的openid String sceneStr = IdUtil.getSnowflake().nextIdStr(); WxMpQrCodeTicket wxMpQrCodeTicket = wxService.getQrcodeService().qrCodeCreateTmpTicket(sceneStr, 3600); // 这里获取到的是二维码,前端只需要用<img>标签中的src接收就可以展示 String s = wxService.getQrcodeService().qrCodePictureUrl(wxMpQrCodeTicket.getTicket()); return RespResult.success(s, sceneStr); }
上面的实现建议可以再做一步redis存一个有过期时间的key,防止后面一直轮询调用
前端轮询回调实现登录
@PostMapping("wechatOpenIdLogin") @ResponseBody public RespResult wechatOpenIdLogin(String sceneStr, HttpServletRequest request) { // 这里的sceneStr是上面接口返回的,这步操作是微信事件推送之后会存一个redis Boolean exists = JedisUtil.exists(sceneStr); if (exists) { // TODO 业务操作,其中JedisUtil.getStr(sceneStr)能得到微信扫码用户的openid return weChatCodeService.wechatLogin(JedisUtil.getStr(sceneStr), request); } else { return RespResult.fail(sceneStr); } }
微信事件推送接口实现
@RequestMapping("/sign") public void sign(HttpServletResponse response, HttpServletRequest request) throws Exception { String method = request.getMethod(); // 微信加密签名 String signature = request.getParameter("signature"); // 随机字符串 String echostr = request.getParameter("echostr"); // 时间戳 String timestamp = request.getParameter("timestamp"); // 随机数 String nonce = request.getParameter("nonce"); // 签名验证是get请求 if ("GET".equals(method)) { String[] str = {"123456token", timestamp, nonce}; // 字典排序 Arrays.sort(str); String bigStr = str[0] + str[1] + str[2]; // SHA1加密 String digest = sha1(bigStr); // 确认请求来至微信 if (digest.equals(signature)) { response.getWriter().print(echostr); } } else { // 获取WxMpXmlMessage WxMpXmlMessage wxMpXmlMessage = WxMpXmlMessage.fromXml(request.getInputStream()); // OpenId String fromUserName = wxMpXmlMessage.getFromUser(); // 消息类型,event final String msgType = wxMpXmlMessage.getMsgType(); // 事件类型 final String event = wxMpXmlMessage.getEvent(); String sceneStr = ""; log.error("微信返回验签是:{},{},{}", msgType, event, fromUserName); if (WxConsts.XmlMsgType.EVENT.equals(msgType)) { if (event.equals(WxConsts.EventType.SUBSCRIBE)) { if (ticket != null) { sceneStr = wxMpXmlMessage.getEventKey().replace("qrscene_", ""); } } //注:事件类型为SCAN即已关注 else if (event.equals(WxConsts.EventType.SCAN)) { if (ticket != null) { sceneStr = wxMpXmlMessage.getEventKey(); } } // 如果sceneStr不为空代表用户已经扫描并且关注了公众号 if (CharSequenceUtil.isNotEmpty(sceneStr)) { // 将微信公众号用户ID缓存到redis中,标记用户已经扫码完成,执行登录逻辑。 JedisUtil.setStr(sceneStr, fromUserName, OpenIdRedisExpireTime); } } response.getWriter().write("可以随便写"); } }
基于转发实现的扫码登录
这个和上面的不一样,这个用微信官方来说的话就是==网页授权获取用户基本信息==
前期准备
这里只需要获取到appid,appsecret,还有一个就是配置服务器域名如下图
代码实现
接口实现
这里需要用到上面公众号推送消息实现的->代码实现->(maven坐标,配置类实现)
/** * 获取微信扫码登录二维码 */ @RequestMapping("/getLoginQrCode") public RespResult getLoginQrCode() throws WxErrorException { // 重定向URl,这里必须配置公众号对应的域名 String redirectUrl = "http://localhost:8080/login"; // 这里只获取用户扫码地址 String url = wxService.oauth2buildAuthorizationUrl(redirectUrl, WxConsts.OAuth2Scope.SNSAPI_BASE, null); return RespResult.success(url); } /** * 微信扫码登录 */ @RequestMapping("wechatLogin") public RespResult wechatLogin(@RequestParam("code") String code, HttpServletRequest request) { return weChatCodeService.wechatLogin(code, request);
⚠️: 注意,这里的url并不是一个二维码,而是一个地址,如果你在浏览器中打开的话,它会提示使用微信客户端打开,所以这个就注定了他并非根本意义上的扫码登录,它仅仅是微信扫码然后直接打开你跳转后的页面,也就是你上面定义的重定向URI,如果你的网站是手机端和PC端都兼容,那么可以直接PC出码,手机扫码直接登录。具体实现和上面的差不多,这里贴个图,针对上面的接口。
画的很丑,将就用吧