微信於9月份推出企業號後引起了業界不小的反響,許多企業都在思索企業號將如何影響企業的運營,從本文開始,我將詳細闡述微信企業號開發的相關知識,而本文將著重於如何實現更高安全機制的二次驗證。
申請企業體驗號:
企業號顧名思義就是企業來申請的號,申請時就像申請服務號碼一樣,需要提供各種組織證明文件,對廣大開發者來說很難操作,好在騰訊公司也像服務號一樣開通了體驗號申請,留意企業體驗號的有效期間非常短,只有90天(服務號測試帳號有1年有效期),且如果企業體驗號長期不使用還會收到騰訊公司提前失效的提醒郵件。企業體驗號碼的申請連結如下,開發者只需要依照騰訊公司的引導完成註冊步驟,立刻就能取得體驗號碼:
http://qydev.weixin.qq.com/try?t= experience
通訊錄新增成員:
與公眾號不同的是,因為是面向企業內部,所以騰訊允許企業主動添加粉絲,具體操作是進入到通訊錄後點選+按鈕新增成員,留意作為唯一識別個人訊息,微訊號、手機號碼或信箱必須至少有一個,直接蒐集微訊號通常比較困難,一般可以使用企業HR資料庫裡的手機號碼和信箱等訊息,具體操作上除了手動添加還可以透過Excel模板導入以及透過騰訊企業號微信API來添加,關於API添加用戶稍後章節介紹。
#
#
輸入完成以後,可以將企業微訊號的二維碼傳送給員工,員工掃描後會自動出現系統預設的企業號小助手,小助手會自動引導員工經過信箱或手機驗證碼來完成員工身分綁定的過程,此為一次驗證,企業自行確保通訊錄員工資料的正確性,後續依賴騰訊公司來進行員工驗證,驗證通過後通訊錄狀態列的問號會消失,顯示一次驗證通過:
#
##為此我們可以參考企業號官方介面文件http://qydev.weixin.qq.com/wiki/index.phptitle=%E5%85%B3%E6%B3%A8% E4%B8%8E%E5%8F%96%E6%B6%88%E5%85%B3%E6%B3%A8在Force.com平台開發對應頁面。
開發二次驗證用頁面:
同樣,頁面分成兩個部分,一部分是顯示部分,用來輸入用戶名和密碼,頁面示意圖如下,用戶輸入使用者名稱user以及密碼123點選綁定按鈕既可以完成綁定:
################## ### #########此時會彈出下列視窗需要輸入企業的二次驗證頁面位址:###################### ## ##################為此我們可以參考企業號官方介面文件http://qydev.weixin.qq.com/wiki/index.phptitle=%E5 %85%B3%E6%B3%A8%E4%B8%8E%E5%8F%96%E6%B6%88%E5%85%B3%E6%B3%A8在Force.com平台開發對應頁面。 ###### ######發展二次驗證用頁:###同樣,頁面分成兩個部分,一部分是顯示部分,用來輸入使用者名稱和密碼,頁面示意圖如下,使用者輸入使用者名稱user以及密碼123點擊綁定按鈕既可以完成綁定:
#頁名是EmployeeAuth,頁面程式碼如下,有些屬於apex程式碼特有的標籤,無需做深入理解,重要是在第13行按鈕的action屬性指定了bind方法,當點擊按鈕的時候將調用控制器類別EmployeeAuthController的bind方法:
<apex:page standardstylesheets="false" showHeader="false" sidebar="false" controller="EmployeeAuthController"> <font size="50"> <h1>Please input your user name and password</h1> </font> <font size="30"> UserName: user<br /> Password: 123<br /><br /> <hr/> <apex:form > UserName: <apex:inputText size="100" style="height:100px" value="{!strUsername}" id="strUsername"/><br /><br /> Password: <apex:inputText size="100" style="height:100px" value="{!strPassword}" id="strPassword"/><br /><br /> <center> <apex:commandButton value="Bind" style="width:600px; height:100px;font-size:50px" action="{!bind}" id="bind" /> </center> </apex:form> {!msg} </font> </apex:page>
#
在解讀EmployeeAuthController控制器類別的程式碼前我們先來看看微信二次認證的步驟。
二次驗證的步驟與機制:
1. 首先,當微信一次驗證(或郵箱或手機號碼等認證)完成後,微信會發送如下圖所示的訊息給到用戶:
2. 頁面跳轉:
當使用者點擊這個圖文的時候實際上打開了一個位於open.weixin.qq.com網站下面的網頁,這個頁面會做一些處理後跳到前面在二次驗證裡設定的URL也就是我們正在開發的這個頁面,在跳轉的時候還會再我們設定的URL後面加上參數code=CODE&state=STATE,例如在本例裡二次驗證配置的URL是http://johnson0001-developer-edition.ap1.force.com/EmployeeAuth,那麼從騰訊openweixin.qq. com跳轉後實際打開的URL是http://johnson0001-developer-edition.ap1.force.com/EmployeeAuth?code=CODE&state=STATE 。這裡的state參數是幹嘛的騰訊公司並沒有說明目前看也不重要。重要的是code參數,利用這個參數可以呼叫騰訊的oauth2介面換取員工的userid,留意userid是一個很重要的概念,在企業號裡沒有微信OpenId一說,只有userid用來唯一標識用戶,這個userid實際上就是我們在維護通訊錄時的帳號欄位值:
3. 透過code呼叫騰訊oauth2介面換取員工userid
關於這個介面的說明請參考騰訊文件http://qydev.weixin.qq.com/wiki/ index.phptitle=%E6%A0%B9%E6%8D%AEcode%E8%8E%B7%E5%8F%96%E6%88%90%E5%91%98%E4%BF%A1%E6%81 %AF,也可以參加下方說明,這裡需要特別說明的是access token和agentid:
做過微信公共號開發或看過前面介紹相關開發文章的讀者應該不會陌生,當主動呼叫騰訊的api時都需要access token已確保訪問的正當性,獲得access token相應的也有一個專門的接口,具體的介紹可以參見騰訊公司文檔http://qydev.weixin.qq.com/wiki/index.php?title=%E4%B8%BB%E5%8A %A8%E8%B0%83%E7%94%A8,簡單點說獲得access token實際上就是透過以下介面:
https://qyapi.weixin.qq.com/cgi-bin/gettoken ?corpid=id&corpsecret=secrect
這個介面裡Corpid好找,開啟設定就能找到,如下圖:
不过corpsecret就没那么好找,实际是需要系统管理员在后台创建管理组,创建管理组后就可以拥有相应的Secret,而这个Secret所拥有的访问权限就是系统管理员创建的管理组所拥有的权限,腾讯文章http://qydev.weixin.qq.com/wiki/index.php?title=Secret也有提到:
再回过头来说agentid腾讯文档里提到指的是“跳转链接时所在的企业应用ID”,在本例里其实指的就是发送“身份验证”图文消息的那个应用也就是“企业小助手”的应用ID,当然在不同的用户场景里可能会是不同的应用在调用换取userid接口,如何查看“企业小助手”的应用ID呢?进到应用中心,第一个就是企业小助手,点击进入就可以看到如下图所示的企业应用ID了:
4. 二次验证
拿到userid后实际就可以进行二次验证了,二次验证的方式有很多种,例如如果公司已经建立起良好的通讯录管理机制(userid等和企业人力资源数据库同步,入职离职员工均能和企业号通讯录同步),拿到userid后只要判断这个userid是一位在职员工就可以自动判断为二次验证通过,或者再保险点如本例演示的,要求员工输入公司的员工用户名和密码进行验证。留意,输入用户名和密码验证的页面也就是我们前面提到的二次验证页面是属于企业拥有也是企业开发的,这样就确保了企业对安全的控制,具体操作上,用户输入用户名和密码后企业可以调用已有的接口进行验证,如果验证成功则将员工的userid等信息保存在业务系统数据库中一遍后续操作。
5.通知腾讯关注成功
现在最后一步等企业在自己的网页里完成了用户验证后只剩下通知腾讯该用户已经验证成功让相应员工关注成功,此时应该调用如下接口,可以看到接口需要的第二个参数即是我们前面换回来的userid:
https://qyapi.weixin.qq.com/cgi-bin/user/authsucc?access_token=ACCESS_TOKEN&userid=USERID
此接口的详细说明如下:
二次验证的代码实现:
按照前面的思路,我们首先获取从腾讯跳转过来的code,并通过code换取用户的userid,换取的这个过程在页面加载中完成,为此主要代码应放在类构造器里。下面的代码里设置了五个变量,其中strPassword和strUsername和用户在页面里输入的用户名和密码相对应,userID用来存储换回来的userid信息,msg用来调试帮助在页面里显示中间信息,accessToken则用来存储access token:
public class EmployeeAuthController { public String strPassword { get; set; } public String strUsername { get; set; } public String msg { get; set; } public String userID { get; set; } public String accessToken { get; set; } public EmployeeAuthController (){ accessToken = obtainAccessToken(); String code = ApexPages.currentPage().getParameters().get('code'); //Obtain user ID Http h = new Http(); HttpRequest req = new HttpRequest(); req.setMethod('GET'); req.setHeader('Accept-Encoding','gzip,deflate'); req.setHeader('Content-Type','text/xml;charset=UTF-8'); req.setHeader('User-Agent','Jakarta Commons-HttpClient/3.1'); req.setEndpoint('https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo?access_token=' + accessToken + '&code=' + code + '&agentid=0'); String bodyRes = ''; try{ HttpResponse res = h.send(req); bodyRes = res.getBody(); } catch(System.CalloutException e) { System.debug('Callout error: '+ e); ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.FATAL, e.getMessage())); } msg = bodyRes ; //String operation to obtain userID: JSONParser parser = JSON.createParser(bodyRes); while(parser.nextToken() != null){ if((parser.getCurrentToken() == JSONToken.FIELD_NAME)){ String fieldName = parser.getText(); parser.nextToken(); if(fieldName == 'UserId'){ userID = parser.getText(); } } } msg = userID; } }
上述代码第9行调用obtainAccessToken方法获取accessToken,后续会介绍该方法的详情,accessToken两个小时内会失效,所以这里采取实时获取的方式,当然可以设计的再巧妙些以省却每次实时获取accessToken的网络开销。第10行获得了从腾讯跳转过来时带的code参数,从第11行通过HttpRequest方法来调用换取接口获得userid,留意第18行指定了agentid为0,这是因为验证消息是从企业小助手应用发起的,而企业小助手应用id是0。第29行开始解析返回来的JSON数据获取userid。
下面是obtainAccessToken方法,方法内容也比较直接,主要通过调用gettoken接口来获取accessToken,并通过JSONParser类来解析返回的JSON数据以获得accessToken:
private String obtainAccessToken(){ String token; Http h = new Http(); HttpRequest req = new HttpRequest(); req.setMethod('GET'); req.setHeader('Accept-Encoding','gzip,deflate'); req.setHeader('Content-Type','text/xml;charset=UTF-8'); req.setHeader('User-Agent','Jakarta Commons-HttpClient/3.1'); req.setEndpoint('https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=wx548178d7f347f582&corpsecret=9pwWy0AVoT6V65hnwZLYdi4jnLLx65ofBRb_Ds0mAozysQoywDaqbqYCqglm2vhr'); String bodyRes = ''; try{ HttpResponse res = h.send(req); bodyRes = res.getBody(); } catch(System.CalloutException e) { System.debug('Callout error: '+ e); ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.FATAL, e.getMessage())); } msg = bodyRes; JSONParser parser = JSON.createParser(bodyRes); while(parser.nextToken() != null){ if((parser.getCurrentToken() == JSONToken.FIELD_NAME)){ String fieldName = parser.getText(); parser.nextToken(); if(fieldName == 'access_token'){ token= parser.getText(); } } } msg = token; return token; }
接下来最重要的方法是bind方法,该方法将负责用户身份验证以及通知腾讯用户关注成功,可以看到下面代码里第2行到第6行只做了很简单的用户名密码校验,真实场景里可以根据企业的具体认证机制进行替换,从第9行开始也即企业内部用户认证通过后开始调用authsucc接口通知腾讯用户关注成功。
public PageReference bind() { if(!strUsername.equals('user')){ msg = 'Please input correct user name'; } else if(!strPassword.equals('123')){ msg = 'Please input correct password'; } else{ msg = 'Bind successfully!'; //Notify tencent to add user Http h = new Http(); HttpRequest req = new HttpRequest(); req.setMethod('GET'); req.setHeader('Accept-Encoding','gzip,deflate'); req.setHeader('Content-Type','text/xml;charset=UTF-8'); req.setHeader('User-Agent','Jakarta Commons-HttpClient/3.1'); req.setEndpoint('https://qyapi.weixin.qq.com/cgi-bin/user/authsucc?access_token=' + accessToken + '&userid=' + userID); String bodyRes = ''; try{ HttpResponse res = h.send(req); bodyRes = res.getBody(); } catch(System.CalloutException e) { System.debug('Callout error: '+ e); ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.FATAL, e.getMessage())); } msg = bodyRes ; } }
更多Force.com微信企业号开发系列- 启用二次验证相关文章请关注PHP中文网!