釘釘掃碼登錄接入WEB第三方系統---前端 vue 後臺golang

1、首先在釘釘開放平臺,配置移動接入應用的登錄,域名,獲取appid 和 appSecret,(後臺服務需要)

 

 

2、我在前端的登錄頁面放置了釘釘的掃碼登錄的二維碼,

login.vue代碼

<template>
  <div class="login-container">
      <div id="login_container" class="dingding_scan" ></div> 
  </div>
</template>



<script>



import { ddlogin } from "@/api/dashboard";
import { getQueryVariable } from "@/utils";



export default {
  name: "Login",
  
  data() {
    return {}
      
  },
  watch: {
    $route: {
      handler: function(route) {
        this.redirect = route.query && route.query.redirect;
      },
      immediate: true
    }
  },
  created() {
  },
  mounted() {
    this.ddLogin();

  },
  destroyed() {
  },
  methods: {
    ddLogin() {
      let code = getQueryVariable("code"); //查看當前Url中有沒有state的這個參數,如果有這個參數證明掃碼登錄成功重定向地址已經調轉完成
      if (code) {
        let state = getQueryVariable("state");
        let temp = {
          code: code,
          state: state
        };
        //釘釘跳轉的url後會攜帶code,state,要傳給後臺,即調用後臺api 
        //因爲系統需要,我在狀態裏面調用並報錯了,你們可以自行寫自己的接口調用就行
        this.$store.dispatch("user/ddlogin", temp).then(() => {

          //調用後,會獲取當前系統的權限信息和登錄用戶的信息
          // 因爲我是跳轉到當前頁面,所以,當前頁面直接回帶codehe state,
          //我調用api後,就不需要這些參數了,就刪除了了url後攜帶的參數,直接跳轉首頁
          var url = window.location.href; //獲取當前頁面的url
          if (url.indexOf("?") != -1) {
            //判斷是否存在參數
            url = url.replace(/(\?|#)[^'"]*/, ""); //去除參數
            window.history.pushState({}, 0, url);
          }
          //然後跳轉首頁
          this.$router.replace({ path: "/" });
        });
      } else {
        //默認二維碼的顯示
        let appid='dingoasvam8tvfkuezexjo'
        let strurl= window.location.protocol+"//"+window.location.host+'/'
        let url = encodeURIComponent(strurl);
        console.log('window.location.host  跳轉',url)
        let goto = encodeURIComponent(
          "https://oapi.dingtalk.com/connect/qrconnect?appid="+appid +"&response_type=code&scope=snsapi_login&state=STATE&redirect_uri=" +
            url
        );
        var obj = DDLogin({
          id: "login_container",
          goto: goto,
          style: "border:none;background-color:#FFFFFF;",
          width: "365",
          height: "400"
        });

        var hanndleMessage = function(event) {
          var origin = event.origin;
          // console.log("origin", event.origin);
            //判斷是否來自ddLogin掃碼事件。
          if (origin == "https://login.dingtalk.com") {
            var loginTmpCode = event.data; //拿到loginTmpCode後就可以在這裏構造跳轉鏈接進行跳轉了
            // console.log("loginTmpCode", loginTmpCode);
            if (loginTmpCode) {
              window.location.href =
                "https://oapi.dingtalk.com/connect/oauth2/sns_authorize?appid="+appid+"&response_type=code&scope=snsapi_login&state=STATE&redirect_uri=" +
                url +
                "&loginTmpCode=" +
                loginTmpCode;
              //拿到用戶唯一標示然後在進行當前頁面的調轉,注意這裏跳轉以後還是會在當前頁面只不過Url上帶了參數了這時候咱們去看上面的條件
            }
          }
        };
        if (typeof window.addEventListener != "undefined") {
          window.addEventListener("message", hanndleMessage, false);
        } else if (typeof window.attachEvent != "undefined") {
          window.attachEvent("onmessage", hanndleMessage);
        }
      }
    },
  }
};
</script>

<style lang="scss" scoped>
$bg: #2d3a4b;
$dark_gray: #889aa4;
$light_gray: #eee;

.login-container {
  min-height: 100%;
  width: 100%;
  background-color: $bg;
  overflow: hidden;
.dingding_scan{
  margin-top: 10%;
  float: right;
  width: 500px;
  height: 500px;
}
}
</style>

 

2、utils文件裏面跑出的方法:getQueryVariable

 

//判斷當前url是否攜帶參數
export function getQueryVariable(variable)
{
       var query = window.location.search.substring(1);
       var vars = query.split("&");
       for (var i=0;i<vars.length;i++) {
               var pair = vars[i].split("=");
               if(pair[0] == variable){return pair[1];}
       }
       return(false);
}

go 配置文件dingding.yaml

AppKey: dingoasxxxxxxxxxxxuezexjo
AppSecret: kdLP8FbJY2xxxxxxxxxxxxxxx5gRUyU_7gHL432hpuL6Qul2jF
DDServerAddress: https://oapi.dingtalk.com/sns/getuserinfo_bycode //釘釘後臺用code請求當前掃碼人的信息鏈接

 

讀取配置文件

 

package dd

import (
	"io/ioutil"

	log "github.com/Sirupsen/logrus"
	"gopkg.in/yaml.v2"
)

var DdConf DDConf

type DDConf struct {
	AppKey         string `yaml:"AppKey"`
	AppSecret string `yaml:"AppSecret"`
	DDServerAddress           string `yaml:"DDServerAddress"`
}

func init() {
	yamlFile, err := ioutil.ReadFile("./conf/dingding.yaml")
	if err != nil {
		log.Fatal("init conf/dingding.yaml發生錯誤:", err)
	}
	err = yaml.Unmarshal(yamlFile, &DdConf)
	if err != nil {
		log.Fatal("init conf/dingding.yaml發生錯誤:", err)
	}
	return
}

go 後臺服務api

 

package user

import (
	"bytes"
	"crypto/hmac"
	"crypto/sha256"
	"crypto/tls"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	dd "server/common/dd"
	jwt "server/common/jwt"
	models "server/models"
	table_user "server/models/table/user"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/gin-gonic/gin"
)

type DdLogin struct {
}

type dDLoginReq struct {
	TmpAuthCode string `json:"tmp_auth_code"` //臨時授權碼
}
type RetDDUserInfo struct {
	Nick                 string `json:"nick"`                     //釘釘暱稱
	Unionid              string `json:"unionid"`                  //Unionid
	DingId               string `json:"dingId"`                   //DingId
	Openid               string `json:","openid"`                 //Openid
	MainOrgAuthHighLevel bool   `json:"main_org_auth_high_level"` //MainOrgAuthHighLevel
}

type DDResp struct {
	Errcode  int64         `json:"errcode"`   //錯誤碼
	Errmsg   string        `json:"errmsg"`    //錯誤信息
	UserInfo RetDDUserInfo `json:"user_info"` //用戶信息
}

type RetddModule struct {
	Id   int64  `json:"id"`
	Name string `json:"name"` //模塊名字
	Op   int64  `json:"op"`   //模塊權限定義值
}
type dDLoginResp struct {
	StatusCode int    `json:"status_code"` //狀態碼
	StatusMsg  string `json:"status_msg"`  //狀態信息
	UserId     int64  `json:"user_id"`     //用戶id
	Name       string `json:"name"`        //用戶名稱
	RoleId     int64  `json:"role_id"`     //用戶所屬角色id
	RoleName   string `json:"role_name"`   //用戶所屬角色名稱

	Phone     string        `json:"phone"`
	Email     string        `json:"email"`
	Avatar    string        `json:"avatar"`
	JobNumber string        `json:"job_number"`
	Token     string        `json:"token"`   //登錄token
	Modules   []RetddModule `json:"modules"` //模塊定義
}

// @Summary  釘釘登錄接口
// @Description 無
// @Tags user
// @Accept  json
// @Produce json
// @Param 請求體 body user.dDLoginReq true "請求體"
// @Success 200 {object} user.dDLoginResp "返回體"
// @Router /algorithm_platform_api/v1/user/dd_login [post]
func (this *DdLogin) DdLogin(c *gin.Context) {
	var resp dDLoginResp
	str_code, _ := c.GetQuery("code")
	timestamp := time.Now().UnixNano() / 1e6
	strTimeStamp := fmt.Sprintf("%d", timestamp)

	appKey := dd.DdConf.AppKey // 讀取配置文件 appid
	appSecret := dd.DdConf.AppSecret
	signature := ComputeHmacSha256(strTimeStamp, appSecret) //簽名
	signature = url.QueryEscape(signature)
	//post請求提交json數據
	var ddreq dDLoginReq
	ddreq.TmpAuthCode = str_code
	ba, _ := json.Marshal(ddreq)
	targetUrl := fmt.Sprintf("%s?accessKey=%s&timestamp=%d&signature=%s", dd.DdConf.DDServerAddress, appKey, timestamp, signature)

	tr := &http.Transport{
		////把從服務器傳過來的非葉子證書,添加到中間證書的池中,使用設置的根證書和中間證書對葉子證書進行驗證。
		// TLSClientConfig: &tls.Config{RootCAs: pool},
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, //InsecureSkipVerify用來控制客戶端是否證書和服務器主機名。如果設置爲true,//
		//則不會校驗證書以及證書中的主機名和服務器主機名是否一致。
	}
	client := &http.Client{Transport: tr}
	resp_dingding, err := client.Post(targetUrl, "application/json", bytes.NewBuffer([]byte(ba)))
	if err != nil {
		log.Error(err)
		resp.StatusCode = 1001
		resp.StatusMsg = "請求參數錯誤"
		c.JSON(http.StatusOK, resp)
		return
	}

	defer resp_dingding.Body.Close()
	body, err := ioutil.ReadAll(resp_dingding.Body)
	if err != nil {
		log.Error(err)
		resp.StatusCode = 1001
		resp.StatusMsg = "請求參數錯誤"
		c.JSON(http.StatusOK, resp)
		return
	}

	log.Debug("到這裏    釘釘登錄=user信息==========================:", string(body))
	log.Debug("下面是我平臺系統的邏輯  ==========================:")



















	var ddResp DDResp
	//解析json結構體
	json.Unmarshal([]byte(body), &ddResp)


	//先查找釘釘用戶表,用Unionid查找,找到返回信息,找不到插入信息
	diongdingUser := new(table_user.DdUser)
	log.Debug("ddResp=先查找釘釘用戶表,用Unionid查找,找到返回信息,找不到插入信息==================================:")
	has, err := models.UserDb.Where("`unionid` = ?", ddResp.UserInfo.Unionid).Get(diongdingUser)
	if err != nil {
		log.Error("查詢DdUser表發生錯誤:", err.Error())
		resp.StatusCode = 1003
		resp.StatusMsg = "系統內部錯誤"
		c.JSON(http.StatusOK, resp)
		return
	}
	//之前存在系統中按協議返回,不存在則新加user表和dd_user
	if has {
		user := new(table_user.User)
		//has, err := models.UserDb.Id(diongdingUser.UserId).Get(user)
		has, err := models.UserDb.Where("`id` = ?", diongdingUser.UserId).Get(user)
		if err != nil {
			log.Error("查詢user表發生錯誤:", err.Error())
			resp.StatusCode = 1004
			resp.StatusMsg = "系統內部錯誤"
			c.JSON(http.StatusOK, resp)
			return
		}
		if !has {
			log.Error("user表中沒發現id:", diongdingUser.UserId)
			resp.StatusCode = 1005
			resp.StatusMsg = "系統內部錯誤"
			c.JSON(http.StatusOK, resp)
			return
		}
		//是否是禁止登錄用戶
		if user.State == PROHIBIT_LOGIN {
			log.Warn("系統禁止登錄用戶:", diongdingUser.Name)
			resp.StatusCode = 1006
			resp.StatusMsg = "系統禁止登錄用戶"
			c.JSON(http.StatusOK, resp)
			return
		}
		role := new(table_user.Role)
		//has, err = models.UserDb.Id(user.RoleId).Get(role)
		has, err = models.UserDb.Where("`id` = ?", user.RoleId).Get(role)
		if err != nil {
			log.Error(err)
			resp.StatusCode = 1007
			resp.StatusMsg = "系統內部錯誤"
			c.JSON(http.StatusOK, resp)
			return
		}
		if !has {
			log.Warn(fmt.Sprintf("role表中沒有id:%d", user.RoleId))
			resp.StatusCode = 1008
			resp.StatusMsg = "系統內部錯誤"
			c.JSON(http.StatusOK, resp)
			return
		}

		permissions := make([]table_user.Permission, 0)
		err = models.UserDb.Where("`role_id` = ?", user.RoleId).Find(&permissions)
		if err != nil {
			log.Error(err)
			resp.StatusCode = 1009
			resp.StatusMsg = "系統內部錯誤"
			c.JSON(http.StatusOK, resp)
			return
		}
		modules := make([]RetddModule, 0)
		for i := 0; i < len(permissions); i++ {
			module_id := permissions[i].ModuleId
			module := new(table_user.Module)
			//has, err = models.UserDb.Id(module_id).Get(module)
			has, err = models.UserDb.Where("`id` = ?", module_id).Get(module)
			if err != nil {
				log.Error(err)
				resp.StatusCode = 1010
				resp.StatusMsg = "系統內部錯誤"
				c.JSON(http.StatusOK, resp)
				return
			}
			if !has {
				log.Warn(fmt.Sprintf("moudle表中沒有發現id:%d", module_id))
				resp.StatusCode = 1011
				resp.StatusMsg = "系統內部錯誤"
				c.JSON(http.StatusOK, resp)
				return
			}
			var retModule RetddModule
			retModule.Id = module.Id
			retModule.Name = module.Name
			retModule.Op = permissions[i].CrudOperation
			modules = append(modules, retModule)
		}

		token := jwt.GenToken(user.Id)

		resp.StatusCode = 1000
		resp.StatusMsg = "ok"
		resp.UserId = user.Id
		resp.Name = ddResp.UserInfo.Nick

		//從釘釘後臺獲取的信息
		// resp.Phone = userInfo.Tel
		// resp.Email = userInfo.Email
		// resp.Avatar = userInfo.Avatar
		// resp.JobNumber = userInfo.JobNumber

		resp.RoleId = role.Id
		resp.RoleName = role.Name
		resp.Token = token
		resp.Modules = modules
		c.JSON(http.StatusOK, resp)
		return

	} else {
		//這裏看是否要按名稱綁定,現在不添加綁定邏輯,只有添加新的
		//先拿到默認角色
		role := new(table_user.Role)
		has, err = models.UserDb.Where("`level` = ?", DEFAULT_USER_ROLE_LEVEL).Get(role)
		if err != nil {
			log.Error(err)
			resp.StatusCode = 1012
			resp.StatusMsg = "系統內部錯誤"
			c.JSON(http.StatusOK, resp)
			return
		}
		if !has {
			log.Warn(fmt.Sprintf("role表中沒有level:%d", DEFAULT_USER_ROLE_LEVEL))
			resp.StatusCode = 1013
			resp.StatusMsg = "系統內部錯誤"
			c.JSON(http.StatusOK, resp)
			return
		}

		//先生成user表記錄
		//這塊最好的做法是能過事務來做,但golang xorm沒有發現沒有Flush方法,沒法獲取到先提交的用戶id
		//不存在系統中增加到系統中,生成默認角色
		var user table_user.User
		user.Name = ddResp.UserInfo.Nick
		// user.Unionid = ddResp.UserInfo.Unionid
		// user.Email = userInfo.Email
		// user.Phone = userInfo.Tel
		user.RoleId = role.Id
		//開始事務時這裏不能用models.DB.Insert得用session.Insert
		_, err = models.UserDb.Insert(&user)
		if err != nil {
			log.Error(err)
			resp.StatusCode = 1014
			resp.StatusMsg = "系統內部錯誤"
			c.JSON(http.StatusOK, resp)
			return
		}

		//再生成dd_user表記錄
		var diongdingUser table_user.DdUser
		//這個id能自動映射

		diongdingUser.Unionid = ddResp.UserInfo.Unionid
		diongdingUser.UserId = user.Id
		diongdingUser.Name = ddResp.UserInfo.Nick
		diongdingUser.Openid = ddResp.UserInfo.Openid
		// diongdingUser.Email = userInfo.Email
		// diongdingUser.Phone = userInfo.Tel
		_, err = models.UserDb.Insert(&diongdingUser)
		if err != nil {
			log.Error("增加dd用戶表記錄發生錯誤:", err)
			//要刪除之前添加的用戶
			_, err = models.UserDb.Where("id = ?", user.Id).Delete(new(table_user.User))
			if err != nil {
				log.Error("增加dd用戶表記錄失敗,在刪除對應的user表中記錄時發生錯誤:", err)
			}
			resp.StatusCode = 1015
			resp.StatusMsg = "系統內部錯誤"
			c.JSON(http.StatusOK, resp)
			return
		}

		//user role都有了,下面邏輯和上面一樣
		permissions := make([]table_user.Permission, 0)
		err = models.UserDb.Where("`role_id` = ?", user.RoleId).Find(&permissions)
		if err != nil {
			log.Error(err)
			resp.StatusCode = 1009
			resp.StatusMsg = "系統內部錯誤"
			c.JSON(http.StatusOK, resp)
			return
		}
		modules := make([]RetddModule, 0)
		for i := 0; i < len(permissions); i++ {
			module_id := permissions[i].ModuleId
			module := new(table_user.Module)
			has, err = models.UserDb.Id(module_id).Get(module)
			if err != nil {
				log.Error(err)
				resp.StatusCode = 1010
				resp.StatusMsg = "系統內部錯誤"
				c.JSON(http.StatusOK, resp)
				return
			}
			if !has {
				log.Warn(fmt.Sprintf("moudle表中沒有發現id:%d", module_id))
				resp.StatusCode = 1011
				resp.StatusMsg = "系統內部錯誤"
				c.JSON(http.StatusOK, resp)
				return
			}
			var retModule RetddModule
			retModule.Id = module.Id
			retModule.Name = module.Name
			retModule.Op = permissions[i].CrudOperation
			modules = append(modules, retModule)
		}

		token := jwt.GenToken(user.Id)

		resp.StatusCode = 1000
		resp.StatusMsg = "ok"
		resp.UserId = user.Id
		resp.Name = user.Name
		resp.RoleId = role.Id
		resp.RoleName = role.Name

		//從釘釘後臺獲取的信息
		// resp.Phone = userInfo.Tel
		// resp.Email = userInfo.Email
		// resp.Avatar = userInfo.Avatar
		// resp.JobNumber = userInfo.JobNumber

		resp.Token = token
		resp.Modules = modules
		c.JSON(http.StatusOK, resp)
		return
	}

}


//釘釘簽名
func ComputeHmacSha256(message string, secret string) string {
	key := []byte(secret)
	h := hmac.New(sha256.New, key)
	h.Write([]byte(message))
	sha := h.Sum(nil)
	return base64.StdEncoding.EncodeToString([]byte(sha))
}

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章