java钉钉微信qq扫码登录

概述

第三方接口其实比较简单,按照文档来操作即可,代码也就那点,最费时间的反而是在对接系统的账号的申请上,不建议个人申请很麻烦,还是让公司运维申请企业账号。

作为一名合格的开人人员,不仅仅是把第三方接口调通拿到你想要的数据就行,对接第三方接口需要注意的反而实他们的接口限制条件,这些不注意的反而会成为系统的卡点(系统用户量很少并发很少可以忽略)。

比如并发量和请求量,这些限制条件是否满足系统需求,接口防刷也需要考虑,第三方的接口请求量一般是有限制的,不注意的话一下子把你和个月的请求数给刷完了。

主要步骤就是:

官网文档:
QQ对接
微信对接
钉钉对接

1、授权

前端页面获取AppId和回调地址组装二维码,用户去微信/钉钉/qq扫码授权页面,用户授权后会自动跳转到我们的回调地址并携带授权码。

2、获取openId

前端传入授权码调用后端接口,后端根据授权码调用微信/qq/钉钉的接口获取用户信息中的openId

3、关联用户完成登录操作

根据openId查询用户授权绑定表,如果存在,直接后台生成对应用户的我们系统的token给前端,完成登录操作。

如果没有,可能有两种情况需要区分,第一种是用户不存在,第二种是用户存在,但是没有绑定过,我们不知道openId对应我们系统的哪个用户,需要页面再次引导用户输入手机号信息判断用户手否存在,如果不存在让用户去注册页面注册,实现注册并绑定,如果存在就登录并绑定。

很久之前第二步获取钉钉/微信/qq用户信息的时候,是可以拿到手机号的,但为了安全性和隐私性早就不会返回这种信息了,所以我们只能依赖openId或者unionId来绑定用户。

数据库设计:

