PC 微信扫码登陆

1、注册微信开发平台

官网网址 自行注册,需要注意的是注册邮箱号不用与腾讯其他产品同号。

2、微信开发平台添加网站应用

添加网站应用

填写基本信息

填写基本信息2

网站信息登记表模板

网站应用名称不能重复

添加授权回调域-可以修改

以上资料提交之后等待TX审核,审核时间大概2天左右,审核通过之后就可以查看AppID AppSecret以及申请微信登录
注意:如果注册完的开放平台之后有进行开发者认证时,审核通过之后微信登录默认是开通的,如果没有进行开发者认证点击申请开通 时会提醒进行开发者认证(如下图)

审核通过

基本信息、开发信息

提醒进行开发者认证

开发者资质认证

3、PC微信登录流程介绍

网站应用微信登录是基于OAuth2.0协议标准构建的微信OAuth2.0授权登录系统。进一步了解OAuth2.0—–理解OAuth2.0 官方介绍资料

大致的步骤如下:
1. 第三方发起微信授权登录请求,微信用户允许授权第三方应用后,微信会拉起应用或重定向到第三方网站,并且带上授权临时票据code参数;
2. 通过code参数加上AppID和AppSecret等,通过API换取access_token;
3. 通过access_token进行接口调用,获取用户基本数据资源或帮助用户实现基本操作。

获取access_token时序图

具体的步骤如下:

第一步:请求CODE

接口说明
https://open.weixin.qq.com/connect/qrconnect?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect
参数说明

参数是否必须说明
appid应用唯一标识
redirect_uri重定向地址,需要进行UrlEncode
response_type填code
scope应用授权作用域,拥有多个作用域用逗号(,)分隔,网页应用目前仅填写snsapi_login即可
state用于保持请求和回调的状态,授权请求后原样带回给第三方。该参数可用于防止csrf攻击(跨站请求伪造攻击),建议第三方带上该参数,可设置为简单的随机数加session进行校验

若提示“该链接无法访问”,请检查参数是否填写错误,如redirect_uri的域名与审核时填写的授权域名不一致或scope不为snsapi_login。

返回说明
用户允许授权后,将会重定向到redirect_uri的网址上,并且带上code和state参数


redirect_uri?code=CODE&state=STATE


若用户禁止授权,则重定向后不会带上code参数,仅会带上state参数


redirect_uri?state=STATE


接口封装


    private static String qrconnect_url = "https://open.weixin.qq.com/connect/qrconnect";


    /**
     * 生成网页二维码授权链接
     * @param appId 应用id
     * @param redirect_uri 回跳地址
     * @return url
     */
    public static String getQrConnectURL(String appId, String redirect_uri) {
        return getQrConnectURL(appId, redirect_uri, null);
    }

    /**
     * 生成网页二维码授权链接
     * @param appId 应用id
     * @param redirect_uri 回跳地址
     * @param state 重定向后会带上state参数,开发者可以填写a-zA-Z0-9的参数值,最多128字节
     * @return url
     */
    public static String getQrConnectURL(String appId, String redirect_uri, String state) {
        Map<String, String> params = new HashMap<String, String>();
        params.put("appid", appId);
        params.put("response_type", "code");
        params.put("redirect_uri", redirect_uri);
        params.put("scope", "snsapi_login");
        if (StrKit.isBlank(state)) {
            params.put("state", "wx#wechat_redirect");
        } else {
            params.put("state", state.concat("#wechat_redirect"));
        }
        String para = PaymentKit.packageSign(params, false);
        return qrconnect_url + "?" + para;
    }

接口测试

1、设置AppId、APPSecret以及回调域名
2、PC访问测试。开源项目中访问地址为:

http://域名/[项目名称]/toWebOauth

设置AppId、APPSecret以及回调域名


//pc扫描登陆
public void toWebOauth(){
    String calbackUrl=PropKit.get("domain")+"/oauth/webCallBack";
    String url=SnsAccessTokenApi.getQrConnectURL(PropKit.get("webAppId"), calbackUrl,"666");
    redirect(url);
}

