Third-party login: WeChat scan code login


Friends who have various questions can refer to the official documents to study the WeChat development documents in detail . This time we will introduce the WeChat scan code login that does not separate the front and back ends.

WeChat login development process:

  1. Apply for WeChat access
  2. Generate login QR code
  3. User scans code and authorizes
  4. Call the callback method
  5. Get user information through code and bring it to the page for display

Official flow chart:
Insert image description here

1. Apply for WeChat access:

First of all, I would like to remind everyone: It is very troublesome to apply for WeChat access. Because of the company's business needs, I use the company's application. I haven’t applied myself yet.

First go to the WeChat open platform https://open.weixin.qq.com

Insert image description here
Apply for a website application (can only be used after approval)

Insert image description here
Note:

  1. appid and appSecret are the credentials for calling the WeChat interface
  2. Authorization callback domain: The QR code address generated by calling the WeChat interface. After the user scans the code and authorizes it, the user will be redirected to the callback address.
  3. Because the test is performed on localhost, third-party WeChat will not be able to jump if the callback address is localhost. The reason is that the external network cannot access the local area. What should I do? Solution: Then use ngrok intranet penetration to map the local project service to the public network, so the callback address filled in during the test is the access domain name during intranet penetration.
  4. If you don’t know about intranet penetration, I suggest you take a look at the use of Sunny-Ngrok intranet penetration first.

Insert image description here

If you don’t know about intranet penetration, I suggest you take a look at the use of Sunny-Ngrok intranet penetration first.

Start the Sunny-Ngrok intranet penetration client and first map the local service to the public network.

Insert image description here

2. Project environment setup:

Let's officially start the code part, create a SpringBoot project and import the required Jar package:
Environment: JDK1.8, SpringBoot2.3.5.RELEASE

<dependencies>
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter</artifactId>
       </dependency>

       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-thymeleaf</artifactId>
       </dependency>

       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-web</artifactId>
       </dependency>

       <dependency>
           <groupId>org.apache.httpcomponents</groupId>
           <artifactId>httpclient</artifactId>
           <version>4.5.13</version>
       </dependency>
       
       <dependency>
           <groupId>org.json</groupId>
           <artifactId>json</artifactId>
           <version>20200518</version>
       </dependency>
</dependencies>

Yaml file:
Because these four parameters are required, they are configured directly in the yaml file. When used, they can be introduced directly in the class through the @Value annotation.

  1. appid:
  2. appsecret:
  3. scope: snsapi_login
  4. callBack:
server:
  port: 80

spring:
  thymeleaf:
    prefix: classpath:/templates/
    suffix: .html
  mvc:
    static-path-pattern: classpath:/static/

wechat:
  #微信接口的AppID:
  appid: 
  #微信接口的AppSecret:
  appsecret: 
  #应用授权作用域(网站应用目前的固定写法就是snsapi_login)
  scope: snsapi_login
  #扫码之后的回调地址:
  callBack: http://wechatlogin.free.idcfengye.com/callBack

A tool class HttpRequestUtils is needed here to initiate http requests. You can copy the code directly and use

package com.login.utils;

import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

/**
 * @author: libo
 * @date: 2020/11/21  20:49
 * @motto: 即使再小的帆也能远航
 */
public class HttpRequestUtils {
    
    

    private static CloseableHttpClient httpClient;

    static {
    
    
        PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
        cm.setMaxTotal(100);
        cm.setDefaultMaxPerRoute(20);
        cm.setDefaultMaxPerRoute(50);
        httpClient = HttpClients.custom().setConnectionManager(cm).build();
    }

    public static String httpGet(String url) {
    
    
        CloseableHttpResponse response = null;
        BufferedReader in = null;
        String result = "";
        try {
    
    
            HttpGet httpGet = new HttpGet(url);
            RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(30000).setConnectionRequestTimeout(30000).setSocketTimeout(30000).build();
            httpGet.setConfig(requestConfig);
            httpGet.setConfig(requestConfig);
            httpGet.addHeader("Content-type", "application/json; charset=utf-8");
            httpGet.setHeader("Accept", "application/json");
            response = httpClient.execute(httpGet);
            in = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
            StringBuffer sb = new StringBuffer("");
            String line = "";
            String NL = System.getProperty("line.separator");
            while ((line = in.readLine()) != null) {
    
    
                sb.append(line + NL);
            }
            in.close();
            result = sb.toString();
        } catch (IOException e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            try {
    
    
                if (null != response) {
    
    
                    response.close();
                }
            } catch (IOException e) {
    
    
                e.printStackTrace();
            }
        }
        return result;
    }
}

3. Backend Controller interface:

package com.login.controller;

import com.login.utils.HttpRequestUtils;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Map;

/**
 * @author: libo
 * @date: 2020/11/21  21:32
 * @motto: 即使再小的帆也能远航
 */
@Controller
@CrossOrigin
@SuppressWarnings("unchecked")
public class weChatController {
    
    

    @Value(value = "${wechat.appid}")
    private String appid;

    @Value(value = "${wechat.appsecret}")
    private String appsecret;

    @Value(value = "${wechat.scope}")
    private String scope;

    @Value(value = "${wechat.callback}")
    private String callBack;


