基于公众号扫码授权登录

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

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

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

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

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

一、申请公众号

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。

在这里插入图片描述
我们可以在微信公众平台接口调试工具 (https://mp.weixin.qq.com/debug/) 中使用测试接口。

例如:获取access_token

在这里插入图片描述

2.2 关注公众测试号

在这里插入图片描述

2.3 配置回调域名

在“网页服务”中找到“网页账号”,修改“网页授权获取用户基本信息”接口的回调域名。
在这里插入图片描述

注意:

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

在这里插入图片描述

接下来就可以开发了。

二、公众号扫码授权登录

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

在这里插入图片描述

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.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.5.12</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.76</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.22</version>
        </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)获取二维码,这里通过工具展示。

在这里插入图片描述
2)用户扫码登录ok。
在这里插入图片描述
3)轮询的登录接口,此时应该响应ok。

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

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

– 求知若饥,虚心若愚。

### 微信公众号登录订阅号实现方法 对于微信公众号中的订阅号而言,要实现在Web端通过描二维完成登录的过程相对复杂一些。由于微信官方并未直接向订阅号开放登录接口,因此通常采用间接的方法来达成这一目标。 #### 利用OAuth2.0授权机制 为了使用户能够安全地通过描二维登录网站,可以借助于微信提供的OAuth2.0授权框架。具体来说: - 用户访问网页时会看到一个由服务器生成并展示出来的唯一二维图片。 - 当用户使用手机上的微信客户端描该二维后,会被重定向到预先设定好的URL地址,在这个过程中包含了用户的同意操作以及授权(code)。 - 接下来,服务端利用获得的`code`参数调用微信API换取`access_token`和`openid`等信息[^4]。 ```javascript // 获取 code 的 URL 构建示例 (Node.js) const querystring = require('querystring'); function getAuthUrl() { const params = { appid: 'YOUR_APP_ID', redirect_uri: encodeURIComponent('REDIRECT_URI'), response_type: 'code', scope: 'snsapi_login', // 或者 snsapi_base 如果只需要 openid state: 'STATE' // 可选状态参数用于保持请求前后上下文一致 }; return `https://open.weixin.qq.com/connect/qrconnect?${querystring.stringify(params)}#wechat_redirect`; } ``` 需要注意的是,上述过程适用于已认证的服务号;而对于未经认证的订阅号,则可能需要采取其他策略,比如引导用户先关注再进行后续的操作[^2]。 另外,考虑到用户体验优化方面的需求,还可以考虑集成第三方组件或库简化开发流程,例如基于Vue.js前端框架配合相应的插件快速构建交互界面,并结合MongoDB存储必要的用户数据以便日后查询分析[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值