基于公众号扫码授权登录

微信扫码登录大体上有两种实现方式:

  • 一种是基于微信开放平台的扫码登录
  • 一种是基于微信公众平台的扫码登录

注意:这两个平台的扫码登录一定要区分开,这两者授权登录是不一样的。

  • 微信开放平台需要企业认证才能注册。
  • 微信公众平台需要认证微信服务号,授权只能在微信客户端中使用(关注公众号),才能进行扫码登录的开发。

下面我们使用基于微信公众平台的扫码登录。

一、申请公众号

1、申请公众号

查看官方文档:https://developers.weixin.qq.com/doc/offiaccount/Getting_Started/Getting_Started_Guide.html

由于我是个人开发者,注册申请一个微信公众平台的测试号就可以了。

2、申请公众测试号

微信公众平台接口测试帐号申请:https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login

2.1 申请公众测试号

使用微信扫码登录后,就可以拿到 appID 和 appsecret。

基于公众号扫码授权登录_第1张图片
我们可以在微信公众平台接口调试工具 (https://mp.weixin.qq.com/debug/) 中使用测试接口。

例如:获取access_token

基于公众号扫码授权登录_第2张图片

2.2 关注公众测试号

基于公众号扫码授权登录_第3张图片

2.3 配置回调域名

在“网页服务”中找到“网页账号”,修改“网页授权获取用户基本信息”接口的回调域名。
基于公众号扫码授权登录_第4张图片

注意:

  • 配置网页授权的回调域名,不用填写完整的回调地址,只需要填写域名即可,回调地址在回调域名之下就ok。
  • 测试我通过内网穿透(使用 natapp工具)将本地服务映射到外网,让微信服务可以访问你本地电脑的服务。
  • 线上生产时,需要将官方 xxx.txt文件放到服务器根路径下,域名需要备案,外网能访问接口保存ok。

基于公众号扫码授权登录_第5张图片

接下来就可以开发了。

二、公众号扫码授权登录

微信授权登录的官方文档:https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html

基于公众号扫码授权登录_第6张图片

1、设计思路

1)页面授权流程相关接口如下:

  1. 获取二维码,二维码文本数据推荐后端生成。
  2. 用户扫码,如果用户同意授权,页面将跳转至 redirect_uri/?code=CODE&state=STATE。
  3. 通过 code 获取微信接口的调用凭证 access_token
  4. 通过 access_token授权凭证获取微信用户信息

2)我们PC端使用微信扫码登陆的设计思路

  1. PC前端调用后端获取二维码文本数据,展示二维码。
  2. 用户扫码,如果用户同意授权,页面将跳转至 redirect_uri/?code=CODE&state=STATE。
    redirect_uri,我们指向前端,然后前端调用后端获取微信用户接口,若接口响应成功,微信手机端关闭页面,回到聊天窗口。
  3. 在第 1 步展示二维码之后,PC前端轮询调用后端微信登录接口,若接口响应成功,则微信扫码登录成功。

注意:

  • 后端获取微信用户接口:一般我们建立微信用户和PC端用户的绑定关系。
  • 后端微信登录接口:一般是监听 后端获取微信用户接口的绑定关系是否触发,即微信用户是否扫码授权登录成功。

为了方便,我们关注PC后端业务实现,简化上面设计思路,将 redirect_uri 直接指向 后端获取微信用户接口。

2、代码实现

创建 springboot项目,引入依赖。

        <dependency>
            <groupId>org.apache.httpcomponentsgroupId>
            <artifactId>httpclientartifactId>
            <version>4.5.12version>
        dependency>

        <dependency>
            <groupId>com.alibabagroupId>
            <artifactId>fastjsonartifactId>
            <version>1.2.76version>
        dependency>
        <dependency>
            <groupId>org.projectlombokgroupId>
            <artifactId>lombokartifactId>
            <version>1.18.22version>
        dependency>

2.1 controller

@RestController
public class WeiXinPublicAccountController {

    @Autowired
    private WinXinPublicAccountService winXinPublicAccountService;

    /**
     * 获取授权url接口
     */
    @RequestMapping("/getAuthUrl")
    public String getAuthUrl(HttpSession session) throws Exception {
        // 用于第三方应用防止CSRF攻击
        String uuid = UUID.randomUUID().toString().replaceAll("-", "");
        // 临时放到session。开发是放到Redis
        session.setAttribute("state", uuid);

        return winXinPublicAccountService.getAuthUrl(uuid);
    }

    /**
     * 授权回调接口
     */
    @GetMapping(value = "/callback")
    public String callback(HttpServletRequest request) throws Exception {
        HttpSession session = request.getSession();
        // 得到Authorization Code
        String code = request.getParameter("code");
        // 我们放在地址中的状态码
        String state = request.getParameter("state");
        String uuid = (String) session.getAttribute("state");

        // 验证我们发送的状态码
        //if(uuid == null || !uuid.equals(state)){
        //    return "参数为空或者状态码已过期,状态码不正确" ;
        //}

        // 建立微信用户和PC端用户的绑定关系
        String accessToken = winXinPublicAccountService.getAccessToken(code);

        /**
         * TODO 将state与微信用户openid建立关联,放到Redis
         */
        return accessToken;
    }


