微信公共平台接入之:网页授权(微信授权,微信access_token获取,获取微信用户信息),微信开发者工具使用,微信公众平台测试号申请接入

1、微信公众平台文档入口

微信公众平台入口地址:https://mp.weixin.qq.com/,截图:
微信公共平台接入之:网页授权(微信授权,微信access_token获取,获取微信用户信息),微信开发者工具使用,微信公众平台测试号申请接入_第1张图片

进入之后的文档地址:
https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1445241432
微信公共平台接入之:网页授权(微信授权,微信access_token获取,获取微信用户信息),微信开发者工具使用,微信公众平台测试号申请接入_第2张图片

2、微信网页授权

介绍:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140842
具体内容:
如果用户在微信客户端中访问第三方网页,公众号可以通过微信网页授权机制,来获取用户基本信息,进而实现业务逻辑。

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

1、在微信公众号请求用户网页授权之前,开发者需要先到公众平台官网中的“开发 - 接口权限 - 网页服务 - 网页帐号 - 网页授权获取用户基本信息”的配置选项中,修改授权回调域名。请注意,这里填写的是域名(是一个字符串),而不是URL,因此请勿加 http:// 等协议头;
即:公众号设置里面–>公众号设置 —>功能设置 里面的 网页授权域名 不用带http或https字样
类似
微信公共平台接入之:网页授权(微信授权,微信access_token获取,获取微信用户信息),微信开发者工具使用,微信公众平台测试号申请接入_第3张图片

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

关于网页授权的两种scope的区别说明

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

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

3、用户管理类接口中的“获取用户基本信息接口”,是在用户和公众号产生消息交互或关注后事件推送后,才能根据用户OpenID来获取用户基本信息。这个接口,包括其他微信接口,都是需要该用户(即openid)关注了公众号后,才能调用成功的。

关于网页授权access_token和普通access_token的区别

1、微信网页授权是通过OAuth2.0机制实现的,在用户授权给公众号后,公众号可以获取到一个网页授权特有的接口调用凭证(网页授权access_token),通过网页授权access_token可以进行授权后接口调用,如获取用户基本信息;

2、其他微信接口,需要通过基础支持中的“获取access_token”接口来获取到的普通access_token调用。

关于UnionID机制

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

2、UnionID机制的作用说明:如果开发者拥有多个移动应用、网站应用和公众帐号,可通过获取用户基本信息中的unionid来区分用户的唯一性,因为同一用户,对同一个微信开放平台下的不同应用(移动应用、网站应用和公众帐号),unionid是相同的。

关于特殊场景下的静默授权

1、上面已经提到,对于以snsapi_base为scope的网页授权,就静默授权的,用户无感知;

2、对于已关注公众号的用户,如果用户从公众号的会话或者自定义菜单进入本公众号的网页授权页,即使是scope为snsapi_userinfo,也是静默授权,用户无感知。

具体而言,网页授权流程分为四步:

1、引导用户进入授权页面同意授权,获取code

2、通过code换取网页授权access_token(与基础支持中的access_token不同)

3、如果需要,开发者可以刷新网页授权access_token,避免过期

4、通过网页授权access_token和openid获取用户基本信息(支持UnionID机制)

目录

1 第一步:用户同意授权,获取code
2 第二步:通过code换取网页授权access_token
3 第三步:刷新access_token(如果需要)
4 第四步:拉取用户信息(需scope为 snsapi_userinfo)
5 附:检验授权凭证(access_token)是否有效

第一步:用户同意授权,获取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参数对应的授权作用域权限。

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

参考链接(请在微信客户端中打开此链接体验):
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_redirect

尤其注意:跳转回调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重定向时候,必须带此参数

要注意的是:这里的redirect_uri要在代码层面进行url.encode,示例代码:

/**
 * 获取微信code的值
 */