    /*生产二维码链接进行扫码登录*/
    @RequestMapping("/login")
    public String index(Model model) throws Exception {
    
    

        String oauthUrl = "https://open.weixin.qq.com/connect/qrconnect?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect";
        String redirect_uri = URLEncoder.encode(callBack, "utf-8");
        oauthUrl = oauthUrl.replace("APPID", appid).replace("REDIRECT_URI", redirect_uri).replace("SCOPE", scope);
        model.addAttribute("oauthUrl", oauthUrl);

        return "login";
    }


    /*生成自定义二维码*/
    @RequestMapping("/custom")
    public String custom(Model model) throws UnsupportedEncodingException {
    
    

        String redirect_uri = URLEncoder.encode(callBack, "utf-8");

        model.addAttribute("appid", appid);
        model.addAttribute("scope", scope);
        model.addAttribute("redirect_uri", redirect_uri);

        return "custom";
    }


    /*回调方法*/
    @RequestMapping("/callBack")
    public String callBack(String code,Map<String,Object> map) {
    
    

        //1.通过code获取access_token
        String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code";
        url = url.replace("APPID", appid).replace("SECRET", appsecret).replace("CODE", code);
        String tokenInfoStr = HttpRequestUtils.httpGet(url);

        JSONObject tokenInfoObject = new JSONObject(tokenInfoStr);

        //2.通过access_token和openid获取用户信息
        String userInfoUrl = "https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID";
        userInfoUrl = userInfoUrl.replace("ACCESS_TOKEN", tokenInfoObject.getString("access_token")).replace("OPENID", tokenInfoObject.getString("openid"));
        String userInfoStr = HttpRequestUtils.httpGet(userInfoUrl);

        map.put("token", tokenInfoStr);

        /*转为JSON*/
        JSONObject user = new JSONObject(userInfoStr);

        /*只获取openid并返回,openid是微信用户的唯一标识,userInfoStr里面有用户的全部信息*/
        String openid = user.getString("openid");
        map.put("openid", openid);

        /*获取用户头像url*/
        String headimgurl = user.getString("headimgurl");
        map.put("headimgurl", headimgurl);

        /*获取用户昵称*/
        String nickname = user.getString("nickname");
        map.put("nickname", nickname);

        /*获取用户性别*/
        int sex = user.getInt("sex");
        map.put("sex", sex);

        /*获取用户国家*/
        String country = user.getString("country");
        map.put("country", country);

        /*获取用户省份*/
        String province = user.getString("province");
        map.put("province", province);

        /*获取用户城市*/
        String city = user.getString("city");
        map.put("city", city);

        return "callBack";
    }

}

4.HTML page code:

1. Click to generate the QR code page: I have embedded a WeChat logo picture here, click the a label to jump to weChatLogin.htmlInsert image description here

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>wechat登录</title>
</head>
<body>
    <a th:href="${oauthUrl}">
        <img src="../static/wechat_logo.png" alt="微信登录">
    </a>
</body>
</html>

2. After calling the method, return to the user information page callBack.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>授权结果页</title>
</head>
<body>

        <h2>用戶,授权成功!</h2><br>
        <h3>通过code获取access_token 结果:</h3>
        <p th:text="${token}"></p>

        <h3>通过access_token获取用户信息 结果:</h3>
        头像:<img th:src="${headimgurl}" alt="用户头像"><br/>
        openid:<span th:text="${openid}"></span><br/>
        昵称:<span th:text="${nickname}"></span><br/>
        性别:<span th:text="${sex}"></span><br/>
        国家:<span th:text="${country}"></span><br/>
        省份:<span th:text="${province}"></span><br/>
        城市:<span th:text="${city}"></span>

</body>
</html>

3. Embed (custom QR code) custom.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>内嵌(自定义二维码)</title>
</head>
<script src="http://res.wx.qq.com/connect/zh_CN/htmledition/js/wxLogin.js"></script>
<body>

<center><div id="login_container"></div></center>
<script th:inline="javascript">
    var obj = new WxLogin({
    
    
        self_redirect:true,
        id:"login_container",
        appid: [[${
    
    appid}]],
        scope: [[${
    
    scope}]],
        redirect_uri: [[${
    
    redirect_uri}]],
        state: "",
        style: "",
        href: ""
    });
</script>
</body>
</html>

5.Test results:

Use the domain name penetrated by the intranet to access the interface, because it has been mapped to the local project on the public network.

  1. Access the login interface and click the logo icon
    Insert image description here

  2. Scan code test

Insert image description here

  1. After authorization, call the callback method to get the user information and display it on the page.
    Note: openid is the unique ID of the WeChat user.

Insert image description here

6. Additional explanation:

At this point, it’s almost enough to log in to SpringBoot through WeChat without separating the front and back ends. I would like to tell you what to do if the front and back ends are separated?

1. The callback address is usually the homepage of the website, for example: www.abc.com
2. The front-end button generates a QR code through the appid and callback address
3. After the user scans the code for authorization, the WeChat interface will redirect back through the callback address Home page www.abc.com. There will be a parameter named code
4. At this time, after the front-end has the code, it is passed to the back-end interface method, and the back-end obtains user information through the code. Return to the front end

Summed up in one sentence: 1. Generate a QR code on the homepage of www.adc.com, 2. Scan the code to authorize login, 3. Use the code parameters to obtain user information.

There is almost nothing that cannot be simplified if we have a chance to think about it again.

Guess you like

Origin blog.csdn.net/weixin_45377770/article/details/109901312