钉钉一键登录第三方网站

news2024/12/23 17:36:16

钉钉一键登录第三方网站

  • 钉钉开发者后台
    • 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>

那么到这里整个关于钉钉一键授权登录的页面代码以及后台逻辑代码也就写完了,下面看一下一键登录的效果

一键登录效果展示

登录页面点击【钉钉一键授权登录】

跳转到钉钉授权页面

点击【立即登录】

跳转到系统首页,这里文中的图片打码主要是由于内容涉及不方便展示,希望可以谅解,整体的流程和代码是可以参考的,如果有问题欢迎大家评论区留言讨论。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/197394.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

物联网终端设备的工作原理和功能讲解

物联网终端设备是实现数据采集和数据传输的设备&#xff0c;它连接了传感网络层和传输网络层&#xff0c;起到了数据采集、数据处理、数据加密和传输的功能。 物联网终端设备由传感器、外部传感接口、CPU和外部通讯组成&#xff0c;工作原理是通过外部传感接口与传感设备连接&a…

和平精英军需精打细算天花板,330拿下一整套军需

和平精英军需精打细算天花板&#xff0c;330拿下一整套军需&#xff01; #和平精英 #这游戏不花钱 #游戏凡星计划 军需精打细算天花板&#xff0c;一个月时间花 110 块获得 436 个军需币。拿个新军需的副套问题不大。要知道和平小店的暖弄大礼包&#xff0c; 100 左右&#…

一次在 classpath 使用通配符导致的偶发问题排查与建议

说起 Classpath&#xff0c;使用 Java 的同学应该都不会陌生。不过&#xff0c;目前的项目基本都会使用 Maven 等构建工具管理&#xff0c;开发过程中也会使用高度智能化的 IDE&#xff0c;在日常使用中直接涉及 Classpath 操作可能不多。前段时间遇到一个跟 Classpath 相关的偶…

【My Electronic Notes系列——触发器】

目录 序言&#xff1a; &#x1f3c6;&#x1f3c6;人生在世&#xff0c;成功并非易事&#xff0c;他需要破茧而出的决心&#xff0c;他需要永不放弃的信念&#xff0c;他需要水滴石穿的坚持&#xff0c;他需要自强不息的勇气&#xff0c;他需要无畏无惧的凛然。要想成功&…

【栈】单调栈详情介绍及其运用

单调栈单调栈的概述&#xff08;Overview&#xff09;何时使用单调栈模拟单调递增栈单调栈的运用&#xff08;算法练习题&#xff09;模板【练习一、单调栈】739. 每日温度【练习二、单调栈哈希表】496. 下一个更大元素 I【练习三、单调栈循环数组】503. 下一个更大元素 II【练…

Word处理控件Aspose.Words功能演示:使用 C++ 处理 Word 文档中的目录

Aspose API支持流行文件格式处理&#xff0c;并允许将各类文档导出或转换为固定布局文件格式和最常用的图像/多媒体格式。 Aspose.words是一种高级Word文档处理API&#xff0c;用于执行各种文档管理和操作任务。API支持生成&#xff0c;修改&#xff0c;转换&#xff0c;呈现和…

4EVERLAND IPFS CID部署,一键部署Uniswap

近日&#xff0c;4EVERLAND推出IPFS CID部署&#xff0c;开发者可以复制IPFS CID&#xff0c;一键部署到4EVERLAND。 一键部署&#xff0c;无需通过Github Repo&#xff0c;只需要知道CID即可。一键跨平台部署项目到 Arweave 或 ICP。了解IPFS CID&#xff0c;通过4EVERLAND绑…

漫谈广告机制设计 | 开篇语

很久没有写文章了&#xff0c;oCPC实践录的专栏还没有写完&#xff0c;我就换工作了&#xff0c;去了M公司&#xff0c;做的内容与oCPC不怎么相关&#xff0c;对于其中的问题思考也没有那么多了&#xff0c;好在专栏的核心思想已经基本阐明了。在M公司也已经快两年了&#xff0…

青龙+WxPusher实现资产推送

1.首先注册WXpusher&#xff1a; https://wxpusher.zjiecode.com/admin/login 扫码注册创建应用 确定完就会出现一个token&#xff0c;一定先复制保存起来&#xff0c;因为只显示一次&#xff0c;没存后期就只能重置了。 关闭后&#xff0c;这个页面有二维码和链接&#xff0…

CSS定位详解