@RequestMapping("/accredit")
    public String accredit(
            HttpServletResponse response,
            HttpServletRequest request,
            Model model,
            @RequestParam(value = "state",required = false,defaultValue = "-1") Long state) throws Exception {
        log.info("【微信-授权】微信授权,获取微信code");
        //thirdPart_weixin_app_id   :wxxxxxxxxxx04
        //thirdPart_domain          :http://网页授权域名/
        //这里可以最后直接redirect到java后端action地址。也可以直接redirect到前端页面地址。前提条件是这两个地址对应的域名是上面配置的"网页授权域名"
        //String redirectUri = URLEncoder.encode(SysConfig.getValue("thirdPart_domain") + "/wap/thirdPart/accreditCallback","utf-8");
        String redirectUri = URLEncoder.encode(SysConfig.getValue("thirdPart_redirect_url"));
        String url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + SysConfig.getValue("thirdPart_weixin_app_id")
                + "&redirect_uri=" + redirectUri
                + "&response_type=code"
                + "&scope=snsapi_userinfo"
                //这个state最后会在跳转后在redirect_url后面携带过去,同样微信code也可以携带过去,显示的现象是redirect_url实际的url后面多了?code=xxxxx&state=xxx 这样的内容。
                //如果是java后端的action,可以通过方法定义中的String code,Long state来接收这两个参数
                + "&state=" + state
                + "#wechat_redirect";
        logger.info("url>>>>>>>>>>>>>>>>>>>>>>>>>>" + url);
        //要注意的是这里必须是重定向的,如果是返回json类型的,证明是不可行的,不要再走死胡同了
        return "redirect:" + url;
    }

下图为scope等于snsapi_userinfo时的授权页面:
微信公共平台接入之:网页授权(微信授权,微信access_token获取,获取微信用户信息),微信开发者工具使用,微信公众平台测试号申请接入_第4张图片

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

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

错误返回码说明如下:

返回码 说明
10003   redirect_uri域名与后台配置不一致
10004   此公众号被封禁
10005   此公众号并没有这些scope的权限
10006   必须关注此测试号
10009   操作太频繁了,请稍后重试
10010   scope不能为空
10011   redirect_uri不能为空
10012   appid不能为空
10013   state不能为空
10015   公众号未授权第三方平台,请检查授权状态
10016   不支持微信开放平台的Appid,请使用公众号Appid

第二步:通过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"}

案例:

