钉钉一键登录第三方网站
- 序
- 钉钉开发者后台
- H5微应用
- 应用代码开发
- 登录页面login.html
- 登录实现LoginController.java
- pom.xml增加
- 一键登录效果展示
序
企业内部系统已经做过了钉钉扫码登录,现在需要添加钉钉一键登录第三方网站功能,这里主要记录一键登录整个实现步骤。
钉钉开发者后台
想要实现钉钉一键登录,首先需要在钉钉开放平台管理平台配置H5微应用
H5微应用
实现钉钉一键登录第三方网站,官方文档地址:实现登录第三方网站根据官方文档的操作步骤来看的话需要先到钉钉开发者后台添加H5微应用,钉钉开发者后台地址:钉钉开发者后台进入钉钉开发者后台后,
选择【企业内部开发】进入
点击右上角【创建应用】
输入【应用名称】,【应用描述】,上传系统图标或者不上传都可以,点击【确认创建】
创建完成之后你就可以看到你创建的H5微应用的应用凭证,点击左侧菜单【权限管理】
这里需要申请【个人手机号信息】、【通讯录个人信息读权限】两个权限,权限申请完成之后点击菜单【登录与分享】
在输入框中输入回调的域名及对应的方法,点击【添加】完成回调URL的配置,这里因为是三套环境(开发、测试、线上),所以配置三套回调URL,三套环境用同一套应用凭证,当然你也可以选择创建三个H5微应用来支持三套环境。
应用代码开发
首先需要在登录页构造钉钉一键登录跳转链接,构造链接参考官方文档
登录页面login.html
登录页面构造好钉钉一键登录链接后的页面效果
页面代码,在原有的登录页面中添加如下代码
<div style="padding-left: 83px;">
<button type="button" class="btn-dingding" onclick="toDingDing();">钉钉一键授权登录</button>
</div>
同时在页面增加跳转方法
//钉钉一键授权登录
function toDingDing() {
var projectUrl = $("#projectUrl").val();
var h5AppKey = $("#h5AppKey").val();
var redirect_uri = projectUrl + "dingdingOneClickLogin";
redirect_uri = encodeURIComponent(redirect_uri);
self.location="https://login.dingtalk.com/oauth2/auth?redirect_uri="+redirect_uri+"&response_type=code&client_id="+h5AppKey+"&scope=openid&state=&prompt=consent";
}
其中:projectUrl 就是获取的请求域名地址
h5AppKey 就是配置的H5微应用的AppKey
至此,页面的内容添加完毕,下面开始来增加后台Java方法
登录实现LoginController.java
登录实现controller需要增加如下方法
/**
* 登录系统
* @param use
* @return
*/
private String doLoginSystem(SysUser use) {
//已绑定账号则直接登录操作
MyUsernamePasswordToken token = new MyUsernamePasswordToken(use.getUnionId(), use.getPassword(),false,true);
// 登陆主流程
Subject subject = SecurityUtils.getSubject();
subject.login(token);
return redirect("/index");
}
public 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);
}
/**
* 获取用户token
* @param authCode
* @return
* @throws Exception
*/
//接口地址:注意/auth与钉钉登录与分享的回调域名地址一致
@RequestMapping(value = "/dingdingOneClickLogin", method = RequestMethod.GET)
public String getAccessToken(@RequestParam(value = "authCode")String authCode) throws Exception {
com.aliyun.dingtalkoauth2_1_0.Client client = authClient();
GetUserTokenRequest getUserTokenRequest = new GetUserTokenRequest()
//应用基础信息-应用信息的AppKey,请务必替换为开发的应用AppKey
.setClientId(dingDingProperties.getH5AppKey())
//应用基础信息-应用信息的AppSecret,,请务必替换为开发的应用AppSecret
.setClientSecret(dingDingProperties.getH5AppSecret())
.setCode(authCode)
.setGrantType("authorization_code");
GetUserTokenResponse getUserTokenResponse = client.getUserToken(getUserTokenRequest);
//获取用户个人token
String accessToken = getUserTokenResponse.getBody().getAccessToken();
GetUserResponseBody userinfo = getUserinfo(accessToken);
//根据unionId 获取用户信息
String unionId = userinfo.getUnionId();
SysUser use = sysUserService.selectUserByUnionId(unionId);
String msg = "";
if (use != null) {
return doLoginSystem(use);
}else {
//未绑定 则自动添加账号 根据unionid获取userid
OapiUserGetbyunionidResponse.UserGetByUnionIdResponse userIdByUnionIdV2 = dingDingService.getUserIdByUnionIdV2(unionId);
if (userIdByUnionIdV2 != null && StringUtils.isNotEmpty(userIdByUnionIdV2.getUserid())) {
//根据userid获取用户详细信息
OapiV2UserGetResponse.UserGetResponse user = dingDingService.getUserDetailByUserid(userIdByUnionIdV2.getUserid(), null);
if (user != null) {
return saveSysUserAuto(user);
}else {
msg = "one";
}
}else {
msg = "one";
}
}
return redirect("/login?msg="+msg);
}
public 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);
}
/**
* 获取用户个人信息
* @param accessToken
* @return
* @throws Exception
*/
public GetUserResponseBody getUserinfo(String accessToken) throws Exception {
com.aliyun.dingtalkcontact_1_0.Client client = contactClient();
GetUserHeaders getUserHeaders = new GetUserHeaders();
getUserHeaders.xAcsDingtalkAccessToken = accessToken;
//获取用户个人信息,如需获取当前授权人的信息,unionId参数必须传me
GetUserResponseBody res = client.getUserWithOptions("me", getUserHeaders, new RuntimeOptions()).getBody();
return res;
}
private String saveSysUserAuto(OapiV2UserGetResponse.UserGetResponse user) {
List<UserRole> userRoleList = new ArrayList();
//整理需要插入数据库字段
String userid = user.getUserid();
//判断当前用户是否已经插入过了
Long userId = null;
SysUser sysUser = sysUserService.selectUserByDingDingUserid(userid);
if (sysUser != null) {
userId = sysUser.getUserId();
}else {
sysUser = new SysUser();
sysUser.setDingdingUserid(userid);
sysUser.setAvatar(user.getAvatar());
sysUser.setUnionId(user.getUnionid());
sysUser.setPhonenumber(user.getMobile());
sysUser.setEmail(user.getEmail());
sysUser.setTitle(user.getTitle());
String userName = user.getName();
sysUser.setUserName(userName);
//中文转拼音作为loginName
String loginName = PinYinUtils.getPingYin(userName);
//根据当前公司人员重名情况,最大重名6人,故此处设置最大重名为10
StringBuilder sb = new StringBuilder(loginName).append(",");
for (int i = 1; i < 10; i++) {
sb.append(loginName+String.valueOf(i)).append(",");
}
String loginnames = sb.toString();
//判断一下当前是否已经存在了loginname
SysUser uniqueuser = sysUserService.selectLastUserByLoginName(Convert.toStrArray(loginnames));
if (uniqueuser != null) {
//获取当前重名用户的序号
String number = uniqueuser.getLoginName().replace(loginName, "");
//序号增加1在放回去拼接好
int i = 0;
if (StringUtils.isNotEmpty(number)) {
i = Integer.parseInt(number) + 1;
}else {
//说明当前loginname还没有后续数字
i = 1;
}
sysUser.setLoginName(loginName+i);
}else {
sysUser.setLoginName(loginName);
}
sysUser.randomSalt();
//初始未编码前password为123456
sysUser.setPassword(passwordService.encryptPassword(sysUser.getLoginName(), "123456", sysUser.getSalt()));
sysUser.setCreateBy("扫码登录补充用户");
sysUser.setCreateTime(new Date());
sysUserService.insertSysUser(sysUser);
userId=sysUser.getUserId();
//插入用户的角色,初始用户都是普通角色
UserRole ur = new UserRole();
ur.setUserId(userId);
if (Constants.ONE_KEY.equals(user.getUserid()) || Constants.TWO_KEY.equals(user.getUserid())) {
ur.setRoleId(1L);
}else {
ur.setRoleId(2L);
}
userRoleList.add(ur);
}
//插入用户的部门
List<Long> deptIdList = user.getDeptIdList();
if (CollectionUtils.isNotEmpty(deptIdList)) {
//插入部门之前需要清除之前的人员部门关系,以防钉钉部门变动而系统未感知
int dele = sysUserDeptService.deleteSysUserDeptByUserId(userId);
//插入人员部门关系表
SysUserDept userDept = new SysUserDept();
userDept.setUserId(userId);
for (Long deptid : deptIdList) {
userDept.setDeptId(deptid);
userDept.setCreateBy("扫码登录补充用户");
userDept.setCreateTime(new Date());
sysUserDeptService.insertSysUserDept(userDept);
}
}
//插入角色
if (CollectionUtils.isNotEmpty(userRoleList)) {
userRoleMapper.batchUserRole(userRoleList);
}
//登录系统
return doLoginSystem(sysUser);
}
其中dingDingService.getUserIdByUnionIdV2(unionId)方法如下
/**
* 根据unionId获取userId 2.0版本
* @param unionId 当前钉钉用户在当前企业下的唯一识别码
* @return
*/
@Override
public OapiUserGetbyunionidResponse.UserGetByUnionIdResponse getUserIdByUnionIdV2(String unionId) {
try {
String accessToken = getAccessToken();
//根据unionId获取userId
DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/user/getbyunionid");
OapiUserGetbyunionidRequest req = new OapiUserGetbyunionidRequest();
req.setUnionid(unionId);
OapiUserGetbyunionidResponse rsp = client.execute(req, accessToken);
OapiUserGetbyunionidResponse.UserGetByUnionIdResponse result = rsp.getResult();
return result;
} catch (ApiException e) {
e.printStackTrace();
}
return null;
}
dingDingService.getUserDetailByUserid(userIdByUnionIdV2.getUserid(), null)方法如下
/**
* 查询用户详情
* @param userid 钉钉userid
* @param accessToken
* @return
*/
@Override
public OapiV2UserGetResponse.UserGetResponse getUserDetailByUserid(String userid, String accessToken) {
try {
if (StringUtils.isEmpty(accessToken)) {
accessToken = getAccessToken();
}
DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/v2/user/get");
OapiV2UserGetRequest req = new OapiV2UserGetRequest();
req.setUserid(userid);
OapiV2UserGetResponse rsp = client.execute(req, accessToken);
OapiV2UserGetResponse.UserGetResponse result = rsp.getResult();
return result;
} catch (ApiException e) {
e.printStackTrace();
return null;
}
}
PinYinUtils.java类如下
package com.dongao.project.utils;
import net.sourceforge.pinyin4j.PinyinHelper;
import net.sourceforge.pinyin4j.format.HanyuPinyinCaseType;
import net.sourceforge.pinyin4j.format.HanyuPinyinOutputFormat;
import net.sourceforge.pinyin4j.format.HanyuPinyinToneType;
import net.sourceforge.pinyin4j.format.HanyuPinyinVCharType;
import net.sourceforge.pinyin4j.format.exception.BadHanyuPinyinOutputFormatCombination;
/**
* @ClassName:PinYinUtils
* @author:dongao
* @date 2022/2/10 14:05
*/
public class PinYinUtils {
/**
* 中文转拼音
* @param inputStr
* @return
*/
public static String getPingYin(String inputStr) {
if (inputStr == null || "".equals(inputStr)) {
return "";
}
HanyuPinyinOutputFormat format = new HanyuPinyinOutputFormat();
//UPPERCASE 大写 LOWERCASE 小写
format.setCaseType(HanyuPinyinCaseType.LOWERCASE);
//WITH_TONE_NUMBER 音标用数字 WITHOUT_TONE 无音标 WITH_TONE_MARK 直接用音标
format.setToneType(HanyuPinyinToneType.WITHOUT_TONE);
//WITH_U_AND_COLON 用u表示ü WITH_V 用v表示ü WITH_U_UNICODE 用ü表示ü
format.setVCharType(HanyuPinyinVCharType.WITH_V);
StringBuilder pYStr = new StringBuilder();
char[] input = inputStr.trim().toCharArray();
try {
for (int i = 0; i < input.length; i++) {
if (Character.toString(input[i]).matches("[\\u4E00-\\u9FA5]+")) {
pYStr.append(PinyinHelper.toHanyuPinyinStringArray(input[i], format)[0]);
} else if (!(input[i] == ' ')) {
//过滤空格
pYStr.append(input[i]);
}
}
} catch (BadHanyuPinyinOutputFormatCombination e) {
e.printStackTrace();
}
return pYStr.toString();
}
}
pom.xml增加
pom.xml涉及到的jar包有
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.6</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>dingtalk</artifactId>
<version>1.1.86</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>alibaba-dingtalk-service-sdk</artifactId>
<version>2.0.0</version>
</dependency>
<dependency>
<groupId>com.belerweb</groupId>
<artifactId>pinyin4j</artifactId>
<version>2.5.0</version>
</dependency>
那么到这里整个关于钉钉一键授权登录的页面代码以及后台逻辑代码也就写完了,下面看一下一键登录的效果
一键登录效果展示
登录页面点击【钉钉一键授权登录】
跳转到钉钉授权页面
点击【立即登录】
跳转到系统首页,这里文中的图片打码主要是由于内容涉及不方便展示,希望可以谅解,整体的流程和代码是可以参考的,如果有问题欢迎大家评论区留言讨论。