【微信登录】Java实现微信网页授权与APP授权

前言

微信登录网页授权与APP授权
微信JSAPI支付
微信APP支付
微信APP和JSAPI退款
支付宝手机网站支付
支付宝APP支付
支付宝退款
以上我都放到个人公众号,搜一搜:JAVA大贼船,文末有公众号二维码!觉得个人以后开发会用到的可以关注一下哦!少走点弯路…

官方文档

H5微信登录授权文档地址

https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html

APP微信登录授权

https://developers.weixin.qq.com/doc/oplatform/Mobile_App/WeChat_Login/Development_Guide.html

iOS 接入指南

https://developers.weixin.qq.com/doc/oplatform/Mobile_App/Access_Guide/iOS.html

Android 接入指南

https://developers.weixin.qq.com/doc/oplatform/Mobile_App/Access_Guide/Android.html

流程步骤

H5微信登录授权
  • 引导用户进入授权页面同意授权,获取code
  • 通过code换取网页授权access_token(与基础支持中的access_token不同)
  • 如果需要,开发者可以刷新网页授权access_token,避免过期
  • 通过网页授权access_token和openid获取用户基本信息(支持UnionID机制)

疑问1:scope为snsapi_base和scope为snsapi_userinfo的区别?

snsapi_base是静默授权并自动跳转到回调页的,snsapi_userinfo是需要用户手动同意。

疑问2:网页授权access_token和普通access_token的区别?

普通access_token获取用户信息时,如果用户未关注,信息获取就为空。而网页授权access_token的获取,只要用户许可,就可以获得,不论用户是否关注。

疑问3:UnionID和openid的区别?

unionid对同一个微信开放平台下的不同应用(移动应用、网站应用和公众帐号)都是相同的。

而openid对同一个微信开放平台下的不同应用都是不相同的,如用户授权应用A和应用B,那么用户的两个openid是不相同的,并且一个应用对应一个openid,如用户在次授权给应用A,openid不变。

疑问4:关于UnionID机制

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

踩过的坑
  • redirect_uri域名与后台配置不一致

解决:在公众号设置-功能设置-网页授权,配置前端存放txt文件的路径,如www.xxx.com/static,然后点击提交,可以事先测一下能不能访问到txt的内容。

  • 获取微信用户信息,返回的unionID为空

解决:前往微信开放平台,绑定该公众号,大功告成

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

准备工作

natapp 内网穿透工具

开发阶段可用此工具获取域名

获取AppID和AppSecret

前往微信开放平台(open.weixin.qq.com)查看对应的应用详情

修改授权回调域名

前往公众平台官网中的“公众号功能-功能设置”的配置选项中,根据说明规范配置网页授权回调域名

代码实现(含APP和H5)

配置参数

application.yml

# 微信相关配置                                                                        
wx:
  #商户 ID
  MCH_ID: 
  # 项目基础域名
  BASEURL: 
  #微信登录-用户同意后回调域名(前端域名)
  URL: 
  # 公众号APP_ID
  H_APP_ID: 
  # 公众号秘钥
  H_APP_SECRET: 
  # app的APP_ID
  A_APP_ID: 
  # APP的 秘钥
  A_APP_SECRET: 
  #微信登录-微信授权基本地址
  LOGIN_AUTH_BASE_URL: https://open.weixin.qq.com/connect/oauth2/authorize?
  #微信登录-获取ACCESS_TOKEN的URL
  LOGIN_ACCESS_TOKEN_URL: https://api.weixin.qq.com/sns/oauth2/access_token?
  #微信登录-获取登录人信息的url
  LOGIN_USER_INFO_URL: https://api.weixin.qq.com/sns/userinfo?
  #微信登录-用户同意后回调地址(前端地址)
  LOGIN_RETURN_URL: ${wx.URL}/static/weixinShouQuan.html
  #微信登录-应用授权作用域,snsapi_base (不弹出授权页面,直接跳转,只能获取用户openid),      snsapi_userinfo
  #(弹出授权页面,可通过openid拿到昵称、性别、所在地。并且,即使在未关注的情况下,只要用户授权,也能获取其信息 )
  SCOPE: snsapi_userinfo
读取参数

YmlParament

/**
 * 获取yml参数实体
 */
@Component
@Data
public class YmlParament {
    /*微信相关字段*/
    @Value("${wx.BASEURL}")
    private String baseurl;

