微信联合登录--第三方网站微信扫码登录对接

最近公司项目网站需要支持第三方登录,需要对接微信,场景是用户通过微信app扫二维码实现自动登录功能。

效果如下图所示:
微信联合登录--第三方网站微信扫码登录对接_第1张图片
一、前期准备

经过查阅文档,发现微信该功能属于微信开放平台(https://open.weixin.qq.com),所以需要注册相关的资质,等待微信方面的审核,审核通过以后获得相应的权限,拿到开发所需要的APP_ID和APP_SECRET。
微信联合登录--第三方网站微信扫码登录对接_第2张图片
微信联合登录--第三方网站微信扫码登录对接_第3张图片

需要注意的是,在微信开放平台也要设置授权回调域,以前做过微信相关开发的同学应该都知道这个。刚接触微信对接的同学会发现,在本地开发调试的时候,微信的回调不能通知到本地接口。这时就需要内网穿透,工具很多在此就不赘述了。以前我也写过相关的文章,有兴趣的同学可以翻一下。
微信联合登录--第三方网站微信扫码登录对接_第4张图片
工具界面如下,通过访问指定的域名就可以访问到本地项目了。
微信联合登录--第三方网站微信扫码登录对接_第5张图片

至此,开发前的准备工具基本告一段落,接下来开始代码编写。

二、代码编写

1、定义常量

public class Constant {
/**
     * 验证是否来自微信服务器的请求.
     */
    public static final String STATE = "xxxxxxxxxxxx";

    /**
     * 功能描述: 微信相关--微信网页登录地址.
     *
     * @param
     * @return
     * @author zz
     * @date 2019/5/27 11:30
     */
    public static final String WECHAT_QR_URL = "https://open.weixin.qq.com/connect/qrconnect?appid=";

    /**
     * 功能描述: 跳转路径.
     *
     * @param
     * @return
     * @author zz
     * @date 2019/5/27 11:36
     */
    public static final String REDIRECT_URI = "/api/authcenter/weChat/callback";
    /**
     * 功能描述:  绑定.
     *
     * @param null
     * @return
     * @author zz
     * @date 2019/6/14 13:56
     */
    public static final String REDIRECT_URI_BIND = "/api/authcenter/weChat/callback/bind";
    /**
     * 获取openId.
     */
    public static final String ACCESS_TOKEN_URL = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=";

    /**
     * 公众号APPID.
     */
    public static final String APP_ID = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";

    /**
     * APP秘钥.
     */
    public static final String APP_SECRET = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxx";

    /**
     * 获取用户信息.
     */
    public static final String GET_WECHAT_USER_INFO = "https://api.weixin.qq.com/sns/userinfo?access_token=";
    }

2、写了一个工具类,将部分方法封装起来,项目里直接去调用。

public class WeChatUtil {

    /**
     * 功能描述:  获取微信网页登录地址.
     *
     * @param redirectUri redirectUri
     * @return url
     * @author zz
     * @date 2019/5/27 11:26
     */
    public static String openWeChatLogin(String redirectUri) {
        String url = Constant.WECHAT_QR_URL + Constant.APP_ID + "&redirect_uri=" + URLEncoder.encode(redirectUri) +
                "&response_type=code&scope=snsapi_login&state=" + Constant.STATE + "#wechat_redirect";
        return url;
    }

    /**
     * 功能描述:  微信扫码绑定地址.
     *
     * @param redirectUri redirectUri
     * @return url
     * @author zz
     * @date 2019/6/14 13:42
     */
    public static String bind(String redirectUri, String userId) {
        String url = Constant.WECHAT_QR_URL + Constant.APP_ID + "&redirect_uri=" + URLEncoder.encode(redirectUri) +
                "&response_type=code&scope=snsapi_login&state=" + Constant.STATE + "_" + userId + "#wechat_redirect";
        return url;
    }

    /**
     * 功能描述:  微信授权获取openid和access_token.
     *
     * @param code code
     * @return map
     * @author zz
     * @date 2019/5/24 17:15
     */
    public static Map getOauth2(String code) {
        Map map = new HashMap();

        String url = Constant.ACCESS_TOKEN_URL + Constant.APP_ID + "&secret=" + Constant.APP_SECRET + "&code=" + code + "&grant_type=authorization_code";
        String result = HttpUtil.httpRequest(url);
        if (!Strings.isNullOrEmpty(result)) {
            JSONObject json = JSON.parseObject(result);
            if (json.get("errcode") != null) {
                log.debug("code参数异常,获取accessToken失败");
                return map;
            }
            String accessToken = json.getString("access_token");
            String openid = json.getString("openid");
            String unionid = json.getString("unionid");
            if (!Strings.isNullOrEmpty(accessToken)) {
                map.put("accessToken", accessToken);
            }
            if (!Strings.isNullOrEmpty(openid)) {
                map.put("openid", openid);
            }
            if (!Strings.isNullOrEmpty(unionid)) {
                map.put("unionid", unionid);
            }
            log.debug("获取到的参数--" + map.toString());
        } else {
            log.debug("请求失败,请重试");
        }
        return map;
    }

    /**
     * 功能描述: 获取微信用户个人信息.
     *
     * @param openId      openId
     * @param accessToken accessToken
     * @return java.conf.Map
     * @author zz
     * @date 2019/5/24 16:42
     */
    public static Map getWeChatInfo(String openId, String accessToken) {
        Map map = new HashMap();

        if (!Strings.isNullOrEmpty(openId) && !Strings.isNullOrEmpty(accessToken)) {
            String weChatUrl = Constant.GET_WECHAT_USER_INFO + accessToken + "&openid=" + openId + "&lang=zh_CN";
            String userInfo = HttpUtil.httpRequest(weChatUrl);
            if (!Strings.isNullOrEmpty(userInfo)) {
                JSONObject json = JSONObject.parseObject(userInfo);
                if (json.get("errcode") != null) {
                    log.debug("openid或accessToken参数异常,获取微信个人信息失败");
                    return map;
                }
                String nickName = json.getString("nickname");
                String headimgurl = json.getString("headimgurl");
                //普通用户性别,1为男性,2为女性
                Integer sex = json.getInteger("sex");
                String province = json.getString("province");
                String city = json.getString("city");
                String country = json.getString("country");
                String unionid = json.getString("unionid");
                if (!Strings.isNullOrEmpty(nickName)) {
                    map.put("nickName", nickName);
                }
                if (!Strings.isNullOrEmpty(headimgurl)) {
                    map.put("headimgurl", headimgurl);
                }
                if (sex != null) {
                    map.put("sex", sex);
                }
                if (!Strings.isNullOrEmpty(province)) {
                    map.put("province", province);
                }
                if (!Strings.isNullOrEmpty(city)) {
                    map.put("city", city);
                }
                if (!Strings.isNullOrEmpty(country)) {
                    map.put("country", country);
                }
                if (!Strings.isNullOrEmpty(unionid)) {
                    map.put("unionid", unionid);
                }
                log.debug("获取到的参数--" + map.toString());
            } else {
                log.debug("获取微信用户信息失败");
            }
        } else {
            log.debug("请求参数不全");
        }
        return map;
    }

    /**
     * 功能描述:  测试.
     *
     * @param
     * @return
     * @author zz
     * @date 2019/5/28 10:34
     */
    public static void main(String[] args) {
        System.out.println("获取微信跳转地址--" + openWeChatLogin("http://tonyfreak.free.idcfengye.com"));
        System.out.println("获取openid和access_token--" + getOauth2("222"));
        System.out.println("获取微信个人信息--" + getWeChatInfo("1", "333").toString());
    }
}

3、控制层

项目前后端分离,前端访问接口,后端将参数进行拼接,返回访问地址。

/**
     * 功能描述: 获取微信第三方登录路径.
     *
     * @param
     * @return
     * @author zz
     * @date 2019/5/24 16:15
     */
    @ApiOperation("获取微信第三方登录路径")
    @GetMapping(value = "/login", produces = MediaType.APPLICATION_JSON_VALUE)
    public Result openWeChatLogin(HttpServletResponse httpServletResponse) throws Exception {
        log.debug("微信跳转链接--" + WeChatUtil.openWeChatLogin(redirectUri + Constant.REDIRECT_URI));
        //httpServletResponse.sendRedirect(WeChatUtil.openWeChatLogin(redirectUri + Constant.REDIRECT_URI));
        return new Result(Constant.MSGSUCCESS, WeChatUtil.openWeChatLogin(redirectUri + Constant.REDIRECT_URI));
    }

获取到的地址:

{
  "code": 200,
  "message": "操作成功",
  "object": "https://open.weixin.qq.com/connect/qrconnect?appid=XXXXXXXX&redirect_uri=http%3A%2F%2Fdev.XXXXXX.com%2Fapi%2Fauthcenter%2FweChat%2Fcallback&response_type=code&scope=snsapi_login&state=XXXXXX#wechat_redirect",
  "map": {}
}

访问该地址,跳转到开头提到的那张图,显示微信生成的二维码。

接着用户在手机上用微信app扫码,进行授权,点击同意后跳转相应页面。
如下图:

微信联合登录--第三方网站微信扫码登录对接_第6张图片
回调方法:


/**
     * 功能描述:  微信扫码登录第三方网站回调接口.
     *
     * @param httpServletRequest httpServletRequest
     * @return result
     * @author zz
     * @date 2019/5/24 16:23
     */
    @ApiOperation("微信扫码登录第三方网站回调接口--(微信自己调用该接口,不需要前端手动调用)")
    @GetMapping(value = "/callback")
    public Result openWeChatCallback(HttpServletRequest httpServletRequest, HttpServletResponse response) throws Exception {
        String code = httpServletRequest.getParameter("code");
        String state = httpServletRequest.getParameter("state");
        Object data = null;
        String message = Constant.MSGSUCCESS;
        // 缓存
        Jedis jedis = new Jedis(host, port);
        if (!Strings.isNullOrEmpty(code) && !Strings.isNullOrEmpty(state)) {
            // 判断state是否合法
            if (Constant.STATE.equals(state)) {
                // 获取微信授权
                Map oauth2 = WeChatUtil.getOauth2(code);
                if (oauth2.size() > 0) {
                    String openid = oauth2.get("openid").toString();
                    String accessToken = oauth2.get("accessToken").toString();
                    SysUser user = sysUserService.findByOpenId(openid, Constant.Status.valid, 1);
                    if (user != null) {
                        // 获取老用户
                        SysUserVo sysUserVo = BeanMapper.map(user, SysUserVo.class);
                        String json = JSON.toJSONString(sysUserVo);
                        HttpSession session = httpServletRequest.getSession();
                        session.setAttribute("userinfo", json);
                        sysUserVo.setTokenId(session.getId());
                        sysUserVo.setRegisterStatus(Constant.Status.valid);
                        data = sysUserVo;
                        String newUrl = "http://XXXXXXXXX.com/#/Index?token=" + session.getId();
                        response.sendRedirect(newUrl);
                        return null;
                    } else {
                        // 创建新用户
                        Map weChatInfo = WeChatUtil.getWeChatInfo(openid, accessToken);
                        if (weChatInfo.size() > 0) {
                            SysUser newUser = SysUser.convert(oauth2, weChatInfo);
                            SysUserVo sysUserVo = BeanMapper.map(newUser, SysUserVo.class);
                            sysUserVo.setRegisterStatus(Constant.Status.invalid);
                            data = sysUserVo;
                            String uuid = UuidUtil.uuid2();
                            jedis.set(uuid, JSON.toJSONString(data));
                            String newUrl = "http://XXXXXX.com/#/Register?key=" + uuid;
                            response.sendRedirect(newUrl);

                            return null;
                        } else {
                            message = "获取微信个人信息失败";
                            log.debug(message);
                            response.sendRedirect("http://XXXXXXX.com/#/Register");
                            return null;
                        }
                    }
                } else {
                    message = "获取微信授权失败";
                    log.debug(message);
                    response.sendRedirect("http://XXXXXXXXX.com/#/Register");
                    return null;
                }
            } else {
                message = "非法参数,请重试";
                log.debug(message);
                response.sendRedirect("http://XXXXXXXX.com/#/Register");
                return null;
            }
        } else {
            message = "用户未授权,请授权";
            log.debug(message);
            response.sendRedirect("http://XXXXXXXX.com/#/Register");
            return null;
        }
    }

这里的代码直接重定向到了页面,测试时返回的json如下:

微信联合登录--第三方网站微信扫码登录对接_第7张图片
到此就完成了微信扫码登录网站的功能,回调中的代码根据自己具体的业务需求去编写,整个流程大方向不变。
如有疑问可留言共同讨论。

你可能感兴趣的:(java开发应用)