引言
进公司实习后,分配给我的一个任务是要实现等等登录的一个功能,这篇文章将记录我实现的这个功能的具体步骤。望本文章指能帮助你实现用户登录第三方网站(扫码或账密方式)。
一. 确定实现思路
看官方文档,确定一下大体的实现思路。官方文档提供了两种方法:
1 是使用钉钉提供的页面登录授权
2 是在应用的登录页面内嵌二维码方式登录授权
本文是使用钉钉提供的页面登录授权实现的。
两者之间的区别:
钉钉提供的页面登录授权:用户会被重定向到钉钉的官方登录页面进行授权,页面风格与钉钉一致。钉钉通过调用钉钉的OAuth2.0接口,将用户重定向到钉钉的授权页面,用户登录并授权后,钉钉将重定向回应用指定的回调地址,并附带授权码。
内嵌二维码方式登录授权:在页面中嵌入钉钉提供的二维码,用户扫码后,钉钉会将授权结果通过回调通知应用。内嵌二维码方式需要处理二维码生成和回调,页面登录授权则相对简单。
两者就是二维码提供的形式不一样,其他都类型,都是利用的OAuth登录授权。OAuth 2.0授权的流程如下图所示:
二. 准备工作
需要到钉钉开发者后台创建并配置应用。
创建项目后在基础信息栏可查看client id 和client secret。
设置需要重定向的url, 只有在这里设置后的URL,才能成功进行重定向
二. 核心步骤
(1) 对应上面流程图中的步骤2,构造第三方网站访问地址。当用户选择使用钉钉登录时,页面跳转到https://login.dingtalk.com/oauth2/auth这个地址,并携带redirect_uri,response_type等参数。
https://login.dingtalk.com/oauth2/auth?
redirect_uri=https%3A%2F%2Fwww.aaaaa.com%2Fauth
&response_type=code
&client_id=dingxxxxxxx //应用的AppKey
&scope=openid //此处的openId保持不变
&prompt=consent
以上示例中携带的参数都是必填参数,参数说明:
redirect_uri 授权成功后的重定向的url (注:需要对url进行urlencode)
response_type 固定值为code
Client_id 应用的appkey
scope 授权范围,当前只支持两种输入
openid:授权后可获得用户userid
openid corpid:授权后可获得用户id和登录过程中用户选择的组织id,空格分隔。注意url编码。
Prompt 值为consent时,会进入授权确认页。
(2)对应流程图中的步骤5,用户进行授权后会向上面redirect_uri设置的URL进行跳转,并携带authCode
(3)对应流程图中的步骤6,获取authCode并把authCode传到后端
(4)后端获取到authCode后,用authCode换取用户个人的token
/**
* 获取用户的token
* @param authCode
* @return
*/
@Override
public ContentModel<String> dtkLogin(String authCode) throws Exception {
com.aliyun.dingtalkoauth2_1_0.Client client = authClient();
GetUserTokenRequest getUserTokenRequest = new GetUserTokenRequest()
.setClientId(dingTalkProperties.getClientId())
.setClientSecret(dingTalkProperties.getSecret())
.setCode(authCode)
.setGrantType("authorization_code");
// 获取用户个人token
GetUserTokenResponse getUserTokenResponse = client.getUserToken(getUserTokenRequest);
GetUserTokenResponseBody getUserTokenResponseBody = getUserTokenResponse.getBody();
if (getUserTokenResponseBody == null) {
return ContentModel.error("获取用户个人token时,getUserTokenResponseBody为null");
}
String token = getUserTokenResponseBody.getAccessToken();
/**
* 创建一个用于访问钉钉(DingTalk)OAuth 2.0 API的客户端实例
* @return config
*/
public static com.aliyun.dingtalkoauth2_1_0.Client authClient() {
Config config = new Config();
config.protocol = "https";
config.regionId = "central";
try {
return new com.aliyun.dingtalkoauth2_1_0.Client(config);
} catch (Exception e) {
throw new ServiceException("创建com.aliyun.dingtalkoauth2_1_0.Client失败", e);
}
}
(5)有了用户的token可通过token换取用户的unionid, 但是用unionid并不能直接获取到用户的个人信息,如姓名,电话号码等。需要企业的access_token和unionid或者企业的access_token和用户的token才能获取到用户的userid,有了userid就可以直接获取到用户的基本信息,也就可以操起其它需要使用用户信息的业务操作。
/**
* 获取企业内部的accessToken
* @return accessToken
*/
public String getAccessToken() {
com.aliyun.dingtalkoauth2_1_0.Client client = authClient();
GetAccessTokenRequest getAccessTokenRequest = new GetAccessTokenRequest()
.setAppKey(dingTalkProperties.getClientId())
.setAppSecret(dingTalkProperties.getSecret());
try {
GetAccessTokenResponse getAccessTokenResponse = client.getAccessToken(getAccessTokenRequest);
GetAccessTokenResponseBody getAccessTokenResponseBody = getAccessTokenResponse.getBody();
if (getAccessTokenResponseBody.getAccessToken() != null && !getAccessTokenResponseBody.getAccessToken().isEmpty()) {
return getAccessTokenResponseBody.getAccessToken();
}
} catch (TeaException err) {
if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
throw new ServiceException("获取系统内部accessToken失败", err.code, err.message);
}
} catch (Exception _err) {
TeaException err = new TeaException(_err.getMessage(), _err);
if (!com.aliyun.teautil.Common.empty(err.code) && !com.aliyun.teautil.Common.empty(err.message)) {
throw new ServiceException("获取系统内部accessToken失败", err.code, err.message);
}
}
return null;
}
(6)获取unionid 和企业内部的accessToken后,可用uinonid 和企业内部的accessToken获取userid
**
* 获取userid
* @param token 用户个人的token
* @param unionId
* @return userid
*/
public OapiUserGetbyunionidResponse.UserGetByUnionIdResponse userGetByUnionId(String token, String unionId) {
try {
DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/user/getbyunionid");
OapiUserGetbyunionidRequest req = new OapiUserGetbyunionidRequest();
req.setUnionid(unionId);
req.setHttpMethod("POST");
OapiUserGetbyunionidResponse rsp = client.execute(req, token);
if (rsp != null && rsp.getResult() != null) {
return rsp.getResult();
}
} catch (ApiException e) {
log.error("获取用户userid发生了错误", e);
}
return null;
}
(7)用userid 获取用户的信息
/**
* 获取用户信息
* @param accessToken
* @param userId
* @return UserGetResponse
*/
public OapiV2UserGetResponse.UserGetResponse userGetByUserId(String accessToken, String userId) {
try {
DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/v2/user/get");
OapiV2UserGetRequest req = new OapiV2UserGetRequest();
req.setUserid(userId);
req.setLanguage("zh_CN");
OapiV2UserGetResponse rsp = client.execute(req, accessToken);
return rsp.getResult();
} catch (ApiException e) {
log.error("用userid获取用户信息失败", e);
return null;
}
}
三. Maven依赖
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>dingtalk</artifactId>
<version>1.1.86</version>
</dependency>
总结
根据上面的操作,就完成了使用钉钉进行登录的功能。上面涉及的方法基本在钉钉开放平台中都有讲解。我在实现这个功能的过程中,在获取userid的过程中被卡住过,因为当时我是使用的用户个人的token和unionId去获取userid, 会报错未在白名单内,没有权限的错误,导致接口请求失败。最后,希望这篇文章能够帮助您快速完成这个功能,谢谢!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。