    @Value("${wx.H_APP_ID}")
    private String h_app_id;
    
    @Value("${wx.A_APP_ID}")
    private String a_app_id;
    
    @Value("${wx.H_APP_SECRET}")
    private String h_app_secret;
    
    @Value("${wx.A_APP_SECRET}")
    private String a_app_secret;

    @Value("${wx.LOGIN_ACCESS_TOKEN_URL}")
    private String login_access_token_url;

    @Value("${wx.LOGIN_USER_INFO_URL}")
    private String login_user_info_url;

    @Value("${wx.LOGIN_AUTH_BASE_URL}")
    private String login_auth_base_url;

    @Value("${wx.LOGIN_RETURN_URL}")
    private String login_return_url;

    @Value("${wx.SCOPE}")
    private String scope;
}
获取code
  • H5获取code

前端通过后台拿到授权url,然后前端请求该url得到code再请求后台

WxController

    @ApiOperation("获取授权url")
    @PostMapping("/getWeiXinLoginUrl")
    public R getWeiXinLoginUrl() throws Exception {
        String url = ymlParament.getLogin_auth_base_url() + "appid=" + ymlParament.getH_app_id()+ "&redirect_uri=" + ymlParament.getLogin_return_url()
+ "&response_type=code"+ "&scope=snsapi_userinfo" + "&state=STATE#wechat_redirect"; 
        //这里的R是自己自定义的,用于返回结果给前端
        return R.ok().data("redirectUrl", url);
    }
  • APP获取code

APP则是前端使用 SDK 请求授权登录,用户同意授权后得到code再去请求后台

iOS 平台应用授权登录接入代码示例(请参考 iOS 接入指南):

-(void)sendAuthRequest
{
  //构造SendAuthReq结构体
  SendAuthReq* req =[[[SendAuthReq alloc]init]autorelease];
  req.scope = @"snsapi_userinfo";
  req.state = @"123";
  //第三方向微信终端发送一个SendAuthReq消息结构
  [WXApi sendReq:req];
}

Android 平台应用授权登录接入代码示例(请参考 Android 接入指南):

{
  // send oauth request
  Final SendAuth.Req req = new SendAuth.Req();
  req.scope = "snsapi_userinfo";
  req.state = "wechat_sdk_demo_test";
  api.sendReq(req);
}
通过code换取网页授权access_token,然后通过access_token和openid拉取用户信息

WxController

/*H5和app都可以调用*/
    @ApiOperation("获取微信用户信息")
    @PostMapping(value = "/getWxUserInFo")
    public R getWxUserInFo(@RequestBody String body) throws Exception {
        String state = JacksonUtil.parseString(body, "state");
        String code = JacksonUtil.parseString(body, "code");
        //标志哪一个应用,用来获取对应的appid和appsecret
        Integer openIdType = JacksonUtil.parseInteger(body, "openIdType");
        //1、获取code
        if(IsNull.isNull(code) || IsNull.isNull(state)) {
            return R.error("参数不能为空");
        }
        //2、通过code获取accesstoken,UserWxOpenidEums是用来记录应用的,如type1是xxAPP,type2是xx服务号
        JSONObject accessToken=WxUtils.getAccessTokenByCode(code, 
 openIdType==UserWxOpenidEums.TYPE_1.getKey()?ymlParament.getA_app_id():ymlParament.getH_app_id(),
openIdType==UserWxOpenidEums.TYPE_1.getKey()?ymlParament.getA_app_secret():ymlParament.getH_app_secret(),
ymlParament.getLogin_access_token_url());
        //3、获取用户信息
        JSONObject userinfo=WxUtils.getUserinfo(openIdType==UserWxOpenidEums.TYPE_1.getKey()?ymlParament.getA_app_id():ymlParament.getH_app_id(),
accessToken.getString("openid"), accessToken.getString("access_token"), ymlParament.getLogin_user_info_url());
        if(!IsNull.isNull(userinfo.getString("errcode"))){
            return R.error(userinfo.getString("errmsg"));
    }
        return R.ok().data("token", token).data("userinfo",userinfo);
}