如果授权回调域名与开发平台中的填写不一致将会出现以下提示

回调域名与接口调用时填写的域名不一致

第二步:通过code获取access_token
第三步:通过access_token调用接口获取用户信息

第二步、第三步过程与微信公众号授权登录一样可以参考之前写的一篇文章授权获取用户信息

授权后接口调用(UnionID)

获取用户个人信息(UnionID机制)

此接口用于获取用户个人信息。开发者可通过OpenID来获取用户基本信息。特别需要注意的是,如果开发者拥有多个移动应用、网站应用和公众帐号,可通过获取用户基本信息中的unionid来区分用户的唯一性,因为只要是同一个微信开放平台帐号下的移动应用、网站应用和公众帐号,用户的unionid是唯一的。换句话说,同一用户,对同一个微信开放平台下的不同应用,unionid是相同的。请注意,在用户修改微信头像后,旧的微信头像URL将会失效,因此开发者应该自己在获取用户信息后,将头像图片保存下来,避免微信头像URL失效后的异常情况。

官方介绍文档

接口封装可以参考开源项目中

网页授权(公众号、PC)获取用户信息

 params = new HashMap();
        params.put("appid", appId);
        params.put("response_type", "code");
        params.put("redirect_uri", redirectUri);
        // snsapi_base(不弹出授权页面,只能拿到用户openid)
        // snsapi_userinfo(弹出授权页面,这个可以通过 openid 拿到昵称、性别、所在地)
        if (snsapiBase) {
            params.put("scope", "snsapi_base");
        } else {
            params.put("scope", "snsapi_userinfo");
        }
        if (StrKit.isBlank(state)) {
            params.put("state", "wx#wechat_redirect");
        } else {
            params.put("state", state.concat("#wechat_redirect"));
        }
        String para = PaymentKit.packageSign(params, false);
        return authorize_uri + "?" + para;
    }


    /**
     * 生成网页二维码授权链接
     * @param appId 应用id
     * @param redirect_uri 回跳地址
     * @return url
     */
    public static String getQrConnectURL(String appId, String redirect_uri) {
        return getQrConnectURL(appId, redirect_uri, null);
    }

    /**
     * 生成网页二维码授权链接
     * @param appId 应用id
     * @param redirect_uri 回跳地址
     * @param state 重定向后会带上state参数,开发者可以填写a-zA-Z0-9的参数值,最多128字节
     * @return url
     */
    public static String getQrConnectURL(String appId, String redirect_uri, String state) {
        Map params = new HashMap();
        params.put("appid", appId);
        params.put("response_type", "code");
        params.put("redirect_uri", redirect_uri);
        params.put("scope", "snsapi_login");
        if (StrKit.isBlank(state)) {
            params.put("state", "wx#wechat_redirect");
        } else {
            params.put("state", state.concat("#wechat_redirect"));
        }
        String para = PaymentKit.packageSign(params, false);
        return qrconnect_url + "?" + para;
    }

    /**
     * 通过code获取access_token
     *
     * @param code   第一步获取的code参数
     * @param appId  应用唯一标识
     * @param secret 应用密钥AppSecret
     * @return SnsAccessToken
     */
    public static SnsAccessToken getSnsAccessToken(String appId, String secret, String code)
    {
        final Map queryParas = ParaMap.create("appid", appId).put("secret", secret).put("code", code).getData();

        return RetryUtils.retryOnException(3, new Callable() {

            @Override
            public SnsAccessToken call() throws Exception {
                String json = HttpUtils.get(url, queryParas);
                return new SnsAccessToken(json);
            }
        });
    }
}" data-snippet-id="ext.fc4c8e08b8a21ac8b08f430a8f26ad21" data-snippet-saved="false" data-codota-status="done">import com.jfinal.kit.StrKit;
import com.jfinal.weixin.sdk.kit.ParaMap;
import com.jfinal.weixin.sdk.kit.PaymentKit;
import com.jfinal.weixin.sdk.utils.HttpUtils;
import com.jfinal.weixin.sdk.utils.RetryUtils;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Callable;


