梁方勇 陳桂強(qiáng)
(華中師范大學(xué),湖北 武漢 430079)
隨著信息技術(shù)和移動(dòng)互聯(lián)網(wǎng)的快速發(fā)展,尤其是微信、企業(yè)微信、微信公眾平臺(tái)的廣泛應(yīng)用,各高校均在大力推進(jìn)數(shù)字校園建設(shè),并依托企業(yè)微信、微信公眾號(hào)等平臺(tái)將各業(yè)務(wù)系統(tǒng)集成到平臺(tái)中,為廣大師生提供便捷的移動(dòng)互聯(lián)服務(wù)。在PC 時(shí)代,各高校一般通過單點(diǎn)登錄(SSO)實(shí)現(xiàn)統(tǒng)一身份認(rèn)證,需要輸入統(tǒng)一的用戶名和密碼,根據(jù)權(quán)限進(jìn)入各自的業(yè)務(wù)系統(tǒng)。在移動(dòng)互聯(lián)時(shí)代,人們已經(jīng)習(xí)慣利用移動(dòng)互聯(lián)平臺(tái)快速獲取信息、進(jìn)行移動(dòng)辦公。一方面,如果使用傳統(tǒng)技術(shù)登錄OA 等移動(dòng)端業(yè)務(wù)系統(tǒng),需要掃碼二維碼或憑用戶名、密碼登錄,操作體驗(yàn)不夠便捷。另一方面,各單位業(yè)務(wù)系統(tǒng)已經(jīng)實(shí)現(xiàn)了PC 端的單點(diǎn)登錄,擁有較好的基礎(chǔ),需要通過技術(shù)手段遷移到移動(dòng)端,實(shí)現(xiàn)移動(dòng)端的身份認(rèn)證。
在企業(yè)微信平臺(tái)下,通過系統(tǒng)設(shè)計(jì)和技術(shù)研發(fā),將企業(yè)微信的OAuth 身份認(rèn)證功能與單點(diǎn)登錄(SSO)中的CAS 認(rèn)證有機(jī)融合,實(shí)現(xiàn)無感登錄,即無須輸入用戶名和密碼即可登錄企業(yè)微信平臺(tái),并根據(jù)用戶權(quán)限訪問不同的業(yè)務(wù)系統(tǒng),有效減輕使用人員記憶密碼的負(fù)擔(dān),提升用戶使用體驗(yàn),提高用戶身份認(rèn)證的安全性和便利性,已成為當(dāng)前高校數(shù)字校園移動(dòng)互聯(lián)系統(tǒng)設(shè)計(jì)中需要解決的重要問題?;谶@樣一種設(shè)想,該文通過具體案例,研究探索該功能的實(shí)現(xiàn)路徑,該系統(tǒng)設(shè)計(jì)方案同時(shí)適用于微信公眾平臺(tái)、微信小程序與SSO集成融合,也可為其他高校的數(shù)字校園建設(shè)提供一定的參考借鑒。
各單位的信息化建設(shè)是一個(gè)循序漸進(jìn)的過程,在建設(shè)中根據(jù)各種需要構(gòu)建了相應(yīng)的信息系統(tǒng),并形成了各自獨(dú)立的用戶認(rèn)證體系。如果不引入單一用戶登錄的解決方案,工作人員需要記憶各類系統(tǒng)的用戶名和密碼,用戶容易混淆或者忘記登錄信息[1],嚴(yán)重影響工作效率。
單點(diǎn)登錄(Single Sign On),簡(jiǎn)稱為SSO,為實(shí)現(xiàn)用戶統(tǒng)一身份認(rèn)證的重要組成部分[2],是比較流行的企業(yè)業(yè)務(wù)整合的解決方案之一。通過應(yīng)用SSO 技術(shù)可實(shí)現(xiàn)登錄一次訪問所有應(yīng)用系統(tǒng)。
SSO 一般將開源軟件CAS 作為基礎(chǔ)平臺(tái),包括CAS Client 和CAS Server 兩個(gè)部分。CAS Client 負(fù)責(zé)處理對(duì)客戶端受保護(hù)資源的訪問請(qǐng)求,需要登錄時(shí),重定向到CAS Server;CAS Server 主要負(fù)責(zé)對(duì)用戶的認(rèn)證工作。CAS 認(rèn)證過程如圖1 所示。
圖1 CAS 認(rèn)證過程
圖1 說明了CAS 的認(rèn)證過程[3],共分為6 步。第一步,用戶通過客戶端瀏覽器請(qǐng)求訪問應(yīng)用;第二步,CAS Client將該請(qǐng)求重定向到CAS Server;第三步,用戶輸入信息,CAS Server 進(jìn)行認(rèn)證;第四步,認(rèn)證成功后CAS Server 會(huì)產(chǎn)生一個(gè)隨機(jī)Service Ticket;第五步,驗(yàn)證票據(jù)。CAS Server 驗(yàn)證Service Ticket 的合法性;第六步,返回用戶信息。CAS Server 驗(yàn)證票據(jù)通過后,返回用戶認(rèn)證結(jié)果信息。
企業(yè)微信平臺(tái)實(shí)現(xiàn)無感登錄的本質(zhì)是對(duì)訪問者身份信息進(jìn)行認(rèn)證[4]。根據(jù)微信開發(fā)者文檔,獲取用戶身份信息實(shí)現(xiàn)單點(diǎn)登錄可分為5 步。第一步獲取授權(quán)標(biāo)識(shí)code??梢酝ㄟ^前端網(wǎng)頁加載一個(gè)url,調(diào)用企業(yè)微信接口獲取授權(quán)標(biāo)識(shí)code;第二步獲取accessToken。它是企業(yè)微信或微信公眾號(hào)的唯一接口調(diào)用憑據(jù),可以使用corpId 和Secret 調(diào)用接口來獲取accessToken,調(diào)用各類接口時(shí)都必須使用accessToken;第三步,獲取用戶ID(userid)。通過code 和accessToken 調(diào)用接口獲取用戶信息userid;第四步通過userid 獲取用戶信息。通過userid 和accessToken 調(diào)用接口獲取用戶信息userinfo;第五步是與單點(diǎn)登錄系統(tǒng)進(jìn)行集成融合。整個(gè)流程如圖2 所示。
圖2 數(shù)字校園下基于單點(diǎn)登錄的企業(yè)微信無感登錄系統(tǒng)的流程圖
數(shù)字校園下基于單點(diǎn)登錄的企業(yè)微信無感登錄系統(tǒng)的整體架構(gòu)如圖3 所示。首先,用戶登錄企業(yè)微信或微信公眾平臺(tái)。其次,通過后臺(tái)調(diào)用接口取得userid,并對(duì)該userid進(jìn)行身份驗(yàn)證。最后,驗(yàn)證通過后集成單點(diǎn)登錄系統(tǒng),即可根據(jù)權(quán)限進(jìn)入不同業(yè)務(wù)系統(tǒng)。
圖3 系統(tǒng)整體架構(gòu)圖
該文以基于單點(diǎn)登錄的企業(yè)微信無感登錄系統(tǒng)為例,根據(jù)上述系統(tǒng)架構(gòu)及流程設(shè)計(jì),各模塊的核心Java 代碼如下。
3.2.1 企業(yè)微信平臺(tái)登錄模塊代碼
企業(yè)微信平臺(tái)登錄實(shí)質(zhì)上就是鏈接到一個(gè)Web 頁面地址,例如本系統(tǒng)中為http://hrzm.ccnu.edu.cn/login.jsp,通過在登錄login.jsp 頁面中添加JavaScript 代碼,JavaScript 程序就會(huì)開始檢測(cè),如果客戶端為微信平臺(tái),則跳轉(zhuǎn)到企業(yè)微信接口域,此接口域內(nèi)含重定向的服務(wù)器地址url。前端login.jsp 頁面核心代碼如下:
<SCRIPT LANGUAGE="JavaScript">
function init(){
var weixin=is_weixin();//判斷是否微信平臺(tái)
if(weixin){
window.location="https://open.weixin.qq.com/connect/oauth2/authorize?appid=corpid&redirect_uri=http%3A%2F%2fhrzm.ccnu.edu.cn%2FloginWxOauth2Action.do&response_type=code&scope=snsapi_base&state=#wechat_redirect。";//進(jìn) 入移動(dòng)端微信登錄}
else{
window.location="/login.do";//進(jìn)入PC 端單點(diǎn)登錄
}}
init();
</SCRIPT>
跳轉(zhuǎn)成功后即轉(zhuǎn)發(fā)服務(wù)器地址url,同時(shí)地址中會(huì)自動(dòng)帶上授權(quán)標(biāo)識(shí)code,在后臺(tái)可以通過返回的url 獲得授權(quán)標(biāo)識(shí)code,獲得授權(quán)標(biāo)識(shí)code 的Java 代碼如下:
String code = request.getParameter(“code”);
此模塊的流程如圖4 所示。
圖4 登錄模塊獲取授權(quán)標(biāo)識(shí)code 流程圖
3.2.2 獲取accessToken 模塊代碼
根據(jù)企業(yè)號(hào)corpID和Secret調(diào)用企業(yè)微信接口來獲取accessToken。核心Java 代碼[5]如下:
/** * 獲取AccessToken
*/
public static AccessToken getAccessToken(){
AccessToken token = new AccessToken();
try{
String url="https://gyapi.weixin.gg.com/cgi-bin/gettoken?corpid="+ID+"&corpsecret ="+SECRET ;//ID 和SECRET 為static final 變量,可在微信公眾平臺(tái)開發(fā)者中心頁面獲得
JSONObject jsObj = doGetStr(url);//解析url
if(jsObj!=null){
token.setAccessToken(jsObj.getString("access_token"));//設(shè)置access_token
token.setExpiresIn(jsObj.getInt("expires_in"));// 設(shè) 置access_token 的有效期
return token;}
}catch (Exception e) {token =null;}
return null;
}
3.2.3 獲取用戶ID 模塊代碼
根據(jù)獲取的code 和accessToken 調(diào)用企業(yè)微信接口獲取用戶信息userid。核心Java 代碼如下:
/** * 根據(jù)AccessToken 和code 獲取用戶id
*/
public static String getUserID(String access_token, String code){
String UserId = "";
String url = "https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo?access_token="+access_token+"&code="+code;
JSONObject jsObj= doGetStr(url);//解析url if(jsObj!=null){
UserId = jsObj.getString("UserId");}return UserId;
}
取得UserID 后,存儲(chǔ)到request 中,并轉(zhuǎn)發(fā)單點(diǎn)登錄模塊。
request.setAttribute("UserId", UserId);
request.getRequestDispatcher("/sso.do").forward(request,response);
3.2.4 根據(jù)用戶ID 獲取用戶信息
通過獲取的userid 和accessToken 調(diào)用接口獲取用戶信息userinfo,可以得到用戶的mobile、e-mail、name 等信息。核心Java 代碼如下:
/** * 根據(jù)AccessToken 和userid 獲取用戶信息
*/
public static JSONObject getUserID(String access_token, String userid) {
String url = "https://qyapi.weixin.qq.com/cgi-bin/user/get?access_token="+access_token+"&userid="+userid;
JSONObject UserInfo = doGetStr(url);
if(UserInfo !=null){return UserInfo;}
}
3.2.5 集成單點(diǎn)登錄系統(tǒng)代碼
從request 中取得userid 后,會(huì)進(jìn)入正常的表單登錄認(rèn)證的AuthenticationHandler 中,需要在執(zhí)行正常的認(rèn)證邏輯之前插入企業(yè)微信認(rèn)證邏輯。CAS 服務(wù)器則保存用戶憑證Principal,通過驗(yàn)證用戶憑證即可訪問各業(yè)務(wù)系統(tǒng)。核心Java代碼如下:
String UserId=request.getAttribute("UserId");
if(!UserId.notEqual(null)&&!UserId.notEqual(""){
credential.setUsername(UserId);
Return createHandlerResult(credential,this.principleFactory.createPrincipal(credential.getUsername()),null);
}
//驗(yàn)證用戶憑證代碼
AttributePrincipal principal = (AttributePrincipal)request.getUserPrincipal();
String userName = principal.getName();//通過用戶憑證獲取username
最后通過取得的userName 得到用戶權(quán)限,進(jìn)入不同的業(yè)務(wù)系統(tǒng),如此即實(shí)現(xiàn)了基于單點(diǎn)登錄的企業(yè)微信無感登錄系統(tǒng)。整個(gè)登錄過程在用戶層面是感受不到登錄過程的,把便利留給了用戶,極大地提升了用戶體驗(yàn)。
系統(tǒng)采用B/S 架構(gòu),運(yùn)行環(huán)境選擇安裝jdk1.7.0_72,Web服務(wù)器采用Apache Tomcat7.0.100,數(shù)據(jù)庫(kù)為Oracle 10,操作系統(tǒng)為Microsoft Sever,開發(fā)語言為Java、Jsp、HTML、JavaScript。以及需要下載安裝第三方庫(kù),包括struts 庫(kù)、requests 庫(kù)、json 庫(kù)和單點(diǎn)登錄SSO 依賴包c(diǎn)as-server-3.5.1+cas-client-3.2.1 等。
企業(yè)微信平臺(tái)使用OAuth2.0 標(biāo)準(zhǔn)實(shí)施安全驗(yàn)證。OAuth2.0 是一個(gè)關(guān)于授權(quán)(authorization)的開放網(wǎng)絡(luò)標(biāo)準(zhǔn),OAuth2.0 中使用的授權(quán)碼模式(authorization code)是功能最完整、流程最嚴(yán)密的授權(quán)模式,也是最安全的一種模式。這使集成單點(diǎn)登錄系統(tǒng)后的整個(gè)系統(tǒng)的安全性也得到了很好的保障。
該文提出了Java 語言環(huán)境中數(shù)字校園下基于單點(diǎn)登錄的企業(yè)微信無感登錄系統(tǒng),通過將企業(yè)微信的OAuth 身份認(rèn)證與單點(diǎn)登錄(SSO)中的CAS 認(rèn)證相融合,較好地解決了企業(yè)微信平臺(tái)下登錄多個(gè)業(yè)務(wù)系統(tǒng)時(shí)無須輸入用戶名和密碼的問題,有效減輕了使用人員記憶密碼的負(fù)擔(dān),對(duì)提升用戶使用體驗(yàn)有較大意義,同時(shí)應(yīng)用OAuth2.0安全機(jī)制也保障了系統(tǒng)的安全性。實(shí)例驗(yàn)證標(biāo)明,該無感登錄系統(tǒng)設(shè)計(jì)方案亦適用于微信公眾號(hào)等平臺(tái)下與業(yè)務(wù)系統(tǒng)的集成,具有較強(qiáng)的拓展性。