开始开发
最后更新:2022/10/11
企业微信提供了OAuth的授权登录方式,可以让从企业微信终端打开的网页获取成员的身份信息,从而免去登录的环节。
 企业应用中的URL链接(包括自定义菜单或者消息中的链接),均可通过OAuth2.0验证接口来获取成员的UserId身份信息。
OAuth2简介
OAuth2的设计背景,在于允许用户在不告知第三方自己的帐号密码情况下,通过授权方式,让第三方服务可以获取自己的资源信息。
 详细的协议介绍,开发者可以参考RFC 6749。
下面简单说明OAuth2中最经典的Authorization Code模式,流程如下:
流程图中,包含四个角色。
- ResourceOwner为资源所有者,即为用户
- User-Agent为浏览器
- AuthorizationServer为认证服务器,可以理解为用户资源托管方,比如企业微信服务端
- Client为第三方服务
调用流程为:
 A) 用户访问第三方服务,第三方服务通过构造OAuth2链接(参数包括当前第三方服务的身份ID,以及重定向URI),将用户引导到认证服务器的授权页
 B) 用户选择是否同意授权
 C) 若用户同意授权,则认证服务器将用户重定向到第一步指定的重定向URI,同时附上一个授权码。
 D) 第三方服务收到授权码,带上授权码来源的重定向URI,向认证服务器申请凭证。
 E) 认证服务器检查授权码和重定向URI的有效性,通过后颁发AccessToken(调用凭证)
D)与E)的调用为后台调用,不通过浏览器进行
获取访问用户身份
最后更新:2022/09/23
该接口用于根据code获取成员信息,适用于自建应用与代开发应用
请求方式:GET(HTTPS)
请求地址:https://qyapi.weixin.qq.com/cgi-bin/auth/getuserinfo?access_token=ACCESS_TOKEN&code=CODE
参数说明:
| 参数 | 必须 | 说明 | 
|---|---|---|
| access_token | 是 | 调用接口凭证 | 
| code | 是 | 通过成员授权获取到的code,最大为512字节。每次成员授权带上的code将不一样,code只能使用一次,5分钟未被使用自动过期。 | 
权限说明:
 跳转的域名须完全匹配access_token对应应用的可信域名,否则会返回50001错误。
返回结果:
 a) 当用户为企业成员时(无论是否在应用可见范围之内)返回示例如下:
{
   "errcode": 0,
   "errmsg": "ok",
   "userid":"USERID",
   "user_ticket": "USER_TICKET"
}
获取访问用户敏感信息
最后更新:2022/09/19
自建应用与代开发应用可通过该接口获取成员授权的敏感字段
请求方式:POST(HTTPS)
请求地址:https://qyapi.weixin.qq.com/cgi-bin/auth/getuserdetail?access_token=ACCESS_TOKEN
请求包体:
{
   "user_ticket": "USER_TICKET"
}参数说明:
| 参数 | 必须 | 说明 | 
|---|---|---|
| access_token | 是 | 调用接口凭证 | 
| user_ticket | 是 | 成员票据 | 
权限说明:
 成员必须在应用的可见范围内。
返回结果:
{
   "errcode": 0,
   "errmsg": "ok",
   "userid":"lisi",
   "gender":"1",
   "avatar":"http://shp.qpic.cn/bizmp/xxxxxxxxxxx/0",
   "qr_code":"https://open.work.weixin.qq.com/wwopen/userQRCode?vcode=vcfc13b01dfs78e981c",
   "mobile": "13800000000",
   "email": "zhangsan@gzdev.com",
   "biz_mail":"zhangsan@qyycs2.wecom.work",
   "address": "广州市海珠区新港中路"
}参数说明:
| 参数 | 说明 | 
|---|---|
| errcode | 返回码 | 
| errmsg | 对返回码的文本描述内容 | 
| userid | 成员UserID | 
| gender | 性别。0表示未定义,1表示男性,2表示女性。仅在用户同意snsapi_privateinfo授权时返回真实值,否则返回0. | 
| avatar | 头像url。仅在用户同意snsapi_privateinfo授权时返回 | 
| qr_code | 员工个人二维码(扫描可添加为外部联系人),仅在用户同意snsapi_privateinfo授权时返回 | 
| mobile | 手机,仅在用户同意snsapi_privateinfo授权时返回,第三方应用不可获取 | 
| 邮箱,仅在用户同意snsapi_privateinfo授权时返回,第三方应用不可获取 | |
| biz_mail | 企业邮箱,仅在用户同意snsapi_privateinfo授权时返回,第三方应用不可获取 | 
| address | 仅在用户同意snsapi_privateinfo授权时返回,第三方应用不可获取 | 
注:对于自建应用与代开发应用,敏感字段需要管理员在应用详情里选择,且成员oauth2授权时确认后才返回。敏感字段包括:性别、头像、员工个人二维码、手机、邮箱、企业邮箱、地址。
上一篇

https://open.weixin.qq.com/connect/oauth2/authorize?appid=wwd70ee88af370290f&redirect_uri=http://yuying-api.xutongbao.top/index1.html&response_type=code&scope=snsapi_privateinfo&agentid=1000004&state=STATE#wechat_redirecthttps://open.weixin.qq.com/connect/oauth2/authorize?appid=wwd70ee88af370290f&redirect_uri=http://yuying-api.xutongbao.top/index1.html&response_type=code&scope=snsapi_privateinfo&agentid=1000004&state=STATE#wechat_redirect

