第三方网站实现钉钉(DingTalk)扫码登陆(Vue+SpringBoot)

一,本地库添加钉钉UserId字段

	字段类型VarChar,管理者的userid为String,其余为Long

二,钉钉开放平台配置回调域名。

在这里插入图片描述

三,前端构造扫码登录页面。

(1)配置需要参数

	appid: '钉钉APiID',
     redirectUrl: '回调地址,当前登陆页面地址',
     apiUrl: '请求后端地址',
     //二维码设置参数
     dingCodeConfig: {
       id: 'login_container',
       style: 'border:none;background-color:#FFFFFF;',
       width: '365',
       height: '400'
     }

(3)构建请求钉钉服务器URL

getRedirectUrl() {
      return encodeURIComponent(this.redirectUrl)
    },
    getAuthUrl() {
      return `https://oapi.dingtalk.com/connect/oauth2/sns_authorize?appid=${this.appid}&response_type=code&scope=snsapi_login&state=STATE&redirect_uri=${this.getRedirectUrl}`
    },
    getGoto() {
      return encodeURIComponent(this.getAuthUrl)
    },
    getDingCodeConfig() {
      return { ...this.dingCodeConfig, goto: this.getGoto }
    }

(4)初始化函数,请求函数

initDingJs() {
      !function(window, document) {

        function d(a) {
          var e, c = document.createElement('iframe'),
            d = 'https://login.dingtalk.com/login/qrcode.htm?goto=' + a.goto
          d += a.style ? '&style=' + encodeURIComponent(a.style) : '',
            d += a.href ? '&href=' + a.href : '',
            c.src = d,
            c.frameBorder = '0',
            c.allowTransparency = 'true',
            c.scrolling = 'no',
            c.width = a.width ? a.width + 'px' : '365px',
            c.height = a.height ? a.height + 'px' : '400px',
            e = document.getElementById(a.id),
            e.innerHTML = '',
            e.appendChild(c)
        }

        window.DDLogin = d

      }(window, document)
    },
    addDingListener() {

      let self = this

      let handleLoginTmpCode = function(loginTmpCode) {
        window.location.href = self.getAuthUrl + `&loginTmpCode=${loginTmpCode}`
      }

      let handleMessage = function(event) {
        if (event.origin == 'https://login.dingtalk.com') {
          handleLoginTmpCode(event.data)
        }
      }

      if (typeof window.addEventListener != 'undefined') {
        window.addEventListener('message', handleMessage, false)
      } else if (typeof window.attachEvent != 'undefined') {
        window.attachEvent('onmessage', handleMessage)
      }

    },
    initDingLogin() {
      window.DDLogin(this.getDingCodeConfig)
    }

四,钉钉回调至登陆页面,前端获取临时授权码。

let getQueryString = function(name) {
    var reg = new RegExp('(^|&)' + name + '=([^&]*)(&|$)', 'i')
    var r = window.location.search.substr(1).match(reg)
    if (r != null) {
      return unescape(r[2])
    }
    return null
  }

五,前端携带临时授权码向后端请求。

let code = getQueryString('code')
   if (code !== null) {
     axios
       .get(`${this.apiUrl}?code=${code}`).then(response => {

       setToken(response.data.token)
       this.$router.push({ path: this.redirect || '/' }).catch(() => {
       })
       commit('SET_TOKEN', response.data.token)
     })
       .catch(error => {
         console.log(error)
       })
   }

六, 后端接受请求

@GetMapping("/dingTalkLogin")
    public AjaxResult dingTalkLogin(@RequestParam("code") String code) {
        AjaxResult ajax=null;
        // 生成令牌
        String token = loginService.dingTalkLogin(code);
        if (token != null) {
            ajax = AjaxResult.success();
            ajax.put(Constants.TOKEN, token);
        }else{
            ajax = AjaxResult.error("登陆失败,请联系管理员或账号登陆");
        }
        return ajax;
    }

