• 本文介绍如何获得微信公众平台关注用户基本信息,包括昵称、头像、性别、国家、省份、城市、语言。 本文的方法将囊括订阅号和服务号以及自定义菜单各种场景,无论是否有高级接口权限,都有办法来获得用户基本信息...


                                微信开发笔记-(2)获取用户基本信息


    文章内容摘自微信|公众平台开发者文档


    在关注者与公众号产生消息交互后,公众号可获得关注者的OpenID(加密后的微信号,每个用户对每个公众号的OpenID是唯一的。对于不同公众号,同一用户的openid不同)。公众号可通过本接口来根据OpenID获取用户基本信息,包括昵称、头像、性别、所在城市、语言和关注时间。

    请注意,如果开发者有在多个公众号,或在公众号、移动应用之间统一用户帐号的需求,需要前往微信开放平台(open.weixin.qq.com)绑定公众号后,才可利用UnionID机制来满足上述需求。

    UnionID机制说明:

    开发者可通过OpenID来获取用户基本信息。特别需要注意的是,如果开发者拥有多个移动应用、网站应用和公众帐号,可通过获取用户基本信息中的unionid来区分用户的唯一性,因为只要是同一个微信开放平台帐号下的移动应用、网站应用和公众帐号,用户的unionid是唯一的。换句话说,同一用户,对同一个微信开放平台下的不同应用,unionid是相同的。
    

    获取用户基本信息(包括UnionID机制)

    开发者可通过OpenID来获取用户基本信息。请使用https协议。

    接口调用请求说明

    http请求方式: GET
    https://api.weixin.qq.com/cgi-bin/user/info?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN
    

    参数说明

    参数 是否必须 说明
    access_token 调用接口凭证
    openid 普通用户的标识,对当前公众号唯一
    lang 返回国家地区语言版本,zh_CN 简体,zh_TW 繁体,en 英语

    返回说明

    正常情况下,微信会返回下述JSON数据包给公众号:

    {
        "subscribe": 1, 
        "openid": "o6_bmjrPTlm6_2sgVt7hMZOPfL2M", 
        "nickname": "Band", 
        "sex": 1, 
        "language": "zh_CN", 
        "city": "广州", 
        "province": "广东", 
        "country": "中国", 
        "headimgurl":    "http://wx.qlogo.cn/mmopen/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4eMsv84eavHiaiceqxibJxCfHe/0", 
       "subscribe_time": 1382694957,
       "unionid": " o6_bmasdasdsad6_2sgVt7hMZOPfL"
       "remark": "",
       "groupid": 0
    }
    

    参数说明

    参数 说明
    subscribe 用户是否订阅该公众号标识,值为0时,代表此用户没有关注该公众号,拉取不到其余信息。
    openid 用户的标识,对当前公众号唯一
    nickname 用户的昵称
    sex 用户的性别,值为1时是男性,值为2时是女性,值为0时是未知
    city 用户所在城市
    country 用户所在国家
    province 用户所在省份
    language 用户的语言,简体中文为zh_CN
    headimgurl 用户头像,最后一个数值代表正方形头像大小(有0、46、64、96、132数值可选,0代表640*640正方形头像),用户没有头像时该项为空。若用户更换头像,原有头像URL将失效。
    subscribe_time 用户关注时间,为时间戳。如果用户曾多次关注,则取最后关注时间
    unionid 只有在用户将公众号绑定到微信开放平台帐号后,才会出现该字段。详见:获取用户个人信息(UnionID机制)
    remark 公众号运营者对粉丝的备注,公众号运营者可在微信公众平台用户管理界面对粉丝添加备注
    groupid 用户所在的分组ID

    错误时微信会返回错误码等信息,JSON数据包示例如下(该示例为AppID无效错误):

    {"errcode":40013,"errmsg":"invalid appid"}
    

    全局返回码说明

    使用网页调试工具调试该接口

    批量获取用户基本信息

    开发者可通过该接口来批量获取用户基本信息。最多支持一次拉取100条。

    接口调用请求说明

    http请求方式: POST
    https://api.weixin.qq.com/cgi-bin/user/info/batchget?access_token=ACCESS_TOKEN
    

    POST数据示例

    {
        "user_list": [
            {
                "openid": "otvxTs4dckWG7imySrJd6jSi0CWE", 
                "lang": "zh-CN"
            }, 
            {
                "openid": "otvxTs_JZ6SEiP0imdhpi50fuSZg", 
                "lang": "zh-CN"
            }
        ]
    }
    

    参数说明

    参数 是否必须 说明
    openid 用户的标识,对当前公众号唯一
    lang 国家地区语言版本,zh_CN 简体,zh_TW 繁体,en 英语,默认为zh-CN

    返回说明

    正常情况下,微信会返回下述JSON数据包给公众号(示例中为一次性拉取了2个openid的用户基本信息,第一个是已关注的,第二个是未关注的):

    {
       "user_info_list": [
           {
               "subscribe": 1, 
               "openid": "otvxTs4dckWG7imySrJd6jSi0CWE", 
               "nickname": "iWithery", 
               "sex": 1, 
               "language": "zh_CN", 
               "city": "Jieyang", 
               "province": "Guangdong", 
               "country": "China", 
               "headimgurl": "http://wx.qlogo.cn/mmopen/xbIQx1GRqdvyqkMMhEaGOX802l1CyqMJNgUzKP8MeAeHFicRDSnZH7FY4XB7p8XHXIf6uJA2SCunTPicGKezDC4saKISzRj3nz/0", 
               "subscribe_time": 1434093047, 
               "unionid": "oR5GjjgEhCMJFyzaVZdrxZ2zRRF4", 
               "remark": "", 
               "groupid": 0
           }, 
           {
               "subscribe": 0, 
               "openid": "otvxTs_JZ6SEiP0imdhpi50fuSZg", 
               "unionid": "oR5GjjjrbqBZbrnPwwmSxFukE41U", 
           }
       ]
    }
    

    参数说明

    参数 说明
    subscribe 用户是否订阅该公众号标识,值为0时,代表此用户没有关注该公众号,拉取不到其余信息,只有openid和UnionID(在该公众号绑定到了微信开放平台账号时才有)。
    openid 用户的标识,对当前公众号唯一
    nickname 用户的昵称
    sex 用户的性别,值为1时是男性,值为2时是女性,值为0时是未知
    city 用户所在城市
    country 用户所在国家
    province 用户所在省份
    language 用户的语言,简体中文为zh_CN
    headimgurl 用户头像,最后一个数值代表正方形头像大小(有0、46、64、96、132数值可选,0代表640*640正方形头像),用户没有头像时该项为空。若用户更换头像,原有头像URL将失效。
    subscribe_time 用户关注时间,为时间戳。如果用户曾多次关注,则取最后关注时间
    unionid 只有在用户将公众号绑定到微信开放平台帐号后,才会出现该字段。详见:获取用户个人信息(UnionID机制)
    remark 公众号运营者对粉丝的备注,公众号运营者可在微信公众平台用户管理界面对粉丝添加备注
    groupid 用户所在的分组ID

    错误时微信会返回错误码等信息,JSON数据包示例如下(该示例为AppID无效错误):

    {"errcode":40013,"errmsg":"invalid appid"}
    


    全局返回码说明


    返回码 说明
    -1 系统繁忙,此时请开发者稍候再试
    0 请求成功
    40001 获取access_token时AppSecret错误,或者access_token无效。请开发者认真比对AppSecret的正确性,或查看是否正在为恰当的公众号调用接口
    40002 不合法的凭证类型
    40003 不合法的OpenID,请开发者确认OpenID(该用户)是否已关注公众号,或是否是其他公众号的OpenID
    40004 不合法的媒体文件类型
    40005 不合法的文件类型
    40006 不合法的文件大小
    40007 不合法的媒体文件id
    40008 不合法的消息类型
    40009 不合法的图片文件大小
    40010 不合法的语音文件大小
    40011 不合法的视频文件大小
    40012 不合法的缩略图文件大小
    40013 不合法的AppID,请开发者检查AppID的正确性,避免异常字符,注意大小写
    40014 不合法的access_token,请开发者认真比对access_token的有效性(如是否过期),或查看是否正在为恰当的公众号调用接口
    40015 不合法的菜单类型
    40016 不合法的按钮个数
    40017 不合法的按钮个数
    40018 不合法的按钮名字长度
    40019 不合法的按钮KEY长度
    40020 不合法的按钮URL长度
    40021 不合法的菜单版本号
    40022 不合法的子菜单级数
    40023 不合法的子菜单按钮个数
    40024 不合法的子菜单按钮类型
    40025 不合法的子菜单按钮名字长度
    40026 不合法的子菜单按钮KEY长度
    40027 不合法的子菜单按钮URL长度
    40028 不合法的自定义菜单使用用户
    40029 不合法的oauth_code
    40030 不合法的refresh_token
    40031 不合法的openid列表
    40032 不合法的openid列表长度
    40033 不合法的请求字符,不能包含\uxxxx格式的字符
    40035 不合法的参数
    40038 不合法的请求格式
    40039 不合法的URL长度
    40050 不合法的分组id
    40051 分组名字不合法
    40117 分组名字不合法
    40118 media_id大小不合法
    40119 button类型错误
    40120 button类型错误
    40121 不合法的media_id类型
    40132 微信号不合法
    40137 不支持的图片格式
    41001 缺少access_token参数
    41002 缺少appid参数
    41003 缺少refresh_token参数
    41004 缺少secret参数
    41005 缺少多媒体文件数据
    41006 缺少media_id参数
    41007 缺少子菜单数据
    41008 缺少oauth code
    41009 缺少openid
    42001 access_token超时,请检查access_token的有效期,请参考基础支持-获取access_token中,对access_token的详细机制说明
    42002 refresh_token超时
    42003 oauth_code超时
    43001 需要GET请求
    43002 需要POST请求
    43003 需要HTTPS请求
    43004 需要接收者关注
    43005 需要好友关系
    44001 多媒体文件为空
    44002 POST的数据包为空
    44003 图文消息内容为空
    44004 文本消息内容为空
    45001 多媒体文件大小超过限制
    45002 消息内容超过限制
    45003 标题字段超过限制
    45004 描述字段超过限制
    45005 链接字段超过限制
    45006 图片链接字段超过限制
    45007 语音播放时间超过限制
    45008 图文消息超过限制
    45009 接口调用超过限制
    45010 创建菜单个数超过限制
    45015 回复时间超过限制
    45016 系统分组,不允许修改
    45017 分组名字过长
    45018 分组数量超过上限
    46001 不存在媒体数据
    46002 不存在的菜单版本
    46003 不存在的菜单数据
    46004 不存在的用户
    47001 解析JSON/XML内容错误
    48001 api功能未授权,请确认公众号已获得该接口,可以在公众平台官网-开发者中心页中查看接口权限
    50001 用户未授权该api
    50002 用户受限,可能是违规后接口被封禁
    61451 参数错误(invalid parameter)
    61452 无效客服账号(invalid kf_account)
    61453 客服帐号已存在(kf_account exsited)
    61454 客服帐号名长度超过限制(仅允许10个英文字符,不包括@及@后的公众号的微信号)(invalid kf_acount length)
    61455 客服帐号名包含非法字符(仅允许英文+数字)(illegal character in kf_account)
    61456 客服帐号个数超过限制(10个客服账号)(kf_account count exceeded)
    61457 无效头像文件类型(invalid file type)
    61450 系统错误(system error)
    61500 日期格式错误
    61501 日期范围错误
    9001001 POST数据参数不合法
    9001002 远端服务不可用
    9001003 Ticket不合法
    9001004 获取摇周边用户信息失败
    9001005 获取商户信息失败
    9001006 获取OpenID失败
    9001007 上传文件缺失
    9001008 上传素材的文件类型不合法
    9001009 上传素材的文件尺寸不合法
    9001010 上传失败
    9001020 帐号不合法
    9001021 已有设备激活率低于50%,不能新增设备
    9001022 设备申请数不合法,必须为大于0的数字
    9001023 已存在审核中的设备ID申请
    9001024 一次查询设备ID数量不能超过50
    9001025 设备ID不合法
    9001026 页面ID不合法
    9001027 页面参数不合法
    9001028 一次删除页面ID数量不能超过10
    9001029 页面已应用在设备中,请先解除应用关系再删除
    9001030 一次查询页面ID数量不能超过50
    9001031 时间区间不合法
    9001032 保存设备与页面的绑定关系参数错误
    9001033 门店ID不合法
    9001034 设备备注信息过长
    9001035 设备申请参数不合法
    9001036 查询起始值begin不合法


    展开全文
  • 如果用户微信客户端中访问第三方网页,公众号可以通过微信网页授权机制,来获取用户基本信息,进而实现业务逻辑。 现在,我们要实现一个微信内网页,通过微信访问网页时,网页会展示微信用户的个人信息。因为涉及...

    说明:该篇博客是博主一字一码编写的,实属不易,请尊重原创,谢谢大家!

    一丶概述

    • 微信网页授权

    如果用户在微信客户端中访问第三方网页,公众号可以通过微信网页授权机制,来获取用户基本信息,进而实现业务逻辑。

    现在,我们要实现一个微信内网页,通过微信访问网页时,网页会展示微信用户的个人信息。因为涉及到用户的个人信息,所以需要有用户授权才可以。当用户授权后,我们的网页服务器(开发者服务器)会拿到用户的“授权书”(code),我们用这个code向微信服务器领取访问令牌(accecc_token)和用户的身份号码(openid),然后凭借access_token和openid向微信服务器提取用户的个人信息。

    1. 第一步:用户同意授权,获取code
    2. 第二步:通过code换取网页授权access_token
    3. 第三步:拉取用户信息(需scope为 snsapi_userinfo)

    那么,如何拿到用户的授权code呢?

    授权是由微信发起让用户进行确认,在这个过程中是微信在与用户进行交互,所以用户应该先访问微信的内容,用户确认后再由微信将用户导向到我们的网页链接地址,并携带上code参数。我们把这个过程叫做网页回调,类似于我们在程序编写时用到的回调函数,都是回调的思想。

    • 关于网页授权回调域名的说明 

    1、在微信公众号请求用户网页授权之前,开发者需要先到公众平台官网中的“开发 - 接口权限 - 网页服务 - 网页帐号 - 网页授权获取用户基本信息”的配置选项中,修改授权回调域名。请注意,这里填写的是域名(是一个字符串),而不是URL,因此请勿加 http:// 等协议头;关于网页授权回调域名的说明

    2、授权回调域名配置规范为全域名,比如需要网页授权的域名为:www.qq.com,配置以后此域名下面的页面http://www.qq.com/music.html 、 http://www.qq.com/login.html 都可以进行OAuth2.0鉴权。但http://pay.qq.com 、 http://music.qq.com 、 http://qq.com无法进行OAuth2.0鉴权

    3、如果公众号登录授权给了第三方开发者来进行管理,则不必做任何设置,由第三方代替公众号实现网页授权即可。

    第一步:用户同意授权,获取code

    在确保微信公众账号拥有授权作用域(scope参数)的权限的前提下(服务号获得高级接口后,默认拥有scope参数中的snsapi_base和snsapi_userinfo),引导关注者打开如下页面:

    https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect

    尤其注意:由于授权操作安全等级较高,所以在发起授权请求时,微信会对授权链接做正则强匹配校验,如果链接的参数顺序不对,授权页面将无法正常访问

    参考链接(请在微信客户端中打开此链接体验):
    scope为snsapi_base
    https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx520c15f417810387&redirect_uri=https%3A%2F%2Fchong.qq.com%2Fphp%2Findex.php%3Fd%3D%26c%3DwxAdapter%26m%3DmobileDeal%26showwxpaytitle%3D1%26vb2ctag%3D4_2030_5_1194_60&response_type=code&scope=snsapi_base&state=123#wechat_redirect
    scope为snsapi_userinfo
    https://open.weixin.qq.com/connect/oauth2/authorize?appid=wxf0e81c3bee622d60&redirect_uri=http%3A%2F%2Fnba.bluewebgame.com%2Foauth_response.php&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirec

    尤其注意:跳转回调redirect_uri,应当使用https链接来确保授权code的安全性。

    参数说明

    参数 是否必须 说明
    appid 公众号的唯一标识
    redirect_uri 授权后重定向的回调链接地址, 请使用 urlEncode 对链接进行处理
    response_type 返回类型,请填写code
    scope 应用授权作用域,snsapi_base (不弹出授权页面,直接跳转,只能获取用户openid),snsapi_userinfo (弹出授权页面,可通过openid拿到昵称、性别、所在地。并且, 即使在未关注的情况下,只要用户授权,也能获取其信息 )
    state 重定向后会带上state参数,开发者可以填写a-zA-Z0-9的参数值,最多128字节
    #wechat_redirect 无论直接打开还是做页面302重定向时候,必须带此参数

     下图为scope等于snsapi_userinfo时的授权页面:

    用户同意授权后

    如果用户同意授权,页面将跳转至 redirect_uri/?code=CODE&state=STATE。

    code说明 : code作为换取access_token的票据,每次用户授权带上的code将不一样,code只能使用一次,5分钟未被使用自动过期。 

    第二步:通过code换取网页授权access_token

    首先请注意,这里通过code换取的是一个特殊的网页授权access_token,与基础支持中的access_token(该access_token用于调用其他接口)不同。公众号可通过下述接口来获取网页授权access_token。如果网页授权的作用域为snsapi_base,则本步骤中获取到网页授权access_token的同时,也获取到了openid,snsapi_base式的网页授权流程即到此为止。

    尤其注意:由于公众号的secret和获取到的access_token安全级别都非常高,必须只保存在服务器,不允许传给客户端。后续刷新access_token、通过access_token获取用户信息等步骤,也必须从服务器发起。

    请求方法

    获取code后,请求以下链接获取access_token:  https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code

    参数说明

    参数 是否必须 说明
    appid 公众号的唯一标识
    secret 公众号的appsecret
    code 填写第一步获取的code参数
    grant_type 填写为authorization_code

    返回说明

    正确时返回的JSON数据包如下:

    { "access_token":"ACCESS_TOKEN",
    "expires_in":7200,
    "refresh_token":"REFRESH_TOKEN",
    "openid":"OPENID",
    "scope":"SCOPE" }
    参数 描述
    access_token 网页授权接口调用凭证,注意:此access_token与基础支持的access_token不同
    expires_in access_token接口调用凭证超时时间,单位(秒)
    refresh_token 用户刷新access_token
    openid 用户唯一标识,请注意,在未关注公众号时,用户访问公众号的网页,也会产生一个用户和公众号唯一的OpenID
    scope 用户授权的作用域,使用逗号(,)分隔

     

    错误时微信会返回JSON数据包如下(示例为Code无效错误):

    {"errcode":40029,"errmsg":"invalid code"}

     第三步:拉取用户信息(需scope为 snsapi_userinfo)

    如果网页授权作用域为snsapi_userinfo,则此时开发者可以通过access_token和openid拉取用户信息了。

    请求方法

    http:GET(请使用https协议) https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN

    参数说明

    参数 描述
    access_token 网页授权接口调用凭证,注意:此access_token与基础支持的access_token不同
    openid 用户的唯一标识
    lang 返回国家地区语言版本,zh_CN 简体,zh_TW 繁体,en 英语

    返回说明

    正确时返回的JSON数据包如下:

    {    "openid":" OPENID",
    " nickname": NICKNAME,
    "sex":"1",
    "province":"PROVINCE"
    "city":"CITY",
    "country":"COUNTRY",
    "headimgurl":    "http://thirdwx.qlogo.cn/mmopen/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4eMsv84eavHiaiceqxibJxCfHe/46",
    "privilege":[ "PRIVILEGE1" "PRIVILEGE2"     ],
    "unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL"
    }
    参数 描述
    openid 用户的唯一标识
    nickname 用户昵称
    sex 用户的性别,值为1时是男性,值为2时是女性,值为0时是未知
    province 用户个人资料填写的省份
    city 普通用户个人资料填写的城市
    country 国家,如中国为CN
    headimgurl 用户头像,最后一个数值代表正方形头像大小(有0、46、64、96、132数值可选,0代表640*640正方形头像),用户没有头像时该项为空。若用户更换头像,原有头像URL将失效。
    privilege 用户特权信息,json 数组,如微信沃卡用户为(chinaunicom)
    unionid 只有在用户将公众号绑定到微信开放平台帐号后,才会出现该字段。

    错误时微信会返回JSON数据包如下(示例为openid无效):

    {"errcode":40003,"errmsg":" invalid openid "}

     二丶代码实现

    • 思路分析
    • 首选在我们的flask程序中需要定义一个视图函数路由规则为/wechat8007/index,定义微信服务器重定向网址redirect_uri为服务器域名+/wechat8007/index(例如http://www.xxxx.com/wechat8007/index),通过访问微信提供的引导页面,让用户同意授权,然后重定向到我们定义的网址,此时微信服务器就会给我们的服务一个code,我们的服务器再通过code向微信服务器换取网页授权access_token(存取令牌),如果网页授权作用域为snsapi_userinfo,则此时可以通过access_token和openid拉取用户信息了。
    • step1 同意授权,这一块不需要代码实现,只需要提供授权链接即可
    • step2 定义视图函数,当用户同意授权,页面将跳转至 redirect_uri/?code=CODE&state=STATE ,在flask程序中定义一个是视图函数接口index,让用户同意授权后,去访问的视图
    • 上一篇博客定义的wechat视图,是由微信服务器访问,现在定义的index视图为用户访问的
    @app.route("/wechat8007/index")
    def index():
        """让用户通过微信访问的网页页面视图"""
    • step3  刚开始还没拉去用户资料时,可直接返回一个模板
    return render_template("index.html")
    • step4 从微信服务器中获取用户的资料数据,将用户的资料数据填充到index.html模板中
    • 1.获取code参数
    code = request.args.get("code")
    • 2.当code不存在时,返回字符串
    if not code:
        return u"缺失code参数"
    url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code" %(WECHAT_APPID,WECHAT_APPSECRET,code)
    
    response = urllib2.urlopen(url)
    
    # 获取响应体数据,微信返回的json数据
    json_str = response.read()
    resp_dict = json.loads(json_str)
    • 4.提取access_token,首先对获取到的响应体数据进行判断,如果不存在,直接返回提示字符串,存在则通过get方式拿去字典中的access_token键的值以及用户编号openid的值
    if "errcode" in resp_dict:
        return u"获取access_token失败"
    
    access_token = resp_dict.get("access_token")
    open_id = resp_dict.get("openid")  # 用户的编号
    • step5 向微信服务器发送http请求,获取用户的资料数据 
    url = "https://api.weixin.qq.com/sns/userinfo?access_token=%s&openid=%s&lang=zh_CN" %(access_token,open_id)
    
    response = urllib2.urlopen(url)
    
    # 读取微信传回的json的响应体数据
    user_json_str = response.read()
    user_dict_data = json.loads(user_json_str)
    • step6 判断微信返回的响应体数据中是否有errorcode字段,如果存在则返回失败信息,不存在说明微信返回的json数据为正确数据,则将该数据传给index.html模板,当用户访问 http://xxx/wechat8007/index地址时,会渲染出我们定义的index.html模板
    if "errcode" in user_dict_data:
        return u"获取用户信息失败"
    else:
        # 将用户的资料数据填充到页面中
        return render_template("index.html", user=user_dict_data)
    •  step7 当前目录下创建templates模板目录,在该目录中创建index.html文件 ,代码如下
    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8">
        <title>{{user["nickname"]}}的个人主页</title>
    </head>
    <body>
        <img alt="头像" src="{{user['headimgurl']}}" width="60">
        <table>
            <tr>
                <th>openid</th>
                <td>{{user["openid"]}}</td>
            </tr>
            <tr>
                <th>昵称</th>
                <td>{{user["nickname"]}}</td>
            </tr>
            <tr>
                <th>性别</th>
                <td>
                    {% if 1 == user["sex"] %}
                        男
                    {% elif 2 == user["sex"] %}
                        女
                    {% else %}
                        未知
                    {% endif %}
                </td>
            </tr>
            <tr>
                <th>省份</th>
                <td>{{user["province"]}}</td>
            </tr>
            <tr>
                <th>城市</th>
                <td>{{user["city"]}}</td>
            </tr>
            <tr>
                <th>国家</th>
                <td>{{user["country"]}}</td>
            </tr>
        </table>
    </body>
    </html>

     三丶部署测试

    • step1 将代码推送到服务器上

    成功推送到服务器上

    •  step2 在服务器上进入虚拟环境,运行此程序

     

    In [1]: import urllib
    
    In [2]: urllib.quote("http://www.xxx.com/wechat8007/index")
    Out[2]: 'http%3A//www.xxx.com/wechat8007/index'
    
    
    

     

    • 拼接好的用户访问的url地址为

    • step4 可以将该网址生成二维码,使用微信扫一扫,也可以在接口公众号直接发送此链接地址
    •  使用谷歌浏览器的二维码插件,将网址生成对应的二维码(这里以百度首页网址为例)

    • 直接在浏览器中输入此地址会提示请在微信客户端打开链接

    •  step5 测试,在手机微信上打开此链接,出现授权登录提示,点击允许即可获取用户个人信息

    点击允许后,进入如下界面

    点击继续访问,则出现博主个人的微信信息了,如下图 

    此时查看服务器上程序运行日志 

    四丶完整代码

    # coding:utf-8
    from flask import Flask, request, render_template
    import json, urllib2
    
    
    WECHAT_APPID = "yourappid"
    WECHAT_APPSECRET = "yoursecret"
    
    app = Flask(__name__)
    
    
    
    @app.route("/wechat8007/index")
    def index():
    
        code = request.args.get("code")
    
        if not code:
            return u"缺失code参数"
    
        url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code" % (WECHAT_APPID, WECHAT_APPSECRET, code)
    
        response = urllib2.urlopen(url)
    
        json_str = response.read()
        resp_dict = json.loads(json_str)
    
        if "errcode" in resp_dict:
            return u"获取access_token失败"
    
        access_token = resp_dict.get("access_token")
        open_id = resp_dict.get("openid")
    
        url = "https://api.weixin.qq.com/sns/userinfo?access_token=%s&openid=%s&lang=zh_CN" % (access_token, open_id)
    
        response = urllib2.urlopen(url)
        user_json_str = response.read()
        user_dict_data = json.loads(user_json_str)
    
        if "errcode" in user_dict_data:
            return u"获取用户信息失败"
        else:
            return render_template("index.html", user=user_dict_data)
    
    
    if __name__ == '__main__':
        app.run(port=8007, debug=True)

    总结:微信公众号接口开发,根据官方提供的开发文档,进行开发,逻辑实现都很简单,多想多思考多练习,你会越来越棒的!

    展开全文
  • 过年前后做了个微信公众号项目,已经过去一段时间了,抽空回忆总结下基本流程吧,不然很快估计自己就忘了。。 微信公众平台官网:https://mp.weixin.qq.com 文章目录一、注册公众号二、了解公众号管理页面三、必备...

    背景:
    过年前后做了个微信公众号项目,已经过去一段时间了,抽空回忆总结下基本流程吧,不然很快估计自己就忘了。。

    微信公众平台官网:https://mp.weixin.qq.com



    一、注册公众号

    在这里插入图片描述
    首先注册时可以看到公众号有三种类型,个人用户大多数选择订阅号,而企业用户一般选择服务号和企业号

    我们平常大多数关注的都是订阅号,他们统一都放置在微信应用的订阅号消息列表中,没有微信支付等高级功能,只是用于发布文章等基础功能。
    在这里插入图片描述

    服务号企业号都在会话列表,和我们的微信好友是同级别的位置,具备微信支付等高级功能,一般是某个企业品牌的对外操作窗口,如海底捞火锅、顺丰速运等。
    在这里插入图片描述

    我们前期开发测试只需要注册个人订阅号即可,真正开发使用的是开发者工具里的测试号,具体下面会说。

    真正生产的话,使用的都是经过微信认证的订阅号、服务号、企业号。


    二、了解公众号管理页面

    我们在微信公众平台扫码登录后可以发现管理页面左侧菜单栏有丰富的功能:
    在这里插入图片描述
    大概可以分为这几大模块:
    首页功能小程序管理推广统计设置开发

    作为开发人员,首先应该关注的是设置、开发模块;而作为产品运营人员,关注的是功能、管理、推广模块;作为数据分析人员,关注的是统计模块。

    首先我们不妨各个功能模块都点击看一看,大概了解下我们能做些什么。可以确认的是,这个微信公众平台当然不只是给开发人员使用的,它提供了很多非技术人员可在UI界面上交互操作的功能模块。

    如配置消息回复、自定义菜单、发布文章等:
    在这里插入图片描述
    这个时候我们可能会想:这些功能好像非技术人员都能随意操作,那么还需要我们技术人员去开发吗?

    答案是: 如果只是日常简单的推送文章,就像我们关注的大多数公众号一样,那确实不需要技术人员去开发;但是,如果你想将你们的网站嵌入进去公众号菜单里(这里指的是把前端项目的首页链接配置在自定义菜单),并且实现微信端的独立登录认证、获取微信用户信息、微信支付等高级功能,或者觉得UI交互的配置方式无法满足你的需求,你需要更加自由、随心所欲的操作,那么我们就必须启用开发者模式了,通过技术人员的手段去灵活控制公众号。

    这里有一点需要注意,如果我们决定技术人员开发公众号,必须启用服务器配置,而这将导致UI界面设置的自动回复和自定义菜单失效!

    我们在 开发 - 基本配置 - 服务器配置 中点击启用
    在这里插入图片描述
    在这里插入图片描述
    我们团队就遇到过这种情况:两个项目组共用一个公众号,结果一个启用了服务器配置,使另一个项目组手动配置的菜单失效了。所以要注意这点!

    至于服务器配置中的选项代表什么意思、如何填写,我们下面再讲。


    三、必备开发者工具的使用

    在这里插入图片描述
    我们进入 开发 - 开发者工具, 可以发现微信提供了六种开发者工具,其中前四种属于开发必备:开发者文档在线接口调试工具web开发者工具公众平台测试账号

    1.开发者文档

    在这里插入图片描述
    这个不用说!在我们开发中属于最最最基础和重要的东西了,我们要想熟练开发公众号,首先必须熟读开发者文档!有些功能的开发甚至非要反复研读、咬文嚼字一番不可。PS:该文档吐槽的地方也不少,有些地方的确讲的不够明确!

    2.在线接口调试工具

    在这里插入图片描述
    这个工具也算比较实用,包含大多数接口的在线调试,我们可以直接在上面输入参数,获取微信服务端的返回结果。

    3.web开发者工具

    在这里插入图片描述
    这个工具是一款桌面应用,需要下载,它通过模拟微信客户端的UI使得开发者可以使用这个工具方便地在PC或者Mac上进行开发和调试工作,一般是前端使用该工具进行页面、接口调试。

    4.公众平台测试账号

    在这里插入图片描述
    这个测试号工具对我们的重要性可以说是仅次于开发者文档。我们可以创建测试号无需申请、认证真实的公众帐号、可在测试帐号中体验并测试微信公众平台所有高级接口。并且所有的配置都可在一个页面上编辑,使开发测试变得极其便利。


    四、细读开发者文档

    文档地址:https://mp.weixin.qq.com/wiki

    需要注意的是,细读开发者文档不是让你所有模块都去阅读,而是重点的重复细读,非重点的选择性阅读。
    在这里插入图片描述
    其中前两个模块:开始前必读开始开发,属于重点关注对象,也是整个微信开发的基石所在,需要多读几遍。其次是微信网页开发模块微信网页授权,比较难理解,需要特别注意。其他的模块则根据你们的项目功能需求,有选择性的阅读即可。

    这里我就不多罗嗦了,大家看文档去吧!下面我会描述一些重点内容的实际操作情况以及代码,请确保你已经浏览过文档


    五、开发流程重点解析


    1.开发环境准备

    这里所谓的开发环境准备主要指的是我们项目服务端和微信服务端的网络通讯环境准备。

    我们平常开发可能只需要IP端口就能通讯,顶多配置下白名单放行,但微信公众号开发我们需要通过域名通讯(微信会访问我们配置的域名地址:服务器基本配置中的URL,下面会介绍),也就是我们各自开发环境需要拥有独立的域名,微信就能通过这个域名请求到我们的本地开发服务,各自进行开发测试。

    而我们一般都是内网开发,整个内网只有一个对外域名,所以这时就需要 内网穿透 ,为我们每个开发人员配置各自开发机器的域名。

    那如何进行内网穿透呢?你首先可以找下你们的网管,看他能不能帮你解决,如果不能,那就安装内网穿透工具,我们自己动手!

    我选择的内网穿透工具是natapp,这个有免费版、收费版,免费版的域名会随机变化,而收费版可以拥有固定域名,建议选择收费版,9元每月并不贵;大家可以对照natapp的文档安装使用,并不难。
    在这里插入图片描述
    这样我们本地开发环境就拥有自己的域名啦!然后就可以在测试号管理页面配置本地访问地址URL了。

    2.服务器基本配置

    无论是在真实公众号开发 - 基本配置 - 服务器配置,还是在 测试号管理 中,我们都可以看到这几个基本参数:
    开发者ID(AppID)、开发者密码(AppSecret)、服务器地址(URL)、令牌(Token)

    AppID 是公众号唯一开发识别码,配合开发者密码可调用公众号的接口能力,大多数微信接口都需要附带该参数。

    AppSecret 是校验公众号开发者身份的密码,具有极高的安全性。切记勿把密码直接交给第三方开发者或直接存储在代码中。如需第三方代开发公众号,请使用授权方式接入。其中获取accessToken就需要同时传入AppID和AppSecret获取。

    URL 是开发者用来接收微信消息和事件的接口URL,也就是我们服务后端的入口地址,需要注意的是该地址必须以域名形式填写,且必须以http 或 https 开头,分别支持80端口和443端口。如:http://yuanj.natapp1.cc/wechat。

    Token 可由开发者可以任意填写,用作生成签名(该Token会和接口URL中包含的Token进行比对,从而验证安全性),也就是我们项目和微信服务端进行通信时,必须保证公众平台配置的Token和我们后台代码配置的Token保持一致,这样微信就能验证我们身份。

    注:EncodingAESKey 参数由开发者手动填写或随机生成,将用作消息体加解密密钥,我们前期可以采用明文模式进行开发测试,暂时先不用关注。
    在这里插入图片描述
    我们点击提交时,微信会以GET请求的方式访问我们配置的URL地址,并附加几个参数进行验证,所以你需要在该地址对应的项目后端接口里对这几个参数进行加工处理返回微信需要的结果,这样就可以验证成功,使微信服务端认可你配置的URL和Token参数,后续就能互相通信了!
    在这里插入图片描述
    具体情况可以阅读微信文档 - 开始前必读 - 接入指南

    这里附上该接口的Java代码:

    /**
     * 微信对接验证接口
     * */
    @RestController
    @RequestMapping(value = "/wechat")
    public class ValidateController {
        @Autowired
        WechatConfig wechatConfig;
    
       @RequestMapping(value = "", method = RequestMethod.GET)
       public void validate(HttpServletRequest req, HttpServletResponse resp) {
            System.out.println("-----开始校验签名-----");
    
            // 接收微信服务器发送请求时传递过来的参数
            String signature = req.getParameter("signature");
            String timestamp = req.getParameter("timestamp");
            String nonce = req.getParameter("nonce"); //随机数
            String echostr = req.getParameter("echostr");//随机字符串
    
            // 将token、timestamp、nonce三个参数进行字典序排序并拼接为一个字符串
            String TOKEN = wechatConfig.getToken();
            String sortStr = sort(TOKEN,timestamp,nonce);
            
            // 字符串进行shal加密
            String mySignature = WechatUtils.shal(sortStr);
            
            // 校验微信服务器传递过来的签名 和  加密后的字符串是否一致, 若一致则签名通过
            if(!"".equals(signature) && !"".equals(mySignature) && signature.equals(mySignature)){
                System.out.println("-----签名校验通过-----");
                try {
                    resp.getWriter().write(echostr);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }else {
                System.out.println("-----校验签名失败-----");
            }
        }
        
       /**
         * 参数排序
         * @param token
         * @param timestamp
         * @param nonce
         * @return
         */
        public static String sort(String token, String timestamp, String nonce) {
            String[] strArray = {token, timestamp, nonce};
            Arrays.sort(strArray);
            StringBuilder sb = new StringBuilder();
            for (String str : strArray) {
                sb.append(str);
            }
            return sb.toString();
        }
    }
    
    

    3.存取access_token参数

    access_token是公众号的全局唯一接口调用凭据,公众号调用各接口时都需使用access_token。开发者需要进行妥善保存。access_token的存储至少要保留512个字符空间。access_token的有效期目前为2个小时(7200秒),需定时刷新重复获取将导致上次获取的access_token失效

    access_token这个参数非常重要,几乎贯穿整个微信公关号项目开发,我们如何在有效期内定时刷新获取呢?
    如果我们的微信公众号项目是单服务架构,可以直接作为静态变量存储在内存里;如果是多服务,可以用中间件存储Redis、数据库都可以。SpringBoot项目内部可以通过@Scheduled注解,执行定时任务,既然access_token有效期是2小时,那我们可以一小时刷新获取一次,将其存入Redis,覆盖之前的access_token。


    4.公众号消息管理

    在这里插入图片描述
    很多公众号都可以通过消息发送来与其进行交互,那这样的功能如何代码实现呢?

    具体我们可以在微信文档 - 消息管理 模块查阅:
    在这里插入图片描述
    在此我要提到的一点就 微信公众号的消息交互都是通过XML格式进行的!这点就很坑了。。现在我们前后端、服务端的消息传输基本都是Json格式了,也习惯了Json格式的解析处理,所以遇到XMl格式的处理又要多费些事了。

    为什么微信采用XML格式呢?我个人猜测是几年前还是XML格式的天下,当时Json还没有这么流行,腾讯毕竟是产品业务驱动的,当然选择当时开发人员最熟悉的XML格式了开发,后面随着微信平台的普及,用户越来越多,想重构改成Json格式估计也十分困难,所以历史就遗留下来了呗。。

    我在此推荐一个GitHub上一个微信开发 Java SDK,里面有整个微信开发平台很多功能模块造好的轮子,我们可以参考下直接使用:
    https://github.com/Wechat-Group/WxJava
    在这里插入图片描述
    比如现在对于XMl消息解析这个需求,上面就提供了完整详尽的代码。


    5.获取openid以及网页授权(重难点)

    注意,这是公众号开发的重难点之一,请把技术文档中的微信网页授权模块多读两遍,然后带着疑问来看我的解析。

    (1)先明确为什么需要网页授权?我们的目的是什么?

    答:用户在微信客户端中访问第三方网页,公众号可以通过微信网页授权机制,来获取用户基本信息,进而实现业务逻辑。也就是通过这种授权机制,我们能获取微信用户信息,比如:头像、昵称、地区、个性签名等。

    (2)既然目的是获取用户基本信息,微信不是提供了专门的接口吗?非要网页授权?

    答:在文档的 用户管理 - 获取用户基本信息(UnionID机制) 模块可以看到的确有获取用户基本信息接口:
    在这里插入图片描述
    可以看到,这个接口只需要提供openid或者unionid,即可直接获取用户基本信息。那么问题来了,openid(unionid)又是如何获取呢?

    微信平台提供了两种方式获取用户的openid

    第一种方式:

    用户与公众号产生消息交互时,会以POST请求的方式向我们配置的服务器URL地址发送XML格式的消息,并附带该用户对应公众号的openid!关于什么是消息交互我们可以查看文档中的消息管理模块,比如我们在公众号输入栏中发送文字图片语音等属于普通消息交互,我们关注、取关、点击自定义菜单等属于事件消息交互,每当前端用户进行这个操作时,微信服务端都会向我们项目后台发送POST请求给我们传达信息:
    在这里插入图片描述
    可以看到,这个推送数据包中就包含了用户的消息交互类型、时间以及我们需要的openid!也就是说,无论用户在公众号里干了啥操作,我们都能知道他这个操作干了啥,以及他是谁(openid),这时就能调用 用户管理 - 获取用户基本信息(UnionID机制) 接口获取用户基本信息了。

    别高兴太早,这种通过消息交互获取用户信息的方式,用户占主动地位,我们项目后端服务被动接受,那么如果我有个基本需求:我想在自定义菜单 - 对应我们网站的前端页面上展示微信用户基本信息,能做到吗?你如何把后台接收到的消息和前端用户关联绑定?
    可见,这种被动的方式并不能实现该功能,我们需要主动出击,在前端就能获取到当前操作用户的openid!

    第二种方式:

    这种方式就是通过网页授权机制主动出击!详情见下文。

    (3)网页授权有哪几种机制?分别是怎样实现?应用于什么场景?

    答:主要有两种机制,对应两种scope:

    snsapi_base为scope发起的网页授权,是用来获取进入页面的用户的openid的,并且是静默授权并自动跳转到回调页的。用户感知的就是直接进入了回调页(往往是业务页面)。

    snsapi_userinfo为scope发起的网页授权,是用来获取用户基本信息的。但这种授权需要用户手动同意,并且由于用户同意过,所以无须关注,就可在授权后获取该用户的基本信息。

    光看这两句解释你可能有一堆疑问,我们逐一分析:

    两种机制的前面授权步骤相同,大概如下:

    我们先要按照文档要求构造一个链接https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect
    其中重点参数是redirect_uri,这个参数填的既可以是前端项目url,也可以是后端接口url,然后点击这个链接后,微信服务端经过重定向到我们填写的redirect_uri,会在此redirect_uri后拼接上一个code参数!然后前端或者后端通过code参数就可以调微信接口https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code获取openid等信息了:
    在这里插入图片描述
    这里讲下 snsapi_basesnsapi_userinfo不同点

    首先snsapi_base静默授权,什么意思呢?就是用户没有感知;与之对应的就是非静默授权snsapi_userinfo了,这个scope公众号会弹出一个小窗口需要用户手动点击授权,类似这种:
    在这里插入图片描述
    那么这两种scope授权的优劣势在哪呢?

    snsapi_base 的优势在于用户无感知,体验好,方便快捷;劣势在于获取openid后只能通过用户管理 - 获取用户基本信息(UnionID机制) 接口获取用户基本信息,而这种方式需要确保用户已经关注,不然是没有相关信息的!
    snsapi_userinfo 的优势在于无需用户关注公众号,只要用户点击了授权确认,即可通过access_token和openid调用专门的拉去用户信息接口获取信息,比较暴力。。;劣势在于需要用户手动授权,可能影响用户体验
    在这里插入图片描述

    在此说下,我们项目是通过snsapi_base静默授权的,其中redirect_uri配置的是前端项目首页地址(前后端分离),并将构造的这个链接封装起来,直接配置在自定义菜单里,那么用户点击菜单,就直接重定向到前端项目,然后前端获取code参数调用后端获取openid接口,将获取的openid缓存到客户端,以便后面使用。

    (4)想要进行网页授权,我们需要在公众平台配置什么吗?

    答:需要!
    如果是测试号,需要在 测试号管理 - 体验接口权限表 - 网页服务 - 网页帐号 点击 修改
    在这里插入图片描述
    在这里插入图片描述
    在这里配置的是回调页面redirect_uri的域名

    如果是正式号(需要微信认证),需要在 开发 - 接口权限 - 网页服务 - 网页帐号 - 网页授权获取用户基本信息 的配置选项中,修改授权回调域名。请注意,这里填写的是域名(是一个字符串),而不是URL,因此请勿加 http:// 等协议头;

    而且正式号其他配置的地方也和测试号不一样,比如多了IP白名单、域名根路径下的txt验证文件,这个稍微摸索下应该没啥问题的。


    over 暂时就回忆这么多了。。。可能有遗漏大家可以提出哈 ~ 下一篇博客写几个开发时的小问题补充下吧

    展开全文
  • 微信开发交流群:148540125 系列文章参考地址 极速开发微信公众号欢迎留言、转发、打赏 项目源码参考地址 点我点我--欢迎Start 极速开发微信公众号系列文章之如何一键导出微信所有用户信息到Excel 前方高能警告⚠...

    微信开发交流群:148540125
    系列文章参考地址 极速开发微信公众号欢迎留言、转发、打赏
    项目源码参考地址 点我点我--欢迎Start

    极速开发微信公众号系列文章之如何一键导出微信所有用户信息到Excel

    前方高能警告⚠️:用户信息导出我们需要使用以下权限以及接口

    以上链接点击可以查看相关文档

    本文中用户导入到excel使用的是jxl,当然大家可以使用poi。如果不会使用jxl可以参考我之前写的 Java实现Excel导入数据库,数据库中的数据导入到Excel

    好了,准备工作做好了那就开干吧!!!!

    实现的目标:访问一个地址可以下载一个保存最新所有用户详细信息的Excel,最终效果图如下

    最终效果图.png

    将详细的用户信息(List)保存到Excel

    /**
       * 将详细的用户信息保存到Excel
       * @param userInfos
       * @return
       */
      private File saveToExcel(List<UserInfo> userInfos){
        File file=null;
        try {
          WritableWorkbook wwb = null;
          
          // 创建可写入的Excel工作簿
          String fileName = "用户详细信息.xls";
          file=new File(fileName);
          //以fileName为文件名来创建一个Workbook
          wwb = Workbook.createWorkbook(file);
    
          // 创建工作表
          WritableSheet ws = wwb.createSheet("用户详细信息", 0);
          ws.setColumnView(0,8);
          ws.setColumnView(1,15);
          ws.setColumnView(2,50);
          ws.setColumnView(3,8);
          ws.setColumnView(4,10);
          ws.setColumnView(5,10);
          ws.setColumnView(6,10);
          ws.setColumnView(7,20);
          ws.setColumnView(8,50);
          ws.setColumnView(9,10);
          ws.setColumnView(10,30);
          ws.setColumnView(11,20);
          ws.setColumnView(12,20);
          
          ws.mergeCells(0,0,12,0);//合并第一列第一行到第七列第一行的所有单元格 
          WritableFont font1= new WritableFont(WritableFont.TIMES,16,WritableFont.BOLD); 
          WritableCellFormat format1=new WritableCellFormat(font1); 
          format1.setAlignment(jxl.format.Alignment.CENTRE);
          Label top= new Label(0, 0, "所有用户详细信息",format1);
          ws.addCell(top);
          
          //要插入到的Excel表格的行号,默认从0开始
          Label labelId= new Label(0, 1, "编号");
          Label labelnickname= new Label(1, 1, "用户的昵称");
          Label labelopenid= new Label(2, 1, "用户的标识");
          Label labelsex= new Label(3, 1, "性别");
          Label labelcountry= new Label(4,1, "所在国家");
          Label labelprovince= new Label(5,1, "所在省份");
          Label labelcity= new Label(6, 1, "所在城市");
          Label labellanguage= new Label(7,1, "用户的语言");
          Label labelheadimgurl= new Label(8,1, "用户头像");
          Label labelsubscribe= new Label(9, 1, "是否订阅");
          Label labelsubscribetime= new Label(10, 1, "关注时间");
          Label labelgroupid= new Label(11, 1, "所在的分组ID");
          Label labelremark= new Label(12, 1, "备注");
          ws.addCell(labelId);
          ws.addCell(labelnickname);
          ws.addCell(labelopenid);
          ws.addCell(labelsex);
          ws.addCell(labelcountry);
          ws.addCell(labelprovince);
          ws.addCell(labelcity);
          ws.addCell(labellanguage);
          ws.addCell(labelheadimgurl);
          ws.addCell(labelsubscribe);
          ws.addCell(labelsubscribetime);
          ws.addCell(labelgroupid);
          ws.addCell(labelremark);
          for (int i = 0; i < userInfos.size(); i++) {
              
              Label labelId_i= new Label(0, i+2, i+1+"");
              Label nickName= new Label(1, i+2, userInfos.get(i).getNickname());
              Label openid= new Label(2, i+2, userInfos.get(i).getOpenid());
    
            String sexStr=userInfos.get(i).getSex();
            //用户的性别,值为1时是男性,值为2时是女性,值为0时是未知
            if (StrKit.notBlank(sexStr)) {
              int sexInt=Integer.parseInt(sexStr);
              if (sexInt==1) {
                sexStr="男";
              }else if (sexInt==2) {
                sexStr="女";
              }
            }else {
              sexStr="未知";
            }
    
              Label sex= new Label(3, i+2, sexStr);
              Label country= new Label(4, i+2, userInfos.get(i).getCountry());
              Label province= new Label(5, i+2, userInfos.get(i).getProvince());
              Label city= new Label(6, i+2, userInfos.get(i).getCity());
              Label language= new Label(7, i+2, userInfos.get(i).getLanguage());
              Label headimgaeurl= new Label(8, i+2, userInfos.get(i).getHeadimgurl());
    
              Label subscribe= new Label(9, i+2, userInfos.get(i).getSubscribe().equals("1")?"已关注":"未关注");
            //获取关注时间
            String subscribe_time = userInfos.get(i).getSubscribe_time();
    
            if (StrKit.notBlank(subscribe_time)) {
              subscribe_time=sfg.format(new Date(Long.parseLong(subscribe_time) * 1000L));
            }
              Label subscribetime= new Label(10, i+2, subscribe_time);
              Label groupid= new Label(11, i+2, userInfos.get(i).getGroupid());
              Label remark= new Label(12, i+2, userInfos.get(i).getRemark());
              ws.addCell(labelId_i);
              ws.addCell(openid);
              ws.addCell(nickName);
              ws.addCell(sex);
              ws.addCell(country);
              ws.addCell(province);
              ws.addCell(city);
              ws.addCell(language);
              ws.addCell(headimgaeurl);
              ws.addCell(subscribe);
              ws.addCell(subscribetime);
              ws.addCell(groupid);
              ws.addCell(remark);
          }
         
          //写进文档
          wwb.write();
          // 关闭Excel工作簿对象
          wwb.close();
        } catch (Exception e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
        }
        return file;
      }

    获取所有用户列表

    /**
       * 获取所有的openid
       * @return
       */
      public List<String> getAllOpenId(){
        List<String> openIds = getOpenIds(null);
        return openIds;
      }

    getOpenIds(Stirng next_openid) 方法中迭代(一次拉取调用最多拉取10000个关注者的OpenID)获取所有的openId并返回一个List集合

    private List<String> getOpenIds(String next_openid){
        List<String> openIdList=new ArrayList<String>();
        ApiResult apiResult=UserApi.getFollowers(next_openid);
        String json=apiResult.getJson();
    log.error("json:"+json);
        if (apiResult.isSucceed()) {
          JSONObject result = JSON.parseObject(json);
          next_openid = apiResult.getStr("next_openid");
          int count = apiResult.getInt("count");
          JSONObject openIdObject = result.getJSONObject("data");
          if (count>0) {
            JSONArray openids=openIdObject.getJSONArray("openid");
            for (int i = 0; i < openids.size(); i++) {
              openIdList.add(openids.getString(i));
            }
          }
          //下一页
          if (next_openid!=null&& !next_openid.equals("")) {
            List<String> list = getOpenIds(next_openid);
            openIdList.addAll(list);
          }
        }
        return openIdList;
      }

    批量获取用户基本信息

    注意批量接口最多支持一次拉取100条

    /**
       * 根据openId列表获取用户信息
       * @param allOpenId  
       * @return
       */
      private List<UserInfo> getAllUserInfo(List<String> allOpenId){
        List<UserInfo> userInfos = new ArrayList<UserInfo>();
        int total=allOpenId.size();
        UserConfig[] user_list=null;
        //开发者可通过该接口来批量获取用户基本信息。最多支持一次拉取100条。
        int temp=100;//一次获取100
        if (total>temp) {
          int page=0;//当前页面
          int count=total/100+(total%100>0?1:0);//总共获取多少次
          int index=0;
          while (page<count) {
            index=(temp*(page+1))>total?total:(temp*(page+1));
            System.out.println("/////////"+page*temp+" "+index);
            user_list=new UserConfig[index-(page*temp)];
            for (int i = page*temp; i <index; i++) {
              UserConfig config=new UserConfig();
              config.setLang(LangType.zh_CN);
              config.setOpenid(allOpenId.get(i));
              user_list[i-(page*temp)]=config;
            }
            GetUserInfo getUserInfo = new GetUserInfo();
            getUserInfo.setUser_list(user_list);
            String jsonGetUserInfo = JsonKit.toJson(getUserInfo);
            System.out.println("jsonGetUserInfo:"+jsonGetUserInfo);
            
            ApiResult apiResult = UserApi.batchGetUserInfo(jsonGetUserInfo);
            
            String jsonResult = apiResult.getJson();
            //将json转化为对象
            List<UserInfo> userInfo = parseJsonToUserInfo(jsonResult);
            userInfos.addAll(userInfo);
            page++;
          }
        }else {
          user_list=new UserConfig[total];
          for (int i = 0; i < user_list.length; i++) {
            System.out.println(allOpenId.get(i));
            UserConfig config=new UserConfig();
            config.setLang(LangType.zh_CN);
            config.setOpenid(allOpenId.get(i));
            user_list[i]=config;
          }
          GetUserInfo getUserInfo = new GetUserInfo();
          getUserInfo.setUser_list(user_list);
          String jsonGetUserInfo = JsonKit.toJson(getUserInfo);
          
          
          ApiResult batchGetUserInfo = UserApi.batchGetUserInfo(jsonGetUserInfo);
          List<UserInfo> userInfo = parseJsonToUserInfo(batchGetUserInfo.getJson());
          userInfos.addAll(userInfo);
        }
        return userInfos;
      }

    大功告成---测试

    开源项目weixin_guide 中添加路由 com.javen.common.APPConfig 类的 configRoute(Routes me) 的方法中添加 me.add("/wxuser", UserController.class,"/front");

    在浏览器中输入http://localhost:8080/wxuser 即可下载Excel

    public void index(){
        List<UserInfo> allUserInfo = getAllUserInfo(getAllOpenId());
        
        if (!allUserInfo.isEmpty()) {
          ///下载userInfos
          File file = saveToExcel(allUserInfo);
          renderFile(file);
        }else {
          render("目前暂无用户...");
        }
      }

    以上如何一键导出微信所有用户信息到Excel的全过程。
    欢迎留言、转发、打赏项目源码参考地址 点我点我--欢迎Start

    展开全文
  • 关于如何获取微信已关注用户基本信息,我这里先说一下步骤:那些基本的信息就不多说了,如AppID,AppSecret1、首先成为开发者,需要外网,如果没有外网,建议在花生壳申请一个账号,通过映射,可以映射到外网,...

    关于如何获取微信已关注用户的基本信息,我这里先说一下步骤:那些基本的信息就不多说了,如AppIDAppSecret1、首先成为开发者,需要外网,如果没有外网,建议在花生壳申请一个账号,通过映射,可以映射到外网,配置好了,如下界面

    2、完成第一步后,接下来就是获取opinid了,关于如何获取openID,其实是比较头疼的,网上给出了各种各样的答案,我这里详细说一下,


    3、设置好了回调域名,接下来就是通过网页来授权了

    文档也说了,有两种方式:

    我用的是第二种,因为第一种要用到https协议,工作紧,故没有去试,那我直接说第二种:

    请求地址是:

    https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect
    APPID不用说了吧,redirect_uri这个,请记住,这个一定是你回调域名下面的一级,如果你的回调域名为

    那你的redirect_uri填的是weixin下的一级,不能跨级。weixin这个是我的项目名,那redirect_uri可以填weixin/servlet,也可以填weixin/index.jsp,记得URL转码。scopesnsapi_userinfo,其他参数默认就行了,稍后本人会加上我的项目供大家参考,因为本人也很菜,代码就见笑了。。

    如果这个填好了,那其他的基本没什么问题了,如果有疑问,欢迎大家加我工作qq320302887,乐意解答。

    接下来用代码解释一下。

     这是我们要获得用户的一些基本信息(User类)

    //用户的唯一标识
    	 private String openId;
    	  // 关注状态(1是关注,0是未关注),未关注时获取不到其余信息
    	  private int subscribe;
    	  // 用户关注时间,为时间戳。如果用户曾多次关注,则取最后关注时间
    	  private String subscribeTime;
    	  // 昵称
    	  private String nickname;
    	  // 用户的性别(1是男性,2是女性,0是未知)
    	  private int sex;
    	  // 用户所在国家
    	  private String country;
    	  // 用户所在省份
    	  private String province;
    	  // 用户所在城市
    	  private String city;
    	  // 用户的语言,简体中文为zh_CN
    	  private String language;
    	  // 用户头像地址
    	  private String headImgUrl;

    记得get set这些属性。

    这是我们要获得token,获得的Accesstoken只有两个小时的生命,多刷新。

    // 接口访问凭证
    	  private String accessToken;
    	  // 凭证有效期,单位:秒
    	  private int expiresIn;

      记得get set这两个属性。
    要这个类
    public class MyX509TrustManager implements X509TrustManager{
    	// 检查客户端证书
    	@Override
    	public void checkClientTrusted(X509Certificate[] chain, String authType)
    			throws CertificateException {
    		// TODO Auto-generated method stub
    		
    	}
    	// 检查服务器端证书
    	@Override
    	public void checkServerTrusted(X509Certificate[] chain, String authType)
    			throws CertificateException {
    		// TODO Auto-generated method stub
    		
    	}
    	// 返回受信任的X509证书数组
    	@Override
    	public X509Certificate[] getAcceptedIssuers() {
    		// TODO Auto-generated method stub
    		return null;
    	}
    
    


    然后还写一个CommonUtil类,

    import java.io.BufferedReader;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.io.OutputStream;
    import java.net.ConnectException;
    import java.net.URL;
    
    import javax.net.ssl.HttpsURLConnection;
    import javax.net.ssl.SSLContext;
    import javax.net.ssl.SSLSocketFactory;
    import javax.net.ssl.TrustManager;
    
    import net.sf.json.JSONObject;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    public class CommonUtil {
    //	private static Logger log = LoggerFactory.getLogger(CommonUtil.class);
    	// 凭证获取(GET)
    //	public final static String token_url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=OPENID&secret=APPSECRET";
    
    	/**
    	 * 发送https请求
    	 * "https://api.weixin.qq.com/cgi-bin/user/info?access_token=ACCESS_TOKEN&openid=OPENID"
    	 * 
    	 * @param requestUrl
    	 *            请求地址
    	 * @param requestMethod
    	 *            请求方式(GET、POST)
    	 * @param outputStr
    	 *            提交的数据
    	 * @return JSONObject(通过JSONObject.get(key)的方式获取json对象的属性值)
    	 */
    	public static JSONObject httpsRequest(String requestUrl,
    			String requestMethod, String outputStr) {
                System.out.println("进入httpsRequest方法");
                JSONObject jsonObject=null;
    		try {
    			// 创建SSLContext对象,并使用我们指定的信任管理器初始化
    			TrustManager[] tm = { new MyX509TrustManager() };
    			SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
    			sslContext.init(null, tm, new java.security.SecureRandom());
    			// 从上述SSLContext对象中得到SSLSocketFactory对象
    			SSLSocketFactory ssf = sslContext.getSocketFactory();
    			URL url = new URL(requestUrl);
    			HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
    			conn.setSSLSocketFactory(ssf);
    			conn.setDoOutput(true);
    			conn.setDoInput(true);
    			conn.setUseCaches(false);
    			// 设置请求方式(GET/POST)
    			conn.setRequestMethod(requestMethod);
    			// 当outputStr不为null时向输出流写数据
    			if (null != outputStr) {
    				OutputStream outputStream = conn.getOutputStream();
    				// 注意编码格式
    				outputStream.write(outputStr.getBytes("UTF-8"));
    				outputStream.close();
    			}
    			// 从输入流读取返回内容
    			InputStream inputStream = conn.getInputStream();
    			InputStreamReader inputStreamReader = new InputStreamReader(
    					inputStream, "utf-8");
    			BufferedReader bufferedReader = new BufferedReader(
    					inputStreamReader);
    			String str = null;
    			StringBuffer buffer = new StringBuffer();
    			while ((str = bufferedReader.readLine()) != null) {
    				buffer.append(str);
    			}
    			// 释放资源
    			bufferedReader.close();
    			inputStreamReader.close();
    			inputStream.close();
    			inputStream = null;
    			conn.disconnect();
    			 jsonObject = JSONObject.fromObject(buffer.toString());
    		} catch (ConnectException ce) {
    //			log.error("连接超时:{}", ce);
    		} catch (Exception e) {
    //			log.error("https请求异常:{}", e);
    		}
    //		System.out.println("json为123:"+jsonObject);
    		return jsonObject;
    	}
    	/**
    	 * 获取接口访问凭证
    	 * 
    	 * @param appid
    	 *            凭证
    	 * @param appsecret
    	 *            密钥
    	 * @return 
    	 *         https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential
    	 *         &appid=OPENID&secret=APPSECRET
    	 */
    	public static Token getToken(String appid, String appsecret) {
    		Token token = null;
    		String requestUrl="https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid="+appid+"&secret="+appsecret; 
    //		String requestUrl = token_url.replace("APPID", appid).replace(
    //				"APPSECRET", appsecret);
    		// 发起GET请求获取凭证
    		JSONObject jsonObject = httpsRequest(requestUrl, "GET", null);
    		if (null != jsonObject) {
    			try {
    				token = new Token();
    				token.setAccessToken(jsonObject.getString("access_token"));
    				token.setExpiresIn(jsonObject.getInt("expires_in"));
    			} catch (Exception e) {
    				token = null;
    //				// 获取token失败
    //				log.error("获取token失败 errcode:{} errmsg:{}",
    //						jsonObject.getInt("errcode"),
    //						jsonObject.getString("errmsg"));
    			}
    		}
    		System.out.println(token);
    		return token;
    	}
    
    	/**
    	 * URL编码(utf-8)
    	 * 因为只是获取用户基本信息,故下面两个方法用不到
    	 * @param source
    	 * @return
    	 */
    	
    /*	public static String urlEncodeUTF8(String source) {
    		String result = source;
    		try {
    			result = java.net.URLEncoder.encode(source, "utf-8");
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    		return result;
    	}*/
    
    	/**
    	 * 根据内容类型判断文件扩展名
    	 * 
    	 * @param contentType
    	 *            内容类型
    	 * @return
    	 */
    /*	public static String getFileExt(String contentType) {
    		String fileExt = "";
    		if ("image/jpeg".equals(contentType))
    			fileExt = ".jpg";
    		else if ("audio/mpeg".equals(contentType))
    			fileExt = ".mp3";
    		else if ("audio/amr".equals(contentType))
    			fileExt = ".amr";
    		else if ("video/mp4".equals(contentType))
    			fileExt = ".mp4";
    		else if ("video/mpeg4".equals(contentType))
    			fileExt = ".mp4";
    		return fileExt;
    	}*/
    }

    个类就这两个方法供使用。

    然后就是用户信息实现的类我这里省去,因为我直接写在servlet里了,额,代码好乱,等下再整理吧,加上自己写代码的过程。

    public class Servlet extends HttpServlet {
        /**
         *
         */
        private static final long serialVersionUID = 1L;
        private static String code;
        private static  String oauth2_url;
        private static String openid;
        private static String appid="wxd00dac2a2ee71120";
        private static String appsecret="db9f234cd02a73e809f5ab02a35e6f09";
        private static JSONObject jsonObject;
        private static JSONObject jsonObject1;
        private static String  accessToken;
        private static User user;
    //    private static int subscribe;
        
    //    private static String access_token="EMpAts8L2GNBPCqd7JwZSD4vAsS9SjK_MoF1aTGxqe8iRXY4Dgh7xZAhDdVpfTFkG6SC9_SuYzyHMNQopxqFrtZdqE9zNZsrJerPY28-JiRqCooYncA3dg8bH1q3JwslDWZdABARKN";
    //    public String Getco(){
    //        return code;
    //    }
        
        public void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            doPost(request, response);
        }
    
        public void doPost(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            request.setCharacterEncoding("utf-8");
            response.setContentType("text/html;charset=utf-8");
            System.out.println("Servlet");
            code=request.getParameter("code");
            oauth2_url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=wxd00dac2a2ee71120&secret=db9f234cd02a73e809f5ab02a35e6f09&code="+code+"&grant_type=authorization_code";
            System.out.println("code:" + code);
    //        String openId = CommonUtil1.getOpenId(code);
    //        System.out.println("openid"+openId);
             jsonObject = CommonUtil.httpsRequest(oauth2_url, "POST", null);
             openid = jsonObject.getString("openid");
            
    //         subscribe=jsonObject.getInt("subscribe");
    //         System.out.println("获得的subscribe为"+subscribe);
            System.out.println("获得的openID为"+openid);
            Token token = CommonUtil.getToken(appid, appsecret);
            accessToken=token.getAccessToken();
            System.out.println(token);
            System.out.println("最后的opinion*-----"+openid);
             user = getUserInfo(accessToken,openid);
            
             int s=jsonObject1.getInt("subscribe");
            
             System.out.println(s);
            
             System.out.println("------------------------");
    //         openid = jsonObject.getString("openid");
             System.out.println(openid);
    //         int subscribe = jsonObject.getInt("subscribe");
             System.out.println(jsonObject);
    //         System.out.println(subscribe);
            System.out.println("这个user为、、、、、、、、、、"+user);
        }
        public static User getUserInfo(String accessToken, String openId) {
            System.out.println("进入getUserInfo方法");
             user = null;
    //         jsonObject1=null;
             String requestUrl = "https://api.weixin.qq.com/cgi-bin/user/info?access_token="+accessToken+"&openid="+openId;
            //appid wx69d4d8acc08af18e
            //9f4b4d5d514b45054d25c1c2787062cf
            // 拼接请求地址
            //https://api.weixin.qq.com/cgi-bin/user/info?access_token=ACCESS_TOKEN&openid=OPENID
    //        requestUrl = requestUrl.replace("ACCESS_TOKEN", accessToken).replace(
    //                "OPENID", openId);
            // 获取用户信息
             jsonObject1 = CommonUtil
                    .httpsRequest(requestUrl, "POST", null);
             System.out.println(jsonObject1);
             System.out.println("到这了没有");
             if (jsonObject1.getJSONArray("tagid_list").isEmpty()) {
                 System.out.println("进来了没有");
    //             user.setOpenId(jsonObject1.getString("openid"));
    ////             System.out.println("这里的opinid777777777");
    //             int ss = jsonObject1.getInt("subscribe");
    //             user.setSubscribe(jsonObject1.getInt("subscribe"));
    //             System.out.println("执行了没有");
                return user;
            }
               System.out.println("想要的json:"+jsonObject1);
            if (null != jsonObject1) {
                try {
                    user = new User();
                    // 用户的标识
                    System.out.println(333);
                    user.setOpenId(jsonObject1.getString("openid"));
                    // 关注状态(1是关注,0是未关注),未关注时获取不到其余信息
    //                user.setSubscribe(ss);
                    // 用户关注时间
                    user.setSubscribeTime(jsonObject1.getString("subscribe_time"));
                    // 昵称
                    user.setNickname(jsonObject1.getString("nickname"));
                    // 用户的性别(1是男性,2是女性,0是未知)
                    user.setSex(jsonObject1.getInt("sex"));
                    // 用户所在国家
                    user.setCountry(jsonObject1.getString("country"));
                    // 用户所在省份
                    user.setProvince(jsonObject1.getString("province"));
                    // 用户所在城市
                    user.setCity(jsonObject1.getString("city"));
                    // 用户的语言,简体中文为zh_CN
                    user.setLanguage(jsonObject1.getString("language"));
                    // 用户头像
                    user.setHeadImgUrl(jsonObject1.getString("headimgurl"));
                } catch (Exception e) {
    //                 if (0 == user.getSubscribe()) {
    //                 log.error("用户{}已取消关注", user.getOpenId());
    //                 } else {
    //                 int errorCode = jsonObject.getInt("errcode");
    //                 String errorMsg = jsonObject.getString("errmsg");
    //                 log.error("获取用户信息失败 errcode:{} errmsg:{}", errorCode,
    //                 errorMsg);
    //                 }
                    e.printStackTrace();
                }
            }
    //        System.out.println("user为:--"+user);
            return user;
        }

    这里有一点要注意,如果用户没有关注就无法获得他的基本信息了,只能获得他的openID和关注状态,关注的用户就可以获得全部了

    就以上代码放同一个包应该就不会报错了,

    以下是微信客户端打开链接获得的信息:


    还有一些jar包需要下载


    这里要配置一下web.xml,配置那个servlet。。。

    所需jar下载地址:http://pan.baidu.com/s/1bo2n4qr 密码:8y3m。



















    展开全文
  • 上一篇《微信开发学习总结(一)——微信开发环境搭建》我们已经完成了微信开发的准备工作,准备工作完成之后,就要开始步入正题了。 一、微信公众平台的基本原理  在开始做之前,先简单介绍了微信公众平台的基本...
  • 公众号可通过本接口来根据OpenID获取用户基本信息,包括昵称、头像、性别、所在城市、语言和关注时间。 开发者可通过OpenID来获取用户基本信息。请使用https协议。 我们可以看看官方的文档:获取用户基本信息
  • 微信公众号后台开发,在什么时候保存用户信息用户关注的时候,用户浏览服务号的信息能不能保存用户信息?) 如果在用户关注的时刻得到用户信息openid,在后台怎么保存用户信息呢?
  • 微信公众号获取用户信息流程1,准备好appid,appsecret,把自己服务器的ip加入ip白名单。2,在开发者工具里面找到调试工具,打开调试工具,输入自己的appid和appsecret进行检测。出现如下页面表示正常,可以获取到...
  • springboot项目,h5页面通过微信公众号获取微信用户信息 最近本人有一个项目需求,微信公众号里点击一个菜单进入一个商城购物系统。 对于在微信公众号还是小白的我来说难度有点大,但是做完后发现也就这样,用多了就...
  • 本课程是一个系列入门教程,目标是从 0 开始带领读者上手实战,课程以微信小程序的核心概念作为主线,介绍配置文件、页面样式文件、JavaScript 的基本知识并以指南针为例对基本知识进行扩展,另外加上开发工具的安装...
  • 微信开发与代码的编写(一) 微信开发环境的搭建 目前移动开发处于比较火的的趋势,很多的开发者都跃跃欲试,目前移动App开发领域主要分为以下几种类型    我在平时的工作中接触得比较多的就是基于Android的Native...
  • 随着腾讯微信公众平台号的开放,围绕着微信的各种开发和营销如火如荼。很多商家也开始在微信上做文章,想充分利用微信平台4亿多的粉丝群体来进行各种营销和推广。这直接带来了很多公司开始做微信第3方营销平台,而...
  • 介绍了微信开发环境的搭建,这篇文章我们就来一步步开发一些具体的功能。 功能需求:当有微信用户关注了您的公众号之后,您用JavaScript发送一个欢迎消息给这个粉丝。 具体实现 我们登陆微信公众号的控制台后,...
  • 本片博客降解的网页授权分为两步,一是企业微信配置的...由于微信是本地开发,需要本地开发测试,准备一个内网穿透,将本地127.0.0.1:端口/项目名下所在的服务穿透到外网去,以便能与微信服务器通讯,开发调试;关...
  • 微信小程序开发系列教程 微信小程序开发系列一:微信小程序的申请和开发环境的搭建 微信小程序开发系列二:微信小程序的视图设计 微信小程序开发系列三:微信小程序的调试方法 微信小程序开发系列四:微信小程序...
  • &lt;?php $appid = "wxXXXXXXXXXXXXXXXXXX"; $appsecret = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"; $url = "...amp;appid=$appid&amp;secret...
  • 微信小程序基于微信进行开发,而微信又存有用户信息,我们是否可以直接通过微信端获取用户信息,则无需用户进行再次输入个人信息微信小程序可以通过wx.getUserInfo()接口来获取部分用户信息,我们可以参考微信...
1 2 3 4 5 ... 20
收藏数 25,074
精华内容 10,029