/**
     * 微信回调,获取微信access_ticket,获取用户信息接口
     * @param response
     * @param request
     * @param code          :微信code
     * @return
     */
    @ResponseBody
    @RequestMapping("/accreditCallback")
    public String accreditCallback(
            HttpServletResponse response,
            HttpServletRequest request,
            String code
            ) {
        log.info("【xxxx分享-授权-回调】微信用户授权获取用户信息开始");
        log.info("【xxxx分享-授权-回调】获取到微信code,code = {},参数params = {}" , code);

        AjaxResponseObj ajaxObj = new AjaxResponseObj();

        try {
            if (StringUtils.isNotBlank(code)) {
                //thirdPart_weixin_app_id  ----> xxxxx_weixin_app_id    ---->     :wxxxxxxxxxxxxxxxx04
                //thirdPart_domain   ----->  xxxxxx_weixin_app_secret    ---->    :http://网页授权域名/
                //thirdPart_weixin_app_secret ---> xxxxx_weixin_app_secret  --->  :d9xxxxxxxxxxxxcca925c92ef
                String authUrl = "https://api.weixin.qq.com/sns/oauth2/access_token?" +
                        "appid=" + SysConfig.getValue("thirdPart_weixin_app_id") +
                        "&secret=" + SysConfig.getValue("thirdPart_weixin_app_secret") +
                        "&code=" + code +
                        "&grant_type=authorization_code";
                String result = HttpClientUtil.getInstance().sendHttpGet(authUrl);
                JSONObject json = JSONObject.fromObject(result);
                log.info("【xxxx分享-授权-回调】返回的用户信息是:{}",json.toString());
                log.info("【xxxx分享-授权-回调】微信用户授权回调返回的状态值:" + (!json.containsKey("errcode")));
                if(!json.containsKey("errcode")) { //调用正确
                    //获取openid
                    String openId = json.getString("openid");
                    String access_token = json.getString("access_token");
                    logger.info("access_token=============" + access_token);
                    XXXXSysUserThreeInfo tb = new XXXXSysUserThreeInfo();
                    tb.setUid(openId);
                    tb.setOpenid(openId);
                    tb.setType(SysUserThreeTypeEnum.REDENVELOPE.getValue());
                    tb.setAccessToken(access_token);
                    //通过access_token, openId获取微信的用户信息
                    WeiXinUserResponse weiXinUserResponse= WeiXinUtil.getScopeUsersDetail(access_token, openId);
                    logger.info("=================微信用户信息 start===================");
                    logger.info(weiXinUserResponse.toString());
                    logger.info("=================微信用户信息 end===================");

                    if(null != weiXinUserResponse) {
                        //tb.setUid(weiXinUserResponse.getUnionid());
                        //替换昵称存在特殊字符串情况,使用*替换
                        tb.setName(StringUtil.removeFourChar(filterEmoji(weiXinUserResponse.getNickname())));
                        log.info("【xxx分享-授权-回调】用户详情获取成功,昵称:" + weiXinUserResponse.getNickname()+":转换后昵称:"+tb.getName());
                        tb.setIconurl(weiXinUserResponse.getHeadimgurl());
                    }

                    //这里略去若干敏感代码

                    if (null != xxxSysUser) {
                        String redirectUrl = SysConfig.getValue("thirdPart_redirect_url");
                        if (StringUtils.isNotEmpty(redirectUrl)) {
                            //获取到系统内的ticket
                            String loginTicket = createLoginTicket(user,userTypestr,2);
                            Map callBackData = new HashMap();
                            callBackData.put("ticket",loginTicket);

                            ajaxObj.setData(callBackData);
                            ajaxObj.setStatus(1);
                            ajaxObj.setShowMessage("成功获取用户信息");

                            //注意,这里是使用的ajax接口返回,若项目没有做前后端分离,可以直接使用redirect:url的方式重定向到相应的页面,并可以可以把相应的参数携带过去。
                            return ajaxObj.getJsonpData(request,response);
                        }
                    }
                }else{
                    //略去若干敏感代码
                    ajaxObj.setStatus(0);
                    ajaxObj.setData("");
                    ajaxObj.setShowMessage("获取用户信息失败");

                    return ajaxObj.getJsonpData(request,response);
                }
            }

            ajaxObj.setStatus(0);
            ajaxObj.setShowMessage("微信code为空");
            ajaxObj.setData("");
            return ajaxObj.getJsonpData(request,response);
        } catch (Exception e) {
            //略
            return ajaxObj.getJsonpData(request,response);
        }
    }

    /**
     * 过滤表情
     * @param source
     * @return
     */
    public static String filterEmoji(String source) {
        if(StringUtils.isNotEmpty(source)) {
            Pattern emoji = Pattern.compile ("[\ud83c\udc00-\ud83c\udfff]|[\ud83d\udc00-\ud83d\udfff]|[\u2600-\u27ff]",Pattern.UNICODE_CASE | Pattern . CASE_INSENSITIVE ) ;
            Matcher emojiMatcher = emoji.matcher(source);
            if ( emojiMatcher.find()) {
                source = emojiMatcher.replaceAll("*");
                return source ;
            }
            return source;
        }
        return source;
    }

第三步:刷新access_token(如果需要)

由于access_token拥有较短的有效期,当access_token超时后,可以使用refresh_token进行刷新,refresh_token有效期为30天,当refresh_token失效之后,需要用户重新授权。

请求方法

获取第二步的refresh_token后,请求以下链接获取access_token:
https://api.weixin.qq.com/sns/oauth2/refresh_token?appid=APPID&grant_type=refresh_token&refresh_token=REFRESH_TOKEN
参数  是否必须    说明
appid   是   公众号的唯一标识
grant_type  是   填写为refresh_token
refresh_token   是   填写通过access_token获取到的refresh_token参数

返回说明

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

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

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

附:检验授权凭证(access_token)是否有效

请求方法

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

参数说明

参数  描述
access_token    网页授权接口调用凭证,注意:此access_token与基础支持的access_token不同
openid  用户的唯一标识

返回说明
正确的JSON返回结果:

{ "errcode":0,"errmsg":"ok"}

错误时的JSON返回示例:

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

案例:


public class WeiXinUtil {

    //网页授权获取用户信息
    public static final String GET_SCOPE_USER = "https://api.weixin.qq.com/sns/userinfo";