public class SnsAccessTokenApi
{
    private static String url = "https://api.weixin.qq.com/sns/oauth2/access_token?grant_type=authorization_code";
    private static String authorize_uri = "https://open.weixin.qq.com/connect/oauth2/authorize";
    private static String qrconnect_url = "https://open.weixin.qq.com/connect/qrconnect";

    /**
     * 生成Authorize链接
     * @param appId 应用id
     * @param redirect_uri 回跳地址
     * @param snsapiBase snsapi_base(不弹出授权页面,只能拿到用户openid)snsapi_userinfo(弹出授权页面,这个可以通过 openid 拿到昵称、性别、所在地)
     * @return url
     */
    public static String getAuthorizeURL(String appId, String redirect_uri, boolean snsapiBase) {
        return getAuthorizeURL(appId, redirect_uri, null, snsapiBase);
    }

    /**
     * 生成Authorize链接
     * @param appId 应用id
     * @param redirectUri 回跳地址
     * @param state 重定向后会带上state参数,开发者可以填写a-zA-Z0-9的参数值,最多128字节
     * @param snsapiBase snsapi_base(不弹出授权页面,只能拿到用户openid)snsapi_userinfo(弹出授权页面,这个可以通过 openid 拿到昵称、性别、所在地)
     * @return url
     */
    public static String getAuthorizeURL(String appId, String redirectUri, String state, boolean snsapiBase) {
        Map<String, String> params = new HashMap<String, String>();
        params.put("appid", appId);
        params.put("response_type", "code");
        params.put("redirect_uri", redirectUri);
        // snsapi_base(不弹出授权页面,只能拿到用户openid)
        // snsapi_userinfo(弹出授权页面,这个可以通过 openid 拿到昵称、性别、所在地)
        if (snsapiBase) {
            params.put("scope", "snsapi_base");
        } else {
            params.put("scope", "snsapi_userinfo");
        }
        if (StrKit.isBlank(state)) {
            params.put("state", "wx#wechat_redirect");
        } else {
            params.put("state", state.concat("#wechat_redirect"));
        }
        String para = PaymentKit.packageSign(params, false);
        return authorize_uri + "?" + para;
    }


    /**
     * 生成网页二维码授权链接
     * @param appId 应用id
     * @param redirect_uri 回跳地址
     * @return url
     */
    public static String getQrConnectURL(String appId, String redirect_uri) {
        return getQrConnectURL(appId, redirect_uri, null);
    }

    /**
     * 生成网页二维码授权链接
     * @param appId 应用id
     * @param redirect_uri 回跳地址
     * @param state 重定向后会带上state参数,开发者可以填写a-zA-Z0-9的参数值,最多128字节
     * @return url
     */
    public static String getQrConnectURL(String appId, String redirect_uri, String state) {
        Map<String, String> params = new HashMap<String, String>();
        params.put("appid", appId);
        params.put("response_type", "code");
        params.put("redirect_uri", redirect_uri);
        params.put("scope", "snsapi_login");
        if (StrKit.isBlank(state)) {
            params.put("state", "wx#wechat_redirect");
        } else {
            params.put("state", state.concat("#wechat_redirect"));
        }
        String para = PaymentKit.packageSign(params, false);
        return qrconnect_url + "?" + para;
    }

    /**
     * 通过code获取access_token
     *
     * @param code   第一步获取的code参数
     * @param appId  应用唯一标识
     * @param secret 应用密钥AppSecret
     * @return SnsAccessToken
     */
    public static SnsAccessToken getSnsAccessToken(String appId, String secret, String code)
    {
        final Map<String, String> queryParas = ParaMap.create("appid", appId).put("secret", secret).put("code", code).getData();

        return RetryUtils.retryOnException(3, new Callable<SnsAccessToken>() {

            @Override
            public SnsAccessToken call() throws Exception {
                String json = HttpUtils.get(url, queryParas);
                return new SnsAccessToken(json);
            }
        });
    }
}

用户管理接口,获取用户基本信息(UnionID机制)


 * https://api.weixin.qq.com/cgi-bin/user/info?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN
 * 