文章目录定位为什么要使用定位定位的组成定位模式静态定位&#xff1a;按照标准流特性摆放&#xff0c;没有边偏移相对定位&#xff1a;元素在移动位置的时候&#xff0c;是相对于它原来的位置来说的绝对定位&#xff1a;在移动位置的时候相对与祖先元素固定定位&#xff1a;元…

C语言:指针详解

往期文章 C语言&#xff1a;初识C语言C语言&#xff1a;分支语句和循环语句C语言&#xff1a;函数C语言&#xff1a;数组C语言&#xff1a;操作符详解 目录往期文章前言1. 指针是什么2. 指针和指针类型3. 野指针4. 指针运算4.1 指针-整数4.2 指针-指针4.3 指针的关系运算5. 二…

“小灵通”的风雨往事

最近&#xff0c;有一部叫做《狂飙》的国产电视剧火遍全网&#xff0c;相信大家都看到了。在剧中&#xff0c;出现了一个通信名词&#xff0c;不知道在座各位有没有关注到。没错&#xff0c;这个名词&#xff0c;就是“小灵通”。《狂飙》剧的主角高启强&#xff0c;原本是个卖…

Web3.0 · 基础层技术 · SCQA模型趣谈密码学

【小木箱成长营】密码学系列教程&#xff1a; Web3.0 基础层技术 密码学在移动端应用与实践 一、序言 Hello&#xff0c;我是小木箱&#xff0c;欢迎来到小木箱成长营密码学系列教程&#xff0c;今天将分享 Web3.0 基础层技术 SCQA 模型趣谈密码学。 SCQA 模型趣谈密码学主…

第一章 opencv与python介绍及环境搭建

目录1.python安装2.opencv3.pycharm安装4.conda环境搭建(my)1.python安装 网上教程很多就不写了&#xff0c;推荐使用python3.8.2及以上版本 2.opencv opencv简单介绍&#xff1a;opencv是一个开源的计算机视觉库&#xff0c;可以在windows、MacOS、Linux等操作系统上运行。 …

Day878.count(*)问题 -MySQL实战

count(*)问题 Hi&#xff0c;我是阿昌&#xff0c;今天学习记录的是关于count(*)问题。 在开发系统的时候&#xff0c;可能经常需要计算一个表的行数&#xff0c;比如一个交易系统的所有变更记录总数。 这时候可能会想&#xff0c;一条 select count(*) from t 语句不就解决…

【自动化测试】从0开始玩转docker—— 02软件配置

目的 CI / CD在目前各类互联网企业中已然成为推动软件开发行为的重要基础设施服务。同样的对于测试团队来说更是有着举足轻重的重大意义&#xff0c;无论是测试左移的具象化提现亦或是持续测试的顺利开展&#xff0c;掌握这一技能已是广大软件测试工程师的必修课。分享这一技术…

第一章:3D点云应用领域分析

&#x1f31e;欢迎来到点云的世界 &#x1f308;博客主页&#xff1a;卿云阁 &#x1f48c;欢迎关注&#x1f389;点赞&#x1f44d;收藏⭐️留言&#x1f4dd; &#x1f31f;本文由卿云阁原创&#xff01; ✉️希望可以和大家一起完成进阶之路&#xff01; &#x1f64f;作者…

力扣(LeetCode)401. 二进制手表(2023.02.03)

二进制手表顶部有 4 个 LED 代表 小时&#xff08;0-11&#xff09;&#xff0c;底部的 6 个 LED 代表 分钟&#xff08;0-59&#xff09;。每个 LED 代表一个 0 或 1&#xff0c;最低位在右侧。 例如&#xff0c;下面的二进制手表读取 “3:25” 。 &#xff08;图源&#xff…

C语言基础知识(56)

下面的C程序的输出是什么#include<stdio.h>intmain(){int a 0;while(a <printf("HI")){a;}return0;}该代码将打印 3 次HI。 printf()函数将返回它正在打印的字符数&#xff0c;并将其与a进行比较。 由于 printf() 的返回值为 2&#xff0c;HI 将被打印 2 次…

字符函数和字符串函数

&#x1f331;博客主页&#xff1a;大寄一场. &#x1f331;系列专栏&#xff1a;C语言学习笔记 &#x1f618;博客制作不易欢迎各位&#x1f44d;点赞⭐收藏➕关注 目录 前言 一、字符串函数 1. 1 strlen 2. 长度不受限制的字符串函数&#xff08;操作的是整个字符串&#…