企业微信登录流程
实现方式
使用js-sdk
使用 @wecom/jssdk 初始化企业微信登录组件。
为了满足网站定制化的需求,我们支持将企业微信登录组件内嵌到开发者的网站中。用户使用企业微信登录授权后,登录组件将 auth code 返回给网站。
企业微信登录组件主要用途:网站希望用户在网站内就能完成登录,无需跳转到企业微信域下登录后再返回,提升登录的流畅性与成功率。
使用:
使用企业微信 JS-SDK可查看官方文档,@wecom/jssdk >=1.3.1;
不再详述本篇主要介绍另一种实现方式。
效果如下:
扫码后就可获取到auth_code。
构造登录链接
同时支持通过构造链接的方式,在新窗口中打开企业微信登录页面,用户使用企业微信登录授权后通过将携带 auth code 跳转至指定的 redirect_uri。
开发流程
这里是我使用构造登录链接的开发流程。
登录授权
服务商登录授权
位置:服务商后台【开发配置】->【登录授权】。
登录授权与之前配置的应用指令回调和数据回调配置相同;但是不要使用同一个路由和方法。
登录授权回调解析
这里需要处理的是指令回调url的get和post处理响应。
Get回调处理把回调内容进行解密后返回给微信服务器,表示正确接收到回调。
Post回调处理接收登录授权各种事件回调的解析和处理:目前主要处理登录授权的ticket解密和保存。
控制器端
可以设置为同一入口,通过请求方法的不同,进行分别处理。
代码如下:
public function companyWxNotify()
{
$all = request()->all();
writeRecordLog('companyWechat.log', '数据回调' . request()->method());
writeRecordLog('companyWechat.log', var_export($all, true));
if (request()->isMethod('POST')) {
$msg_signature = request()->input('msg_signature');
$timeStamp = request()->input('timestamp');
$nonce = request()->input('nonce');
// post请求的密文数据
$sReqData = file_get_contents('php://input');
echo $this->wxService->notifyPOST($msg_signature, $timeStamp, $nonce, $sReqData);
} else {
// 获取参数
$msg_signature = request()->input('msg_signature');
$timeStamp = request()->input('timestamp');
$nonce = request()->input('nonce');
$echoStr = request()->input('echostr');
echo $this->wxService->callbackGET($msg_signature, $timeStamp, $nonce, $echoStr);
}
die();
}
业务层
Get回调处理把回调内容进行解密后返回给微信服务器,表示正确接收到回调。
Post回调处理接收登录授权各种事件回调的解析和处理:
目前主要处理登录授权的ticket解密和保存。
代码如下:
/**
* 企业微信GET回调处理(URL地址校验)
* @param $sVerifyMsgSig
* @param $sVerifyTimeStamp
* @param $sVerifyNonce
* @param $sVerifyEchoStr
* @return string
*/
public function callbackGET($sVerifyMsgSig, $sVerifyTimeStamp, $sVerifyNonce, $sVerifyEchoStr)
{
$sVerifyEchoStr = str_replace(" ", "+", $sVerifyEchoStr);
$wxcpt = new \WXBizMsgCrypt(‘Token’, ‘EncodingAESKey’, ‘通用开发参数-CorpID’);
// 调用验证函数
$sEchoStr = "";
$errCode = $wxcpt->VerifyURL($sVerifyMsgSig, $sVerifyTimeStamp, $sVerifyNonce, $sVerifyEchoStr, $sEchoStr);
writeRecordLog('companyWechat.log', $sEchoStr);
if ($errCode == 0) {
return $sEchoStr;
} else {
return "ERR: " . $errCode;
}
}
/**
* 企业微信-登录授权post回调解析
* @param $sReqMsgSig
* @param $sReqTimeStamp
* @param $sReqNonce
* @param $sReqData
* @return string
*/
public function notifyPOST($sReqMsgSig, $sReqTimeStamp, $sReqNonce, $sReqData)
{
try {
$sMsg = ""; // 解析之后的明文
$wxcpt = new \WXBizMsgCrypt(‘登录授权token’, ‘登录授权EncodingAESKey’, ‘登录授权SuiteID’);
$errCode = $wxcpt->DecryptMsg($sReqMsgSig, $sReqTimeStamp, $sReqNonce, $sReqData, $sMsg);
if ($errCode == 0) {
// 解密成功,sMsg即为xml格式的明文
writeRecordLog('companyWechat.log', "解密成功:\r\n" . var_export($sMsg, true));
// TODO: 对明文的处理
// 解析该xml字符串,利用simpleXML
libxml_disable_entity_loader(true);
//禁止xml实体解析,防止xml注入
$xml = simplexml_load_string($sMsg, 'SimpleXMLElement', LIBXML_NOCDATA);
switch ($xml->InfoType) {
case 'suite_ticket': // 存储suite_ticket
$log_desc = '登录授权-存储suite_ticket:' . $xml->SuiteTicket;
self::$redisWechat->setLoginTicket($xml->SuiteTicket);
break;
default:
$log_desc = '未知的类型:' . $xml->InfoType;
break;
}
writeRecordLog('companyWechat.log', '解析日志:' . $log_desc);
return 'success';
} else {
writeRecordLog('companyWechat.log', "ERR: " . $errCode . "\r\n");
return "ERR: " . $errCode . "\r\n";
}
} catch (\Exception $e) {
logUnit($e, 'wx_callback.log');
return 'success';
}
}
构造企业微信登录链接
同时支持通过构造链接的方式,在新窗口中打开企业微信登录页面,用户使用企业微信登录授权后通过将携带 auth code 跳转至指定的 redirect_uri 。
请求地址
企业微信
参数说明
参数名 | 类型 | 必填 | 说明 |
login_type | string | 是 | 登录类型。 ServiceApp:服务商登录;CorpApp:企业自建/代开发应用登录。 |
appid | string | 是 | 第三方登录时填登录授权 SuiteID |
agentid | string | 否 | 企业自建应用/服务商代开发应用 AgentID,当login_type=CorpApp时填写 |
redirect_uri | string | 是 | 登录成功重定向 url 需进行 URLEncode |
state | string | 否 | 登录 state 用于保持请求和回调的状态,授权请求后原样带回给企业。该参数可用于防止CSRF 攻击(跨站请求伪造攻击),建议带上该参数,可设置为简单的随机数加 session 进行校验 需进行 URLEncode |
lang | string | 否 | 语言类型。zh:中文;en:英文。 |
返回说明
用户允许授权后,将会重定向到 redirect_uri 的网址上,并且在地址栏带上 code 和 state 参数可通过返回链接获取code。
示例
首先构造web登录链接,之后扫码同意登录;回调到设置的路由地址,并携带授权code。
代码如下:
/**
* 测试登录授权-构造企业微信登录链接
*/
public function createLoginUrl()
{
$backUrl = getSolveUrl('/api/getLoginCode');
$url = "https://login.work.weixin.qq.com/wwlogin/sso/login?login_type=ServiceApp&appid=登录授权SuiteID&redirect_uri={$backUrl}&state=STATE";
echo '<a href="' . $url . '">点击获取code</a>';
}
/**
* 获取企业微信回调信息
*/
public function getLoginCode()
{
print_r(request()->input());
}
登录流程模拟
1.首先访问登录链接
2.点击后出现授权二维码
3.同意登录获取授权code
企业微信扫码同意登录后,跳转至设置好路径,获取授权code。
4.获取用户身份信息实现登录
通过授权code获取登录用户身份信息然后再跟自定义网站的账号信息绑定,实现登录。
获取登录用户身份
通过授权code获取登录用户身份。
请求方式
GET(HTTPS)
请求地址
https://qyapi.weixin.qq.com/cgi-bin/service/auth/getuserinfo3rd?suite_access_token=SUITE_ACCESS_TOKEN&code=CODE
参数说明
参数 | 必须 | 说明 |
suite_access_token | 是 | 服务商登录授权SuiteId对应的suite_access_token,参见“获取第三方应用凭证” 。不允许代开发自建应用调用。代开发自建应用获取用户身份参考“获取访问用户身份” |
code | 是 | 通过成员授权获取到的code,最大为512字节。每次成员授权带上的code将不一样,code只能使用一次,5分钟未被使用自动过期。 |
业务代码
获取access_token加上前端传递的code去获取登录用户信息。
代码如下:
public function loginAccessToken()
{
$url = "https://qyapi.weixin.qq.com/cgi-bin/service/get_suite_token";
$params = json([
'suite_id' => ‘登录授权suiteId’,
'suite_secret' => ‘登录授权secret’,
'suite_ticket' => self::$redisWechat->getLoginTicket()
]);
$info = $this->linkCurl($url, 'POST', $params);
$res = djson($info);
if (isset($res['errcode'])) {
return toFail('error', $res);
}
$access_token = $res['suite_access_token'];
return toSuccess('success', [
'access_token' => $access_token
]);
}
/**
* 请求接口返回内容
* @param $url : 请求的URL地址
* @param $method : 请求方式POST|GET
* @param bool $params : 请求的参数
* @param bool $header : 请求头
* @return bool|string
*/
protected function linkCurl($url, $method, $params = false, $header = false)
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
curl_setopt($ch, CURLOPT_URL, $url);
//curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
curl_setopt($ch, CURLOPT_FAILONERROR, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
if (strpos("$" . $url, "https://") == 1) {
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
}
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 60);
curl_setopt($ch, CURLOPT_TIMEOUT, 60);
if ($method == "POST") {
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $params);
} else if ($params) {
curl_setopt($ch, CURLOPT_URL, $url . '?' . http_build_query($params));
}
$response = curl_exec($ch);
if ($response === FALSE) return false;
curl_close($ch);
return $response;
}
/**
* 获取企业微信用户信息
* @param $code
* @return array|mixed
*/
public function getWxUserInfo($code,)
{
if (empty($code)) {
return toFail('请输入code参数!');
}
$get_access_token = $this->loginAccessToken();
if ($get_access_token['status'] != 1) {
return $get_access_token;
}
$access_token = $get_access_token['data']['access_token'];
$url = "https://qyapi.weixin.qq.com/cgi-bin/service/auth/getuserinfo3rd?suite_access_token={$access_token}&code={$code}";
$info = $this->linkCurl($url, 'GET');
$res = djson($info);
if ($res['errcode'] != 0) {
return toFail($res['errmsg'], $res);
}
return toSuccess('success', $res);
}
返回参数说明
参数 | 说明 |
errcode | 返回码 |
errmsg | 对返回码的文本描述内容 |
corpid | 用户所属企业的corpid |
userid | 用户在企业内的UserID,如果该企业与第三方应用没有授权关系时,返回密文UserId,有授权关系时,按照升级后的ID策略返回明文或密文 |
open_userid | 全局唯一。对于同一个服务商,不同应用获取到企业内同一个成员的open_userid是相同的,最多64个字节。仅第三方应用可获取 |
注意:返回与网页授权登录内容相同,只是获取方式为登录授权的参数。
总结
企业微信web登录使用构建授权登录链接实现的全过程。