*/ public class UserApi { private static String getUserInfo = "https://api.weixin.qq.com/cgi-bin/user/info"; private static String getFollowers = "https://api.weixin.qq.com/cgi-bin/user/get"; private static String batchGetUserInfo = "https://api.weixin.qq.com/cgi-bin/user/info/batchget?access_token="; /** * 获取用户基本信息(包括UnionID机制) * @param openId 普通用户的标识,对当前公众号唯一 * @return ApiResult */ public static ApiResult getUserInfo(String openId) { ParaMap pm = ParaMap.create("access_token", AccessTokenApi.getAccessTokenStr()).put("openid", openId).put("lang", "zh_CN"); return new ApiResult(HttpUtils.get(getUserInfo, pm.getData())); } /** * 获取用户列表 * @param nextOpenid 第一个拉取的OPENID,不填默认从头开始拉取 * @return ApiResult */ public static ApiResult getFollowers(String nextOpenid) { ParaMap pm = ParaMap.create("access_token", AccessTokenApi.getAccessTokenStr()); if (nextOpenid != null) pm.put("next_openid", nextOpenid); return new ApiResult(HttpUtils.get(getFollowers, pm.getData())); } /** * 获取用户列表 * @return ApiResult */ public static ApiResult getFollows() { return getFollowers(null); } /** * 批量获取用户基本信息, by Unas * @param jsonStr json字符串 * @return ApiResult */ public static ApiResult batchGetUserInfo(String jsonStr) { String jsonResult = HttpUtils.post(batchGetUserInfo + AccessTokenApi.getAccessTokenStr(), jsonStr); return new ApiResult(jsonResult); } /** * 批量获取用户基本信息 * @param openIdList openid列表 * @return ApiResult */ public static ApiResult batchGetUserInfo(List openIdList) { Map>> userListMap = new HashMap>>(); List> userList = new ArrayList>(); for (String openId : openIdList) { Map mapData = new HashMap(); mapData.put("openid", openId); mapData.put("lang", "zh_CN"); userList.add(mapData); } userListMap.put("user_list", userList); return batchGetUserInfo(JsonUtils.toJson(userListMap)); } private static String updateRemarkUrl = "https://api.weixin.qq.com/cgi-bin/user/info/updateremark?access_token="; /** * 设置备注名 * @param openid 用户标识 * @param remark 新的备注名,长度必须小于30字符 * @return {ApiResult} */ public static ApiResult updateRemark(String openid, String remark) { String url = updateRemarkUrl + AccessTokenApi.getAccessTokenStr(); Map mapData = new HashMap(); mapData.put("openid", openid); mapData.put("remark", remark); String jsonResult = HttpUtils.post(url, JsonUtils.toJson(mapData)); return new ApiResult(jsonResult); } } " data-snippet-id="ext.08b5e56863fb3ea3d62b4329261fcdd8" data-snippet-saved="false" data-codota-status="done">import com.jfinal.weixin.sdk.kit.ParaMap; import com.jfinal.weixin.sdk.utils.HttpUtils; import com.jfinal.weixin.sdk.utils.JsonUtils; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * 用户管理 API * <pre> * https://api.weixin.qq.com/cgi-bin/user/info?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN * </pre> */ public class UserApi { private static String getUserInfo = "https://api.weixin.qq.com/cgi-bin/user/info"; private static String getFollowers = "https://api.weixin.qq.com/cgi-bin/user/get"; private static String batchGetUserInfo = "https://api.weixin.qq.com/cgi-bin/user/info/batchget?access_token="; /** * 获取用户基本信息(包括UnionID机制) * @param openId 普通用户的标识,对当前公众号唯一 * @return ApiResult */ public static ApiResult getUserInfo(String openId) { ParaMap pm = ParaMap.create("access_token", AccessTokenApi.getAccessTokenStr()).put("openid", openId).put("lang", "zh_CN"); return new ApiResult(HttpUtils.get(getUserInfo, pm.getData())); } /** * 获取用户列表 * @param nextOpenid 第一个拉取的OPENID,不填默认从头开始拉取 * @return ApiResult */ public static ApiResult getFollowers(String nextOpenid) { ParaMap pm = ParaMap.create("access_token", AccessTokenApi.getAccessTokenStr()); if (nextOpenid != null) pm.put("next_openid", nextOpenid); return new ApiResult(HttpUtils.get(getFollowers, pm.getData())); } /** * 获取用户列表 * @return ApiResult */ public static ApiResult getFollows() { return getFollowers(null); } /** * 批量获取用户基本信息, by Unas * @param jsonStr json字符串 * @return ApiResult */ public static ApiResult batchGetUserInfo(String jsonStr) { String jsonResult = HttpUtils.post(batchGetUserInfo + AccessTokenApi.getAccessTokenStr(), jsonStr); return new ApiResult(jsonResult); } /** * 批量获取用户基本信息 * @param openIdList openid列表 * @return ApiResult */ public static ApiResult batchGetUserInfo(List<String> openIdList) { Map<String, List<Map<String, Object>>> userListMap = new HashMap<String, List<Map<String, Object>>>(); List<Map<String, Object>> userList = new ArrayList<Map<String,Object>>(); for (String openId : openIdList) { Map<String, Object> mapData = new HashMap<String, Object>(); mapData.put("openid", openId); mapData.put("lang", "zh_CN"); userList.add(mapData); } userListMap.put("user_list", userList); return batchGetUserInfo(JsonUtils.toJson(userListMap)); } private static String updateRemarkUrl = "https://api.weixin.qq.com/cgi-bin/user/info/updateremark?access_token="; /** * 设置备注名 * @param openid 用户标识 * @param remark 新的备注名,长度必须小于30字符 * @return {ApiResult} */ public static ApiResult updateRemark(String openid, String remark) { String url = updateRemarkUrl + AccessTokenApi.getAccessTokenStr(); Map<String, String> mapData = new HashMap<String, String>(); mapData.put("openid", openid); mapData.put("remark", remark); String jsonResult = HttpUtils.post(url, JsonUtils.toJson(mapData)); return new ApiResult(jsonResult); } }

