加载中...

PC端微信扫码关注公众号并登录

PC端微信扫码关注公众号并登录

 

碰到这样一个需求,PC端生成公众号二维码,用户用手机微信扫描,如果用户未关注公众号,则关注公众号后自动登录,如果用户已经关注过公众号,则直接登录。

前端Vue,后端Java实现

分2步来完成:

1.生成带参数的临时二维码

https://developers.weixin.qq.com/doc/offiaccount/Account_Management/Generating_a_Parametric_QR_Code.html

 

2. 公众号后台服务监听扫码事件并获取用户信息

https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Receiving_event_pushes.html

https://developers.weixin.qq.com/doc/offiaccount/User_Management/Get_users_basic_information_UnionID.html#UinonId

1.生成带参数的临时二维码

获取临时二维码的接口为微信官方提供的 

https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=TOKEN,注意为POST方式

  1. @RequestMapping("/getQRCode")
  2. public Map<String,Object> getQRCode(HttpServletRequest request,String code) {
  3. Map<String,Object> map = new HashMap<String,Object>();
  4. String code_url = "https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=TOKEN";
  5. try{
  6. //获取公众号的Token,自行实现,我是定任务获取,存到了数据库
  7. PcAccessToken token = accessTokenService.findPcToken("");
  8. code_url = code_url.replace("TOKEN", token.getAccessToken());
  9. String scene_id = (System.currentTimeMillis()+"").substring(0,10);
  10. JSONObject json = new JSONObject();
  11. json.put("expire_seconds", "36000");
  12. json.put("action_name", "QR_SCENE");
  13. JSONObject info = new JSONObject();
  14. JSONObject scen = new JSONObject();
  15. scen.put("scene_id", scene_id);
  16. info.put("scene", scen);
  17. json.put("action_info", info);
  18. String data = HttpUtil.requestPost(code_url, json.toString());
  19. JSONObject resp = JSONObject.fromObject(data);
  20. System.out.println("data=="+data);
  21. map.put("data", resp);
  22. map.put("scene_id", scene_id);
  23. map.put("succ", "1");
  24. } catch(Exception e){
  25. map.put("succ", "0");
  26. e.printStackTrace();
  27. }
  28. return map;
  29. }

请求微信接口返回的数据,url参数即为二维码地址,和scene_id一起返回给前端。

前端vue使用 qrcodejs2组件,将url转为二维码图片,显示在相应位置。请求到二维码后,用sceneId查询扫码登录是否成功。

  1. <div class="" id="qrcode" ref="qrcode"></div>
  2. import QRCode from ''qrcodejs2''
  3. import loginApi from ''@/api/login'';
  4. data(){
  5. return{
  6. url: '''', //二维码地址
  7. sceneId:'''',//二维码带的参数,用来判断是那个用户扫码的
  8. timer: null,//定时器
  9. }
  10. },
  11. created(){
  12. getLoginQRCode();
  13. },
  14. methods:{
  15. async getLoginQRCode(){
  16. this.url = '''';
  17. window.clearInterval(this.timer);
  18. const res = await loginApi.getQRCode();
  19. console.log(''44res'',res)
  20. if(res.data.succ == ''1''){
  21. this.url = res.data.data.url;
  22. this.sceneId = res.data.scene_id;
  23. console.log(''sceneId'',res.data.scene_id);
  24. this.timer = setInterval(this.getUserByScene, 1000);
  25. this.$nextTick(() => {
  26. this.getQrcode(this.url)
  27. })
  28. }
  29. },
  30. //根据 sceneId 轮询查询用户信息,判断用户是否登录成功
  31. async getUserByScene(){
  32. if(!this.sceneId) return;
  33. const res = await loginApi.getUserBySceneId({sceneId: this.sceneId});
  34. console.log(''getUser'', res);
  35. if(res.data.succ == ''1'' && res.data.user){
  36. this.user = res.data.user;
  37. window.clearInterval(this.timer);
  38. this.url = '''';
  39. this.sceneId = '''';
  40. //登录成功,处理登录成功逻辑
  41. this.$Message.success(''登录成功'')
  42. }
  43. },
  44. getQrcode (text) {
  45. this.$refs.qrcode.innerHTML = '''' // 清除
  46. const qrcode = new QRCode(''qrcode'', {
  47. width: 160,
  48. height: 160,
  49. text: text, // 二维码地址
  50. colorDark: ''#000'',
  51. colorLight: ''#fff''
  52. })
  53. return qrcode
  54. },
  55. }

2.公众号后台服务监听扫码事件并获取用户信息

这块要在微信公众平台配置服务地址信息

开发—基本配置

New ImageNew Image