https://developer.work.weixin.qq.com/document/path/91022
接口:
const toolsQiWeUserInfo = async (req, res) => {
  const { code } = req.body
  axios({
    url: 'https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=wwd70e*****290f&corpsecret=JrPSfOHlNzPiF******CV4VPwUuuHwAxyUdUDu8',
  }).then((resData) => {
    let access_token = resData.data.access_token
    axios({
      url: `https://qyapi.weixin.qq.com/cgi-bin/auth/getuserinfo`,
      params: {
        access_token,
        code,
        debug: 1,
      },
    }).then((resData) => {
      console.log(res)
      let userInfo = resData.data
      axios({
        url: `https://qyapi.weixin.qq.com/cgi-bin/auth/getuserdetail?access_token=${access_token}&debug=1`,
        data: {
          user_ticket: userInfo.user_ticket
        },
        method: 'post'
      }).then((resData) => {
        console.log(res)
        res.send({
          code: 200,
          data: {
            userInfo,
            userDetail: resData.data
          },
          message: '成功',
        })
      })
    })
  })
}前端:
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <script src="https://res.wx.qq.com/open/js/jweixin-1.2.0.js"></script>
    <script
      src="https://open.work.weixin.qq.com/wwopen/js/jwxwork-1.0.0.js"
      referrerpolicy="origin"
      ref
    ></script>
    <script src="lib/axios.min.js"></script>
    <style>
      .m-info {
        white-space: pre;
      }
      .m-img {
        width: 200px;
        height: 200px;
      }
    </style>
  </head>
  <body>
    <div>
      <div>信息</div>
      <div><span id="m-info" class="m-info"></span></div>
      <div>群ID:<span id="m-chat-id"></span></div>
    </div>
    <script src="lib/vconsole.min.js"></script>
    <script>
      // VConsole will be exported to `window.VConsole` by default.
      var vConsole = new window.VConsole()
    </script>
    <script>
      console.log(location.href)
      axios({
        url: `http://yuying-api.xutongbao.top/api/light/tools/qiWeToken`,
        method: 'post',
        data: {
          url: location.href,
        },
      }).then((res) => {
        console.log(res)
        const agentFun = () => {
          const { timestamp, noncestr, signature } = res.data.data.agentData
          wx.agentConfig({
            corpid: 'wwd70e*****290f', // 必填,企业微信的corpid,必须与当前登录的企业一致
            agentid: '1000004', // 必填,企业微信的应用id (e.g. 1000247)
            timestamp, // 必填,生成签名的时间戳
            nonceStr: noncestr, // 必填,生成签名的随机串
            signature, // 必填,签名,见附录-JS-SDK使用权限签名算法
            jsApiList: ['getCurExternalChat'], //必填,传入需要使用的接口名称
            success: function (res) {
              console.log(666, res)
              wx.invoke('getCurExternalChat', {}, function (res) {
                console.log(777, res)
                if (res.err_msg == 'getCurExternalChat:ok') {
                  let chatId = res.chatId //返回当前外部群的群聊ID
                  document.getElementById('m-chat-id').innerHTML = chatId
                } else {
                  //错误处理
                }
              })
              // 回调
            },
            fail: function (res) {
              console.log(res)
              if (res.errMsg.indexOf('function not exist') > -1) {
                alert('版本过低请升级')
              }
            },
          })
          console.log(timestamp, noncestr, signature)
        }
        agentFun()
        getUserInfo({
          access_token: res.data.data.corpData.tokenData.access_token,
        })
      })
      const parseQueryString = (url) => {
        let params = {}
        let arr = url.split('?')
        if (arr.length <= 1) {
          return params
        }
        arr = arr[1].split('&')
        for (let i = 0, l = arr.length; i < l; i++) {
          let a = arr[i].split('=')
          params[a[0]] = a[1]
        }
        return params
      }
      const getUserInfo = ({ access_token }) => {
        let qs = parseQueryString(location.href)
        let code = qs.code
        axios({
          url: `http://yuying-api.xutongbao.top/api/light/tools/qiWeUserInfo`,
          method: 'post',
          data: {
            code,
          },
        }).then((res) => {
          console.log(res)
          if (res.data.code === 200) {
            const { mobile, userid, avatar, qr_code } = res.data.data.userDetail
            console.log(avatar)
            console.log(qr_code)
            document.getElementById('m-info').innerHTML = `
            手机号:${mobile}
            UserId: ${userid}
            <div><img src="${avatar}" class="m-img" /></div>
            <div><img src="${qr_code}" class="m-img" /></div>`
          }
        })
      }
    </script>
  </body>
</html>

 






![[短的文章] Spring Boot 日志创建使用、日志级别、@Slf4j、日志持久化——Spring Boot 系列](https://img-blog.csdnimg.cn/img_convert/13588e5daa7a7d802f61e1a700aaad3d.png)









![[附源码]java毕业设计疫情防控期间网上教学管理](https://img-blog.csdnimg.cn/d902728649074f0db00dee6d44fdda84.png)