七,后端使用临时授权码请求钉钉服务器,获取钉钉UserId

	/**
     * 钉钉登录
     * @return 结果
     */
    public String dingTalkLogin(String code) {
        OapiSnsGetuserinfoBycodeResponse bycodeResponse = null;
        // 获取access_token,注意正式代码要有异常流处理
        String access_token = Access_tokenUtil.getDingTalkAccess_token();
        try {
            // 通过临时授权码获取授权用户的个人信息
            DefaultDingTalkClient client2 = new DefaultDingTalkClient("https://oapi.dingtalk.com/sns/getuserinfo_bycode");
            OapiSnsGetuserinfoBycodeRequest reqBycodeRequest = new OapiSnsGetuserinfoBycodeRequest();
            // 通过扫描二维码,跳转指定的redirect_uri后,向url中追加的code临时授权码
            reqBycodeRequest.setTmpAuthCode(code);
            bycodeResponse = client2.execute(reqBycodeRequest, DingTalkConstant.DINGTALK_APPKEY, DingTalkConstant.DINGTALK_APPSECRST);
            // 根据unionid获取userid
            String unionid = bycodeResponse.getUserInfo().getUnionid();
            DingTalkClient clientDingTalkClient = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/user/getbyunionid");
            OapiUserGetbyunionidRequest reqGetbyunionidRequest = new OapiUserGetbyunionidRequest();
            reqGetbyunionidRequest.setUnionid(unionid);
            OapiUserGetbyunionidResponse oapiUserGetbyunionidResponse = clientDingTalkClient.execute(reqGetbyunionidRequest, access_token);
            String userid = oapiUserGetbyunionidResponse.getResult().getUserid();

        } catch (ApiException e) {
            e.printStackTrace();
            return null;
        }
    }

八,后端根据钉钉UserId获取库中用户信息

	/**
     * 钉钉登录
     * @return 结果
     */
    public String dingTalkLogin(String code) {
            // 根据userId获取用户信息
            String userid = oapiUserGetbyunionidResponse.getResult().getUserid();
            //更加userID查询用户
            SysUser user=sysUserMapper.selectUserByDingTalkUserId(userid);
            AsyncManager.me().execute(AsyncFactory.recordLogininfor(user.getUserName(), Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
            //生成loginUser
            LoginUser loginUser =(LoginUser) userDetailsService.createLoginUser(user);
            recordLoginInfo(loginUser.getUserId());
            // 生成token
            return tokenService.createToken(loginUser);
        } catch (ApiException e) {
            e.printStackTrace();
            return null;
        }
    }

九,后端跳过Spring Security验证,直接构建LoginUser

跳过UsernamePasswordAuthenticationToken,用户名密码验证

	/**
   * 钉钉登录
   * @return 结果
   */
  public String dingTalkLogin(String code) {
          // 根据userId获取用户信息
          String userid = oapiUserGetbyunionidResponse.getResult().getUserid();
          //生成loginUser
          LoginUser loginUser =(LoginUser) userDetailsService.createLoginUser(user);
          recordLoginInfo(loginUser.getUserId());
          // 生成token
          return tokenService.createToken(loginUser);
      } catch (ApiException e) {
          e.printStackTrace();
          return null;
      }
  }

附1,前端完整源码

<template>
  <div id="app">
    <div id="login_container"></div>
  </div>
</template>

<script>
import axios from 'axios'
import { setToken } from '@/utils/auth'

export default {
  components: {},
  data() {
    return {
      redirect: undefined,
      appid: 'ding8lrom1le5zopavwt',
      redirectUrl: 'http://localhost:9527/login?redirect=%2Findex',
      apiUrl: 'http://localhost:8080/dingTalk/user/dingTalkLogin',
      dingCodeConfig: {
        id: 'login_container',
        style: 'border:none;background-color:#FFFFFF;',
        width: '365',
        height: '400'
      }
    }
  },
  watch: {
    $route: {
      handler: function(route) {
        this.redirect = route.query && route.query.redirect
      },
      immediate: true
    }
  },
  computed: {
    getRedirectUrl() {
      return encodeURIComponent(this.redirectUrl)
    },
    getAuthUrl() {
      return `https://oapi.dingtalk.com/connect/oauth2/sns_authorize?appid=${this.appid}&response_type=code&scope=snsapi_login&state=STATE&redirect_uri=${this.getRedirectUrl}`
    },
    getGoto() {
      return encodeURIComponent(this.getAuthUrl)
    },
    getDingCodeConfig() {
      return { ...this.dingCodeConfig, goto: this.getGoto }
    }
  },
  created() {
    this.initDingJs()
  },
  mounted() {
    this.addDingListener()
    this.initDingLogin()
    this.getUser()
  },
  methods: {
    initDingJs() {
      !function(window, document) {

        function d(a) {
          var e, c = document.createElement('iframe'),
            d = 'https://login.dingtalk.com/login/qrcode.htm?goto=' + a.goto
          d += a.style ? '&style=' + encodeURIComponent(a.style) : '',
            d += a.href ? '&href=' + a.href : '',
            c.src = d,
            c.frameBorder = '0',
            c.allowTransparency = 'true',
            c.scrolling = 'no',
            c.width = a.width ? a.width + 'px' : '365px',
            c.height = a.height ? a.height + 'px' : '400px',
            e = document.getElementById(a.id),
            e.innerHTML = '',
            e.appendChild(c)
        }

        window.DDLogin = d

      }(window, document)
    },
    addDingListener() {

      let self = this

      let handleLoginTmpCode = function(loginTmpCode) {
        window.location.href = self.getAuthUrl + `&loginTmpCode=${loginTmpCode}`
      }

      let handleMessage = function(event) {
        if (event.origin == 'https://login.dingtalk.com') {
          handleLoginTmpCode(event.data)
        }
      }

      if (typeof window.addEventListener != 'undefined') {
        window.addEventListener('message', handleMessage, false)
      } else if (typeof window.attachEvent != 'undefined') {
        window.attachEvent('onmessage', handleMessage)
      }

    },
    initDingLogin() {
      window.DDLogin(this.getDingCodeConfig)
    },
    getUser() {
      let getQueryString = function(name) {
        var reg = new RegExp('(^|&)' + name + '=([^&]*)(&|$)', 'i')
        var r = window.location.search.substr(1).match(reg)
        if (r != null) {
          return unescape(r[2])
        }
        return null
      }
      let code = getQueryString('code')
      if (code !== null) {
        axios
          .get(`${this.apiUrl}?code=${code}`).then(response => {

          setToken(response.data.token)
          this.$router.push({ path: this.redirect || '/' }).catch(() => {
          })
          commit('SET_TOKEN', response.data.token)
        })
          .catch(error => {
            console.log(error)
          })

      }

    }

  }
}
</script>


