1、首先在钉钉开放平台,配置移动接入应用的登录,域名,获取appid 和 appSecret,(后台服务需要)
2、我在前端的登录页面放置了钉钉的扫码登录的二维码,
login.vue代码
2、utils文件里面跑出的方法:getQueryVariable
//判断当前url是否携带参数
export function getQueryVariable(variable)
{
var query = window.location.search.substring(1);
var vars = query.split("&");
for (var i=0;i
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×tamp=%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))
}