    /**
     * 微信登录接口(轮询)
     */
    @GetMapping(value = "/weixinLogin")
    public String weixinLogin(HttpServletRequest request) throws Exception {
        HttpSession session = request.getSession();
        // 我们放在地址中的状态码
        String state = request.getParameter("state");
        String uuid = (String) session.getAttribute("state");

        // 验证我们发送的状态码
        //if(uuid == null || !uuid.equals(state)){
        //    return "参数为空或者状态码已过期,状态码不正确" ;
        //}
        /**
         * 1、TODO 通过state获取微信用户openid,从而获取都PC端用户信息
         * 2、TODO 通过PC端用户信息登录并保存到Session,最后返回登录后的信息, 即与我们通过账号登录校验用户信息之后的的登录逻辑同理。
         */

        return "微信扫码登录成功";
    }

}

2.2 service

@Service
public class WinXinPublicAccountService {
    /**
     * 公众平台提供的 appid 和 appsecret
     */
    public String APPID = "wx2a8f0xxx";
    public String APPSECRET = "afd4b135xxxxxxxxx";

    /**
     * 我们自定义认证重定向url
     */
    public static final String AUTH_REDIRECT_URI = "http://qy7cbq.natappfree.cc/weixin/callback";


    public String getAuthUrl(String state){
        // 获取用户认证授权URL,来获取Authorization Code
        String oauthUrl = WeiXinConstant.AUTH_URL
                .replace(WeiXinConstant.APPID, APPID)
                .replace(WeiXinConstant.REDIRECT_URI, URLEncoder.encode(AUTH_REDIRECT_URI, StandardCharsets.UTF_8))
                .replace(WeiXinConstant.SCOPE, WeiXinConstant.SNSAPI_USERINFO)
                .replace(WeiXinConstant.STATE, state);
        System.out.println(oauthUrl);
        return oauthUrl;
    }

    public String getAccessToken(String code){
        //通过Code获取Access Token
        String getAccessTokenUrl = WeiXinConstant.GET_USER_ACCESS_TOKEN_URL
                .replace(WeiXinConstant.APPID, APPID)
                .replace(WeiXinConstant.SECRET, APPSECRET)
                .replace(WeiXinConstant.CODE, code);
        JSONObject resJson = null;
        try {
            resJson = HttpRequestUtils.httpRequestGet(getAccessTokenUrl);
        } catch (IOException e) {
            System.out.println("获取 accessToken 异常, e=" + e);
            return "获取 accessToken 异常";
        }
        System.out.println("获取 access_token接口成功,resJson=" + resJson);
        String accessToken = resJson.getString("access_token");
        String openId = resJson.getString("openid");
        if (StringUtils.isBlank(accessToken) || StringUtils.isBlank(openId)) {
            return "获取 accessToken 为空";
        }
        //测试获取用户信息
        getUserInfo(accessToken, openId);
        return resJson.toString();
    }

    public String getUserInfo(String accessToken, String openId){
        // 根据openid和 access_token获取用户信息
        String getUserInfoUrl = WeiXinConstant.GET_USER_INFO_URL
                .replace(WeiXinConstant.ACCESS_TOKEN, accessToken)
                .replace(WeiXinConstant.OPENID, openId);
        JSONObject resJson = null;
        try {
            resJson = HttpRequestUtils.httpRequestGet(getUserInfoUrl);
        } catch (IOException e) {
            System.out.println("获取 UserInfo 异常, e=" + e);
            return "获取 UserInfo 异常";
        }
        System.out.println("获取 UserInfo 接口成功,resJson=" + resJson);
        /**
         * TODO 这时就该写自己的业务逻辑了
         */
        return resJson.toString();
    }
}

微信平台常量类:

/**
 * 微信平台常量
 */
public class WeiXinConstant {

    /**
     * 公众号-服务号URL
     */
    public static String APPID = "APPID";
    public static String SECRET = "SECRET";
    public static String REDIRECT_URI = "REDIRECT_URI";
    public static String SCOPE = "SCOPE";
    public static String STATE = "STATE";
    public static String CODE = "CODE";
    public static String ACCESS_TOKEN = "ACCESS_TOKEN";
    public static String OPENID = "OPENID";
    public static String SNSAPI_BASE = "snsapi_base";
    public static String SNSAPI_USERINFO = "snsapi_userinfo";

    /**
     * 获取用户认证授权URL
     */
    public static final String AUTH_URL = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect";

    /**
     * 获取用户access_token和openid信息URL
     */
    public static final String GET_USER_ACCESS_TOKEN_URL = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code";

    /**
     * 根据openid和 access_token获取用户信息URL
     */
    public static final String GET_USER_INFO_URL = "https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN";
    

}

HttpRequestUtils工具类:

public class HttpRequestUtils {

    /**
     * GET 请求
     */
    public static JSONObject httpRequestGet(String url) throws IOException {
        CloseableHttpClient client = HttpClients.createDefault();
        HttpGet httpGet = new HttpGet(url);
        HttpResponse response = client.execute(httpGet);
        HttpEntity entity = response.getEntity();
        if (entity != null) {
            String result = EntityUtils.toString(entity, "UTF-8");
            return JSONObject.parseObject(result);
        }
        httpGet.releaseConnection();
        return null;
    }

}

2.3 测试

启动项目.

1)获取二维码,这里通过工具展示。

基于公众号扫码授权登录_第7张图片
2)用户扫码登录ok。
基于公众号扫码授权登录_第8张图片
3)轮询的登录接口,此时应该响应ok。

到此,基于公众号扫码授权登录就搞定了。

根据设计思路,实现代码比较简陋,实际开发中可根据需求做响应调整。

– 求知若饥,虚心若愚。

你可能感兴趣的:(微信,基于公众号扫码授权登录)