附2,后端完整源码s

(1)Controller

	 /**
     * 钉钉扫码登录方法
     *
     * @param code 扫码返回code
     * @return 结果
     */
    @GetMapping("/dingTalkLogin")
    public AjaxResult dingTalkLogin(@RequestParam("code") String code) {
        AjaxResult ajax=null;
        // 生成令牌
        String token = loginService.dingTalkLogin(code);
        if (token != null) {
            ajax = AjaxResult.success();
            ajax.put(Constants.TOKEN, token);
        }else{
            ajax = AjaxResult.error("登陆失败,请联系管理员或账号登陆");
        }
        return ajax;
    }

(2)Service

	/**
     * 钉钉登录
     * @return 结果
     */
    public String dingTalkLogin(String code) {
        OapiSnsGetuserinfoBycodeResponse bycodeResponse = null;
        // 获取access_token,注意正式代码要有异常流处理
        String access_token = Access_tokenUtil.getDingTalkAccess_token();
        try {
            // 通过临时授权码获取授权用户的个人信息
            DefaultDingTalkClient client2 = new DefaultDingTalkClient("https://oapi.dingtalk.com/sns/getuserinfo_bycode");
            OapiSnsGetuserinfoBycodeRequest reqBycodeRequest = new OapiSnsGetuserinfoBycodeRequest();
            // 通过扫描二维码,跳转指定的redirect_uri后,向url中追加的code临时授权码
            reqBycodeRequest.setTmpAuthCode(code);
            bycodeResponse = client2.execute(reqBycodeRequest, DingTalkConstant.DINGTALK_APPKEY, DingTalkConstant.DINGTALK_APPSECRST);
            // 根据unionid获取userid
            String unionid = bycodeResponse.getUserInfo().getUnionid();
            DingTalkClient clientDingTalkClient = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/user/getbyunionid");
            OapiUserGetbyunionidRequest reqGetbyunionidRequest = new OapiUserGetbyunionidRequest();
            reqGetbyunionidRequest.setUnionid(unionid);
            OapiUserGetbyunionidResponse oapiUserGetbyunionidResponse = clientDingTalkClient.execute(reqGetbyunionidRequest, access_token);

            // 根据userId获取用户信息
            String userid = oapiUserGetbyunionidResponse.getResult().getUserid();
            //更加userID查询用户
            SysUser user=sysUserMapper.selectUserByDingTalkUserId(userid);
            AsyncManager.me().execute(AsyncFactory.recordLogininfor(user.getUserName(), Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
            //生成loginUser
            LoginUser loginUser =(LoginUser) userDetailsService.createLoginUser(user);
            recordLoginInfo(loginUser.getUserId());
            // 生成token
            return tokenService.createToken(loginUser);
        } catch (ApiException e) {
            e.printStackTrace();
            return null;
        }
    }

在这里插入图片描述

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