我们开发了一个钉钉应用,但是客户要求手机端要使用微信。这样的话,我们给客户的解决方案是后台管理功能监控功能使用的钉钉(主要是依托于钉钉的人员,部门,角色的管理,我们应用本身就没有做相关的维护页面),手机端使用公众号。
这样的话,就带来了一个问题,由于我们应用是依托于钉钉的,没有自己的账号体系。所以就必须使用钉钉的账号来登录。
1、前后端分离。前端使用的vue
2、后端是springboot 工程
(1)技术选型经过
1、首先我们是想用微信小程序来开发,但是发现小程序的web-view没法实现,尤其是我们还是用的钉钉,光是跳转域名的审核就没法通过,审核需要的验证文件我们根本没办法放到钉钉的域名下。所以就选择普通的H5工程。
2、找到钉钉官方文档,这里有两种方式,扫码和账号密码。
这里由于我们本就是为了手机端做的,扫码登录这个就不考虑了。选用账号密码。
(2)步骤
接下来是步骤,基本上是按照官方文档来的,不过有些地方我加上了截图
1、首先是登录开发者后台,选择应用开发 > 移动接入应用 > 登录,然后单击创建扫码登录应用授权,创建用于免登过程中验证身份的appId及appSecret,创建后即可看到appId和appSecret。
位置:
单击“创建扫码登录应用授权”按钮,弹出以下对话框
加完之后,能够得到AppId和appSecret。
注:所有的字段中,只有描述没有实际的用途,其他的字段都很重要
2、构造要跳转的链接
构造如下跳转链接,此链接处理成功后,会重定向跳转到指定的redirect_uri,并向url追加临时授权码code及state两个参数。
1 |
|
根据我上面的参数,构造的url是
1 |
|
其中需要注意的是:
redirect_uri 的参数值必须是申请扫码登录授权应用的回调url。如果一直但是还是有问题,看看是否是没有urlencode编码导致的。
在浏览器中查看构造好的url
如果错了的话,提示
如果正确的话,就能够跳转到第三方登录页面。图片因为用的百度的,是403 (Forbidden)了。这个换成可用的图片url就可以了。
单击登录账号,
输入手机号和密码,单击授权登录,就会跳转到我们指定的回调地址上并添加上临时授权码
我们的工程是vue,所以我的回调地址直接就是前段的地址。
这里前端从url中拿到临时授权码,用这个授权码请求我的后端,获取用户的信息,完成整个登录过程
后端代码
controller
@ApiOperation("第三方网站使用钉钉账号登录")
@RequestMapping(value = "/third-party/login", method = RequestMethod.GET)
public ResponseMessage<DingUserDto> thirdPartyLogin(@ApiParam("临时授权码") @RequestParam("code") @NotBlank(message = "授权Code 不能为空") String code) throws Exception {
return ResponseMessage.ok(dingUserService.thirdPartyLogin(code));
}
service
获取用户信息的步骤,首先是unionId。
这个unionId是用户在钉钉的唯一标识。我的理解是手机号的替代。因为手机号虽然也具有唯一性,但是信息太敏感,所以给用户添加了一个unionId来唯一区分各个用户。
我们在进行应用开发的时候还有一个概念叫userId。这个userId是针对组织而言的。也就是,同样一个用户,不同的组织当中userId是不一样的。
举例子
我用手机号(13512341234)申请了一个钉钉账号,我会获得一个unionId,比如dingunion1234
后来我加入了公司A,那么我的unionId依然还是dingunion1234,我在这个公司的urserId是dinguser987654
我同时又加入了公司B,那么我的unionId依然还是dingunion1234,但是我在公司B的urserId就变成了dinguser29387928
这样的话其实就可以看到,unionId + 企业corpId就可以唯一确定userId
后面也是这么获取用户信息的,只不过企业coprId要换成企业accessToken
1、通过临时授权码(一次性的,用过就失效)获取用户的unionId。
public OapiSnsGetuserinfoBycodeResponse getUserInfoByCode(String code) {
DefaultDingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/sns/getuserinfo_bycode");
OapiSnsGetuserinfoBycodeRequest reqBycodeRequest = new OapiSnsGetuserinfoBycodeRequest();
// code临时授权码
reqBycodeRequest.setTmpAuthCode(code);
OapiSnsGetuserinfoBycodeResponse response;
try {
response = client.execute(reqBycodeRequest, dingSnsPropertiesConfig.getAppId(), dingSnsPropertiesConfig.getAppSecret());
} catch (ApiException e) {
log.info(e.toString(), e);
return null;
}
if (response == null || !response.isSuccess()) {
return null;
}
return response;
}
2、获取企业accessToken
public static OapiServiceGetCorpTokenResponse getCorpTokenUtil(String corpId, String suiteTicket, String suiteKey, String suiteSecret) {
long timestamp = System.currentTimeMillis();
String signature = DingTalkSignatureUtil.computeSignature(suiteSecret, DingTalkSignatureUtil.getCanonicalStringForIsv(timestamp, suiteTicket));
Map<String, String> params = new LinkedHashMap<>();
params.put("timestamp", "" + timestamp);
params.put("suiteTicket", suiteTicket);
params.put("accessKey", suiteKey);
params.put("signature", signature);
String queryString = DingTalkSignatureUtil.paramToQueryString(params, "utf-8");
DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/service/get_corp_token?" + queryString);
OapiServiceGetCorpTokenRequest request = new OapiServiceGetCorpTokenRequest();
request.setAuthCorpid(corpId);
try {
return client.execute(request, suiteKey, suiteSecret, suiteTicket);
} catch (ApiException e) {
e.printStackTrace();
throw new DingApiException("获取企业 token 异常,详情:" + e.getErrCode() + "-" + e.getErrMsg() + ":" + e.getSubErrCode() + "-" + e.getSubErrMsg());
} finally {
log.info("GetCorpTokenUtil 耗时:{}", System.currentTimeMillis() - timestamp);
}
}
3、根据企业的accessToken和unionId获取用户信息
public OapiUserGetbyunionidResponse getByUnionId(String unionId, String accessToken) {
DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/user/getbyunionid");
OapiUserGetbyunionidRequest req = new OapiUserGetbyunionidRequest();
req.setUnionid(unionId);
OapiUserGetbyunionidResponse response;
try {
response = client.execute(req, accessToken);
} catch (ApiException e) {
log.info(e.toString(), e);
return null;
}
if (response == null || !response.isSuccess()) {
return null;
}
return response;
}
到此为止,我们获取到了该用户的userid,名称,角色,corpId等信息。
剩下的根据自己的业务做处理就行了。