推荐阅读
Android版-微信APP支付
极速开发微信公众号之微信买单
极速开发微信公众号之公众号支付
极速开发微信公众号之扫码支付
极速开发微信公众号之刷卡支付
极速开发微信公众号之现金红包
[Android版-支付宝APP支付](http://www.jianshu.com/p/3d91248aea4b
)
支付宝Wap支付
一张二维码集成微信、支付宝支付

源码下载地址
记录学习的点滴,以此勉励不断奋斗的自己✌️✌️✌️

<think>好的,我现在需要帮助用户解决在Vue3项目中实现PC微信登录的问题。首先,我应该回顾一下用户提供的参考内容,看看有没有相关的信息。用户提到的引用[1]是关于企业微信登录的教程,虽然用户现在问的是微信登录,但可能有类似的地方,比如OAuth流程、二维生成事件监听等。 接下来,我需要确保回答符合用户的要求:结构清晰,分步骤解决问题,真实可靠。同时,还要遵循系统级指令,比如正确使用LaTeX格式,生成相关问题,添加引用标识等。 首先,我应该分析微信登录的一般流程。通常,这包括获取二维、监听事件、处理回调等步骤。微信开放平台提供了相关的API,需要注册应用并获取AppIDAppSecret。然后,前端生成二维,用户后,后端通过回调验证,前端轮询登录状态。 然后,我需要考虑在Vue3中的具体实现。可能涉及使用第三方库生成二维,比如qrcode.vue,或者直接使用微信的JS库。同时,前端需要轮询后端接口以检查用户是否并确认登录。此外,需要注意跨域问题安全设置,比如回调域名的配置。 接下来,我需要参考用户提供的引用[1],看看企业微信的实现是否有可借鉴之处。比如,如何嵌入二维,如何监听状态,如何处理用户信息获取等。虽然企业微信普通微信的接口可能不同,但整体思路类似,都是生成二维、轮询状态、获取用户信息。 然后,我需要编写示例代。可能需要分步骤说明:配置微信开放平台应用,安装必要的库,创建二维组件,实现轮询逻辑,处理登录后的用户信息存储等。代部分要使用正确的语法,并添加注释说明。 同时,需要提醒用户注意安全性配置细节,比如回调域名的设置,HTTPS的要求,以及存储token的最佳实践。此外,可能还需要提到测试方法,比如使用内网穿透工具进行本地测试。 最后,生成相关问题,帮助用户进一步了解相关主题,比如性能优化、移动端适配、超时处理等。 现在需要检查是否有遗漏的步骤,比如OAuth2.0的授权流程是否正确,是否需要后端配合,以及如何处理可能的错误情况,比如二维过期、用户取消登录等。确保每个步骤都清晰,并且代示例能够运行,或者至少给出正确的框架。 另外,需要确保回答中的LaTeX格式正确,比如行内公式用$...$,独立公式用$$...$$,但在这个问题中可能数学公式较少,但如果有技术术语或变量,可能需要使用。不过目前看来,技术内容可能不需要数学公式,所以可能这部分不需要太多处理,但还是要检查用户的要求。 最后,生成的相关问题需要与主题相关,并且能够引导用户深入了解更多细节,比如第三方登录库的比较、安全措施、移动端适配等。</think>在Vue3中实现PC微信登录功能需要遵循微信开放平台OAuth2.0协议规范[^1],以下是关键步骤示例代: ### 一、准备工作 1. 注册微信开放平台账号并创建网站应用 2. 获取`AppID``AppSecret` 3. 设置授权回调域名(需备案且支持HTTPS) ### 二、核心实现流程 #### 1. 生成登录二维组件 ```vue <template> <div class="qrcode-container"> <img :src="qrCodeUrl" v-if="qrCodeUrl" alt="微信登录" /> <p v-else>加载二维中...</p> </div> </template> <script setup> import { ref, onMounted } from 'vue' import QRCode from 'qrcode' const qrCodeUrl = ref('') const state = ref(Date.now().toString(36)) // 防CSRF令牌 const generateQRCode = async () => { const authUrl = `https://open.weixin.qq.com/connect/qrconnect?appid=${YOUR_APPID}&redirect_uri=${encodeURIComponent(YOUR_CALLBACK_URL)}&response_type=code&scope=snsapi_login&state=${state.value}#wechat_redirect` try { qrCodeUrl.value = await QRCode.toDataURL(authUrl) } catch (err) { console.error('二维生成失败:', err) } } onMounted(() => { generateQRCode() }) </script> ``` #### 2. 状态轮询机制 ```javascript const checkLoginStatus = () => { let retryCount = 0 const maxRetries = 30 // 30次*2秒=1分钟超时 const poll = setInterval(async () => { if (retryCount++ >= maxRetries) { clearInterval(poll) return } try { const response = await axios.get('/api/check-login', { params: { state: state.value } }) if (response.data.code === 200) { clearInterval(poll) handleLoginSuccess(response.data.userInfo) } } catch (error) { console.error('状态检查失败:', error) } }, 2000) } ``` ### 三、后端交互要点 1. 接收微信回调处理: ```javascript router.get('/wx/callback', async (ctx) => { const { code, state } = ctx.query // 验证state有效性 // 通过code获取access_token const tokenRes = await axios.get(`https://api.weixin.qq.com/sns/oauth2/access_token?appid=${APPID}&secret=${APPSECRET}&code=${code}&grant_type=authorization_code`) // 获取用户信息 const userRes = await axios.get(`https://api.weixin.qq.com/sns/userinfo?access_token=${tokenRes.access_token}&openid=${tokenRes.openid}`) // 存储会话状态 sessionStorage.setItem(state, JSON.stringify(userRes.data)) ctx.redirect('/login-success') }) ``` ### 四、安全注意事项 1. 必须使用HTTPS协议保障通信安全 2. `state`参数需要服务端验证防止CSRF攻击 3. 敏感数据应加密存储,推荐使用`Web Crypto API`处理 4. 设置合理的二维过期时间(建议2分钟)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值