服务器配置信息提交的时候要验证 TOKEN,  验证通过才能提交。checkToken接口GET请求用来验证TOKEN,

  1. import java.security.MessageDigest;
  2. //...
  3. @RequestMapping(value="/checkToken",method=RequestMethod.GET)
  4. public String checkToken(HttpServletRequest req, HttpServletResponse resp)throws IOException {
  5. req.setCharacterEncoding("UTF-8");
  6. resp.setCharacterEncoding("UTF-8");
  7. String message = "success";
  8. String signature = req.getParameter("signature");
  9. String timestamp = req.getParameter("timestamp");
  10. String nonce = req.getParameter("nonce");
  11. String echostr = req.getParameter("echostr");
  12. try {
  13. String[] arr = {"sjyx", timestamp, nonce};
  14. Arrays.sort(arr);
  15. StringBuilder content = new StringBuilder();
  16. for (int i = 0; i < arr.length; i++) {
  17. content.append(arr[i]);
  18. }
  19. //sha1Hex 加密
  20. MessageDigest md = null;
  21. String temp = null;
  22. md = MessageDigest.getInstance("SHA-1");
  23. byte[] digest = md.digest(content.toString().getBytes());
  24. temp = byteToStr(digest);
  25. if ((temp.toLowerCase()).equals(signature)){
  26. return echostr;
  27. }
  28. return null;
  29. } catch (Exception e) {
  30. e.printStackTrace();
  31. }
  32. return message;
  33. }
  34. private static String byteToStr(byte[] byteArray){
  35. String strDigest = "";
  36. for (int i = 0; i < byteArray.length; i++) {
  37. strDigest += byteToHexStr(byteArray[i]);
  38. }
  39. return strDigest;
  40. }
  41. private static String byteToHexStr(byte mByte){
  42. char[] Digit = {''0'', ''1'', ''2'', ''3'', ''4'', ''5'', ''6'', ''7'', ''8'', ''9'', ''A'',''B'', ''C'', ''D'', ''E'', ''F'' };
  43. char[] tempArr = new char[2];
  44. tempArr[0] = Digit[(mByte >>> 4)& 0X0F];
  45. tempArr[1] = Digit[mByte & 0X0F];
  46. String s = new String(tempArr);
  47. return s;
  48. }

checkToken接口POST请求用来监听扫码事件,并获取用户信息

  1. @RequestMapping(value="/checkToken",method=RequestMethod.POST)
  2. public String responseEvent(HttpServletRequest req, HttpServletResponse resp) {
  3. String message = "success";
  4. try {
  5. //把微信返回的xml信息转义成map
  6. Map<String, String> map = XmlUtil.xmlToMap(req);
  7. System.out.println("微信接收到的消息为:"+map.toString());
  8. String openId = map.get("FromUserName");//消息来源用户标识
  9. String toUserName = map.get("ToUserName");//消息目的用户标识
  10. String msgType = map.get("MsgType");//消息类型(event或者text)
  11. String EventKey = map.get("EventKey");//消息来源用户标识
  12. if(EventKey.contains("_")){ //scene_id首次关注会有前缀,去掉就和生成二维码时带的一致了
  13. EventKey = EventKey.split("_")[1];
  14. }
  15. String scene_Id = EventKey;
  16. String eventType = map.get("Event");//事件类型
  17. if("subscribe".equals(eventType) || "SCAN".equals(eventType) ){ //关注 or 浏览
  18. //获取缓存的最新的公众号 token
  19. PcAccessToken token = accessTokenService.findPcToken("");
  20. String USER_INFO_URL = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN";
  21. USER_INFO_URL = USER_INFO_URL.replace("ACCESS_TOKEN", token.getAccessToken());
  22. USER_INFO_URL = USER_INFO_URL.replace("OPENID", openId);
  23. String info = HttpUtil.getRequest(USER_INFO_URL);
  24. System.out.println("----user-----"+info);
  25. JSONObject obj = JSONObject.fromObject(info);
  26. String unionid = obj.getString("unionid");
  27. String nickname = obj.getString("nickname");
  28. String headimgurl = obj.getString("headimgurl");
  29. String city = obj.getString("city");
  30. String province = obj.getString("province");
  31. String qr_scene = obj.getString("qr_scene");
  32. //获取到用户信息后处理登录逻辑,比如保存用户信息,用户表scene_id 字段,保存二维码的scend_id,供前端查询用户登录情况。
  33. }
  34. } catch (Exception e) {
  35. e.printStackTrace();
  36. }
  37. return message;
  38. }

xml转Map工具类 

  1. import java.io.IOException;
  2. import java.io.InputStream;
  3. import java.util.HashMap;
  4. import java.util.List;
  5. import java.util.Map;
  6. import javax.servlet.http.HttpServletRequest;
  7. import org.dom4j.Document;
  8. import org.dom4j.DocumentException;
  9. import org.dom4j.Element;
  10. import org.dom4j.io.SAXReader;
  11. public class XmlUtil {
  12. /*
  13. * xml转map
  14. */
  15. public static Map<String, String> xmlToMap(HttpServletRequest request) throws IOException, DocumentException{
  16. HashMap<String, String> map = new HashMap<String,String>();
  17. SAXReader reader = new SAXReader();
  18. InputStream ins = request.getInputStream();
  19. Document doc = reader.read(ins);
  20. Element root = doc.getRootElement();
  21. @SuppressWarnings("unchecked")
  22. List<Element> list = (List<Element>)root.elements();
  23. for(Element e:list){
  24. map.put(e.getName(), e.getText());
  25. }
  26. ins.close();
  27. return map;
  28. }
  29. }