CREATE TABLE `user_auth_bind` ( `unid` varchar(50) NOT NULL COMMENT '主键id',
  `user_code` varchar(50) NOT NULL COMMENT '用户id',
  `auth_unid` varchar(255) NOT NULL COMMENT '授权方唯一id',
  `auth_type` varchar(10) NOT NULL COMMENT '授权类型,wx:微信;dd:钉钉;qq: QQ ',
  `business_type` varchar(10) NOT NULL COMMENT '业务类型,区分多个系统使用',
  PRIMARY KEY (`unid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='第三方登录授权绑定表'

后端核心代码实现:

<dependency>
    <groupId>com.aliyun</groupId>
    <artifactId>dingtalk</artifactId>
    <version>1.5.58</version>
</dependency>

代码:

	String unionId = "";
	try {
	    if (AuthTypeEnum.WECHAT.getType().equals(authType)) {
	        // 微信小程序
	        if (AppKey.APPLET.equals(appKey)) {
	            unionId = getWeChatAppletUnionId(appId, appSecret, code);
	        }else {
	            unionId = getWeChatUnionId(appId, appSecret, code);
	        }
	    }
	    if (AuthTypeEnum.DINGTALK.getType().equals(authType)) {
	        unionId = getDingTalkUnionId(appId, appSecret, code);
	    }
	    if (AuthTypeEnum.QQ.getType().equals(authType)) {
	    // QQ的回调地址必填
	    AuthAppResponseDto authAppInfo = getAuthAppInfo(sid, authType);
	    unionId = getQQUnionId(appId, appSecret, authAppInfo.getRedirectUrl(), code);
	    }
	} catch (Exception e) {
	    logger.error("获取第三方授权登录用户信息异常,errMsg= {}", e.getMessage());
	    throw new BizServiceException(AuthErrorCode.AUTH_LOGIN_APP_GET_USER_INFO_ERROR);
	}



    /**
     * 获取微信小程序唯一id
     * @param appId 应用id
     * @param appSecret 秘钥
     * @param code 授权码
     * @return 唯一id
     */
    private String getWeChatAppletUnionId(String appId, String appSecret, String code) {
        String getAccessTokenUrl = "https://api.weixin.qq.com/sns/jscode2session?appid=" + appId + "&secret=" + appSecret + "&js_code=" + code + "&grant_type=authorization_code";
        String response = restTemplate.getForObject(getAccessTokenUrl, String.class);
        JSONObject accessTokenJson = JSONObject.parseObject(response);
        logger.debug("微信小程序获取access_token返回结果:response={}", accessTokenJson);
        if (!ObjectUtils.isEmpty(accessTokenJson)) {
            if (accessTokenJson.containsKey(AuthLoginKey.UNION_ID) && StringUtils.isNotBlank(accessTokenJson.getString(AuthLoginKey.UNION_ID))) {
                return accessTokenJson.getString(AuthLoginKey.UNION_ID);
            }else {
                // 获取不到unionid就直接获取openid
                return accessTokenJson.getString("openid");
            }
        }
        return "";
	    }
	
	
	   /**
	     * 获取微信用户唯一id
	     *
	     * @param appId     应用id
	     * @param appSecret 应用秘钥
	     * @param code      授权码
	     * @return 唯一id
	     */
	    private String getWeChatUnionId(String appId, String appSecret, String code) {
	        String getAccessTokenUrl = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=" + appId + "&secret=" + appSecret + "&code=" + code + "&grant_type=authorization_code";
	        String response = restTemplate.getForObject(getAccessTokenUrl, String.class);
	        JSONObject accessTokenJson = JSONObject.parseObject(response);
	        logger.debug("微信获取access_token返回结果:response={}", accessTokenJson);
	        if (!ObjectUtils.isEmpty(accessTokenJson)) {
	            if (accessTokenJson.containsKey(AuthLoginKey.UNION_ID) && StringUtils.isNotBlank(accessTokenJson.getString(AuthLoginKey.UNION_ID))) {
	                return accessTokenJson.getString(AuthLoginKey.UNION_ID);
	            }
	            if (accessTokenJson.containsKey("access_token")) {
	                String accessToken = accessTokenJson.getString("access_token");
	                String openId = accessTokenJson.getString("openid");
	                String getUserInfoUrl = "https://api.weixin.qq.com/sns/userinfo?access_token=" + accessToken + "&openid=" + openId + "&lang=zh_CN";
	                String getUserInfoResponse = restTemplate.getForObject(getUserInfoUrl, String.class);
	                JSONObject jsonObject = JSONObject.parseObject(getUserInfoResponse);
	                logger.debug("微信获取用户信息返回结果:response={}", jsonObject);
	                return jsonObject.getString(AuthLoginKey.UNION_ID);
	            }
	        }
	        return "";
	    }

    /**
     * 获取QQ用户唯一id
     *
     * @param appId     应用id
     * @param appSecret 应用秘钥
     * @param redirectUri 回调地址 QQ的必填
     * @param code 授权码
     * @return 唯一id
     */
    private String getQQUnionId(String appId, String appSecret, String redirectUri, String code) {
        String getAccessTokenUrl = "https://graph.qq.com/oauth2.0/token" +
                "?grant_type=authorization_code" +
                "&fmt=json" +
                "&need_openid=1" +
                "&client_id=" + appId +
                "&client_secret=" + appSecret +
                "&redirect_uri=" + URLEncodeUtil.encode(redirectUri) +
                "&code=" + code;
        String response = restTemplate.getForObject(getAccessTokenUrl, String.class);
        JSONObject accessTokenJson = JSONObject.parseObject(response);
        logger.debug("QQ获取access_token返回结果:response={}", accessTokenJson);
        if (!ObjectUtils.isEmpty(accessTokenJson)) {
            if (accessTokenJson.containsKey(AuthLoginKey.OPENN_ID) && StringUtils.isNotBlank(accessTokenJson.getString(AuthLoginKey.OPENN_ID))) {
                return accessTokenJson.getString(AuthLoginKey.OPENN_ID);
            }
        }
        return "";
    }

      /**
     * 获取钉钉用户唯一id
     *
     * @param appId     应用id
     * @param appSecret 应用秘钥
     * @param code      授权码
     * @return 唯一id
     */
    private String getDingTalkUnionId(String appId, String appSecret, String code) throws Exception {
        com.aliyun.dingtalkoauth2_1_0.Client client = authClient();
        GetUserTokenRequest getUserTokenRequest = new GetUserTokenRequest()
                .setClientId(appId)
                .setClientSecret(appSecret)
                .setCode(code)
                .setGrantType("authorization_code");
        GetUserTokenResponse getUserTokenResponse = client.getUserToken(getUserTokenRequest);
        logger.debug("钉钉获取access_token返回结果:response={}", getUserTokenResponse);
        if (!ObjectUtils.isEmpty(getUserTokenResponse.getBody()) && !ObjectUtils.isEmpty(getUserTokenResponse.getBody().accessToken)) {
            String accessToken = getUserTokenResponse.getBody().getAccessToken();
            String userInfo = getUserInfo(accessToken);
            JSONObject jsonObject = JSONObject.parseObject(userInfo);
            logger.debug("钉钉获取用户信息返回结果:response={}", jsonObject);
            return jsonObject.getString("unionId");
        }
        return "";
    }

    /**
     * 获取钉钉用户个人信息
     *
     * @param accessToken 令牌
     * @return 用户信息
     */
    private String getUserInfo(String accessToken) throws Exception {
        com.aliyun.dingtalkcontact_1_0.Client client = contactClient();
        GetUserHeaders getUserHeaders = new GetUserHeaders();
        getUserHeaders.xAcsDingtalkAccessToken = accessToken;
        // 获取用户个人信息,如需获取当前授权人的信息,unionId参数必须传me
        return JSON.toJSONString(client.getUserWithOptions("me", getUserHeaders, new RuntimeOptions()).getBody());
    }

        private static com.aliyun.dingtalkoauth2_1_0.Client authClient() throws Exception {
        Config config = new Config();
        config.protocol = "https";
        config.regionId = "central";
        return new com.aliyun.dingtalkoauth2_1_0.Client(config);
    }

    private static com.aliyun.dingtalkcontact_1_0.Client contactClient() throws Exception {
        Config config = new Config();
        config.protocol = "https";
        config.regionId = "central";
        return new com.aliyun.dingtalkcontact_1_0.Client(config);
    }
相关推荐
魔道不误砍柴功39 分钟前
Java 中如何巧妙应用 Function 让方法复用性更强
java·开发语言·python
NiNg_1_23439 分钟前
SpringBoot整合SpringSecurity实现密码加密解密、登录认证退出功能
java·spring boot·后端
闲晨42 分钟前
C++ 继承:代码传承的魔法棒,开启奇幻编程之旅
java·c语言·开发语言·c++·经验分享
cs_dn_Jie2 小时前
钉钉 H5 微应用 手机端调试
前端·javascript·vue.js·vue·钉钉
测开小菜鸟2 小时前
使用python向钉钉群聊发送消息
java·python·钉钉
P.H. Infinity3 小时前
【RabbitMQ】04-发送者可靠性
java·rabbitmq·java-rabbitmq
生命几十年3万天3 小时前
java的threadlocal为何内存泄漏
java
caridle3 小时前
教程:使用 InterBase Express 访问数据库(五):TIBTransaction
java·数据库·express
^velpro^4 小时前
数据库连接池的创建
java·开发语言·数据库
苹果醋34 小时前
Java8->Java19的初步探索
java·运维·spring boot·mysql·nginx