WxUtils

    /**
     * =============>>登录<<=============
     * 第二步
     * 通过code获取access_token
     * 正确时返回的JSON数据包如下:
     * {
          "access_token":"ACCESS_TOKEN",
          "expires_in":7200,
          "refresh_token":"REFRESH_TOKEN",
          "openid":"OPENID",
          "scope":"SCOPE" 
        }
     */
    public static JSONObject getAccessTokenByCode(String code,String appId,String appSecret,String login_access_token_url) throws Exception {
        Map map = new HashMap();
        map.put("appid",appId);
        map.put("secret",appSecret);
        map.put("code",code);
        map.put("grant_type","authorization_code");
        return (JSONObject)JSON.parse(HttpUtil.get(login_access_token_url, map));
    }
    
    /**
     * =============>>登录<<=============
     * 第三步:刷新access_token(如果需要)
     * 正确时返回的JSON数据包如下:
     * { 
          "access_token":"ACCESS_TOKEN",
          "expires_in":7200,
          "refresh_token":"REFRESH_TOKEN",
          "openid":"OPENID",
          "scope":"SCOPE" 
        }
     */
    public static JSONObject refreshAccessToken(String appid,String refresh_token) throws Exception {
        Map map = new HashMap<>();
        map.put("appid",appid);
        map.put("grant_type","refresh_token");
        map.put("refresh_token",refresh_token);
        return (JSONObject)JSON.parse(HttpUtil.get("https://api.weixin.qq.com/sns/oauth2/refresh_token?", map));
    }
    
    /**
     * =============>>登录<<=============
     * 第四部,获取用户信息
     * 通过code获取access_token
     * 正确时返回的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"
        }
     * @throws Exception 
     */
    public static JSONObject getUserinfo(String appid,String openid,String accessToken,String login_user_info_url) throws Exception {
        //先判断下accessToken是否有效
        HashMap param =new HashMap();
        param.put("access_token", accessToken);
        param.put("openid", openid);
        JSONObject check=(JSONObject)JSON.parse(HttpUtil.get("https://api.weixin.qq.com/sns/auth?access_token="+accessToken+"&openid="+openid, param));
        //检验授权凭证(access_token)是否有效,如果accessToken失效了,则刷新accessToken
        if(!check.getString("errcode").equals("0")) {
            accessToken=refreshAccessToken(appid, accessToken).getString("refresh_token");
        }
        param =new HashMap();
        param.put("openid",openid);
    param.put("access_token",accessToken);
        param.put("lang","zh_CN");
    return (JSONObject) JSON.parse(HttpUtil.get(login_user_info_url, param));
    }
工具类

HttpUtil

public static String get(String urlStr, Map parameters) throws IOException {
        URL url = new URL(urlStr);
        HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
        httpURLConnection.setDoInput(true);
        httpURLConnection.setDoOutput(true); // 设置该连接是可以输出的
        httpURLConnection.setRequestMethod("GET"); // 设置请求方式
        httpURLConnection.setRequestProperty("charset", "utf-8");
        PrintWriter pw = new PrintWriter(new BufferedOutputStream(httpURLConnection.getOutputStream()));

        StringBuffer parameter = new StringBuffer();
        parameter.append("1=1");
        for (Entry entry : parameters.entrySet()) {
            parameter.append("&" + entry.getKey() + "=" + entry.getValue());
        }
        pw.write(parameter.toString());// 向连接中写数据(相当于发送数据给服务器)
        pw.flush();
        pw.close();
    
        BufferedReader br = new BufferedReader(new InputStreamReader(httpURLConnection.getInputStream(), "utf-8"));
        String line = null;
        StringBuilder sb = new StringBuilder();
        while ((line = br.readLine()) != null) { // 读取数据
            sb.append(line + "\n");
    }
        br.close();
    return sb.toString();
    }

JacksonUtil

public class JacksonUtil {
    public static String parseString(String body, String field) {
        ObjectMapper mapper = new ObjectMapper();
        JsonNode node;
        try {
            node = mapper.readTree(body);
            JsonNode leaf = node.get(field);
            if (leaf != null) {
                return leaf.asText();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
    
    public static Integer parseInteger(String body, String field) {
        ObjectMapper mapper = new ObjectMapper();
        JsonNode node;
        try {
            node = mapper.readTree(body);
            JsonNode leaf = node.get(field);
            if (leaf != null) {
                return leaf.asInt();
            }
        } catch (IOException e) {
            e.printStackTrace();
      }
        return null;
    }
}
【微信登录】Java实现微信网页授权与APP授权_第1张图片
image

你可能感兴趣的:(【微信登录】Java实现微信网页授权与APP授权)