    /**
     * 根据openid获取用户信息
     * @param access_token
     * @param open_id
     * @return
     */
    public static WeiXinUserResponse getScopeUsersDetail(String access_token,String open_id){
        WeiXinUserResponse tb=null;
        try{
            String url = GET_SCOPE_USER+"?access_token=" + access_token+"&openid="+open_id+"&lang=zh_CN";
            //URLConnectionUtil 可以通过编写httpclient来替换
            String result  = URLConnectionUtil.getHttps(url);
            if(StringUtils.isNotEmpty(result)){
                JSONObject jo=JSONObject.fromObject(result);
                if(!jo.containsKey("errcode")){
                    jo.remove("privilege");
                    tb=(WeiXinUserResponse)JSONObject.toBean(jo, WeiXinUserResponse.class);
                }
            }
        }catch(Exception e){
            e.printStackTrace();
        }
        return tb;
    }
}


import java.io.Serializable;

import org.apache.ibatis.type.Alias;
@Alias("WeiXinUserResponse")
public class WeiXinUserResponse implements Serializable{

    private static final long serialVersionUID = 1L;
    private String openid;//    用户的唯一标识
    private String nickname;//  用户昵称
    private String sex;//   用户的性别,值为1时是男性,值为2时是女性,值为0时是未知
    private String province;//  用户个人资料填写的省份
    private String city;//  普通用户个人资料填写的城市
    private String country;//   国家,如中国为CN
    private String headimgurl;//    用户头像,最后一个数值代表正方形头像大小(有0、46、64、96、132数值可选,0代表640*640正方形头像),用户没有头像时该项为空。若用户更换头像,原有头像URL将失效。
    private String unionid;//


    public String getOpenid() {
        return openid;
    }
    public void setOpenid(String openid) {
        this.openid = openid;
    }
    public String getNickname() {
        return nickname;
    }
    public void setNickname(String nickname) {
        this.nickname = nickname;
    }
    public String getSex() {
        return sex;
    }
    public void setSex(String sex) {
        this.sex = sex;
    }
    public String getProvince() {
        return province;
    }
    public void setProvince(String province) {
        this.province = province;
    }
    public String getCity() {
        return city;
    }
    public void setCity(String city) {
        this.city = city;
    }
    public String getCountry() {
        return country;
    }
    public void setCountry(String country) {
        this.country = country;
    }
    public String getHeadimgurl() {
        return headimgurl;
    }
    public void setHeadimgurl(String headimgurl) {
        this.headimgurl = headimgurl;
    }
    public String getUnionid() {
        return unionid;
    }
    public void setUnionid(String unionid) {
        this.unionid = unionid;
    }
    public static long getSerialversionuid() {
        return serialVersionUID;
    }

}

3、公众号网页测试

在前端开发的时候,如果是写一点然后在手机微信里面进行测试,这种方式显的低效,微信提供了一个“微信开发者工具”关于前端的调试,可以再这个工具中进行调试:
下载地址:https://mp.weixin.qq.com/debug/wxadoc/dev/devtools/download.html?t=201714
微信公共平台接入之:网页授权(微信授权,微信access_token获取,获取微信用户信息),微信开发者工具使用,微信公众平台测试号申请接入_第5张图片

下载安装后工具界面如下:
微信公共平台接入之:网页授权(微信授权,微信access_token获取,获取微信用户信息),微信开发者工具使用,微信公众平台测试号申请接入_第6张图片

如果调试的时候,发现提示”未绑定开发者账号”,需要到微信公众平台中“开发者工具”(名称忘记了,好像是这个)去绑定自己的微信账号。

在绑定过程中提示 要绑定的微信的 需要关注”公众平台安全助手”,然后打开自己的手机,登录自己的微信,进入”发现”频道–>在搜索栏输入”公众平台安全助手”,然后进行关注。
微信公共平台接入之:网页授权(微信授权,微信access_token获取,获取微信用户信息),微信开发者工具使用,微信公众平台测试号申请接入_第7张图片

然后在回到公众平台管理后台,进行设置。设置完成后,发现还需要进入自己的手机上的”公众平台安全助手”中接受并同意邀请。
界面类似:
微信公共平台接入之:网页授权(微信授权,微信access_token获取,获取微信用户信息),微信开发者工具使用,微信公众平台测试号申请接入_第8张图片

3、微信公众平台测试号申请接入案例

http://blog.csdn.net/u014427391/article/details/50963607

http://blog.csdn.net/mzmzo/article/details/50991616

4、微信公众平台项目部署

你可能感兴趣的:(#,微信公众号,服务号,订阅号,小程序,微信)