具体接入方式可参考官方介绍:https://open.dingtalk.com/document/orgapp-server/tutorial-obtaining-user-personal-information
本文只演示vue+springboot如何实现钉钉扫码登录
前端实现
使用钉钉提供的扫码登录页面
<template>
<a :href="getDingtalkLoginAddr()" title="使用钉钉账号登录">
<img src="/img/dingtalk.svg" alt="钉钉登录">
</a>
</template>
<script>
export default {
name: "login",
data() {
return {
};
},
methods: {
getDingtalkLoginAddr() {
// client_id值为appKey,state值随便填,会在回调接口中原样返回
return 'https://login.dingtalk.com/oauth2/auth?redirect_uri=' + encodeURIComponent('http://127.0.0.1:8080/ddlogin/qrlogin') + '&response_type=code&client_id=' + 'xxx' + '&scope=openid&state=test&prompt=consent'
}
}
};
</script>
自己嵌入扫码页面
全局定义ddlogin.js
官方文档给的ddlongin.js是这个:https://g.alicdn.com/dingding/h5-dingtalk-login/0.21.0/ddlogin.js
也可以使用最新版本:https://g.alicdn.com/dingding/h5-dingtalk-login/0.30.1/ddlogin.js
新建src/util/ddlogin.js,内容如下:
// 将上述ddlogin.js链接返回内容粘贴到这里
const loginFrame = window.DTFrameLogin;
export default loginFrame;
在main.js中全局引入ddlogin.js
import loginFrame from '@/util/ddlogin.js'
// Vue.prototype.$ddLogin = loginFrame // vue2.x写法
app.config.globalProperties.$ddLogin = loginFrame // vue3.x写法
定义扫码页面
新建dingtalkLogin.vue
<template>
<div id="self_defined_element" class="self-defined-classname"></div>
</template>
<script>
export default {
name: "dingtalkLogin",
data() {
return {
redirectUri: 'http://127.0.0.1:8080/ddlogin/qrlogin', // 扫码后回调URL
appId: 'xxx', // 应用的AppKey
state: 'test' // 随便指定,扫码后会原样返回
}
},
mounted() {
this.$ddLogin({
id: 'self_defined_element',
width: 300,
height: 300,
},
{
redirect_uri: encodeURIComponent(this.redirectUri),
client_id: this.appId,
scope: 'openid', // 固定值
response_type: 'code', // 固定值
state: this.state,
prompt: 'consent', // 值为consent时需要授权确认
},
loginResult => {
console.log(loginResult)
const {redirectUrl, authCode, state} = loginResult;
// 这里可以直接进行重定向
window.location.href = redirectUrl;
// 也可以在不跳转页面的情况下,使用code进行授权
console.log(authCode);
},
errorMsg => {
// 这里一般需要展示登录失败的具体原因
alert(`Login Error: ${errorMsg}`);
});
}
}
</script>
<style scoped>
.self-defined-classname {
width: 300px;
height: 300px;
}
</style>
后端实现
依赖项
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>dingtalk</artifactId>
<version>1.5.31</version>
</dependency>
回调接口
/**
* 扫码登录(用于钉钉应用“登录与分享”菜单“回调域名”)
*
* @param authCode 授权码,通过扫码获取
* @param state 用户自己设置的值
* @throws Exception 异常对象
*/
@GetMapping("/ddlogin/qrlogin")
public void login(@RequestParam("authCode") String authCode, @RequestParam("state") String state) throws Exception {
// 通过授权码获取access_token
GetUserTokenResponse userTokenResponse = getUserAccessToken("appKey", "appSecret", authCode, "authorization_code");
// 通过token获取当前用户信息
GetUserResponse me = getUserWithOptions(userTokenResponse.getBody().getAccessToken(), "me");
String mobile = me.getBody().getMobile();
// 通过手机号匹配当前系统用户
User user = getUserByMobile(mobile);
// 自己实现登录逻辑,例如跳转到系统首页或结合自己的第三方认证逻辑进行跳转等
// ...
}
public GetUserResponse getUserWithOptions(String accessToken, String unionId) throws Exception {
// 准备请求配置参数
Config config = new Config();
// 设置请求协议
config.protocol = "https";
// 设置请求区域
config.regionId = "central";
// 初始化账号Client
com.aliyun.dingtalkcontact_1_0.Client client = new com.aliyun.dingtalkcontact_1_0.Client(config);
GetUserHeaders getUserHeaders = new GetUserHeaders();
getUserHeaders.xAcsDingtalkAccessToken = accessToken;
return client.getUserWithOptions(unionId, getUserHeaders, new RuntimeOptions());
}
public GetUserTokenResponse getUserAccessToken(String appKey, String appSecret, String authCode, String grantType) throws Exception {
// 准备请求配置参数
Config config = new Config();
// 设置请求协议
config.protocol = "https";
// 设置请求区域
config.regionId = "central";
// 初始化账号Client
com.aliyun.dingtalkoauth2_1_0.Client client = new com.aliyun.dingtalkoauth2_1_0.Client(config);
GetUserTokenRequest getUserTokenRequest = new GetUserTokenRequest()
.setClientId(appKey)
.setClientSecret(appSecret)
.setCode(authCode)
.setGrantType(grantType);
return client.getUserToken(getUserTokenRequest);
}