本案例是爲一箇中型規模的外包軟件公司開發的WiFi網頁認證系統。
整個公司的網絡都是採用典型的Windows活動目錄(Active Directory)進行管理,所有員工都分配有各自的域賬戶。員工的計算機都是有線連接,因爲主要做的是外包項目,信息安全方面要求很嚴格,所以公司規定一般的員工不能上網。
現在公司希望在每一層都能爲員工提供無線WiFi上網的方式(非辦公用),但是也需要用域賬戶進行認證,而且要用Web認證(可以防止WiFi萬能鑰匙這類手機軟件)。
我們採用的方式非常簡單,即,在每一層都安裝一個無線路由器,WAN口接入公司網絡(非辦公用網段)並連接外網。無線路由器統一刷DD-WRT,安裝Wiwiz客戶端。
然後在公司內網的Java服務器中增加一個我們寫的JSP頁面,用於Web認證。
重點就在這個JSP頁面程序,這裏簡要說明一下。
這個JSP頁面程序接收來自無線網絡用戶提交的域名、用戶名和密碼,然後連接域控制器,進行域賬戶的驗證,如果出錯則報錯。如果域賬戶驗證通過,則調用Wiwiz Auth API使之完成Web認證。認證通過後,瀏覽器將自動顯示公司內網主頁。
簡易版的效果圖如下:
以下是Web認證頁面的JSP程序源碼:
- <%@ page contentType="text/html;charset=UTF-8" %>
- <%@ page import="java.io.*"%>
- <%@ page import="java.util.*"%>
- <%@ page import="java.net.*"%>
- <%@ page import="javax.naming.*"%>
- <%@ page import="javax.naming.directory.*"%>
- <%
- String userkey = "XXXXXXXXXXXXXXXX"; // 替換爲你的User Key
- String host = "XXX.XXX.XXX.XXX"; // AD服務器地址
- String port = "389"; // AD服務器端口
- String hrsys = "http://hrsys"; // 公司內網系統首頁地址,用於認證後顯示
- //****************************************************
- // 取得接收到的傳入參數
- //****************************************************
- String pTokencode = request.getParameter("tokencode"); // 接收到的傳入參數"tokencode"
- String pSrvurl = request.getParameter("srvurl"); // 接收到的傳入參數"srvurl"
- /* 如必要,把傳入參數存放於Session對象中 */
- if(pTokencode != null)
- session.setAttribute("tokencode", pTokencode);
- if(pSrvurl != null)
- session.setAttribute("srvurl", pSrvurl);
- %>
- <html>
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
- <meta http-equiv="Content-Language" content="zh">
- <meta http-equiv="Pragma" content="no-cache">
- <meta http-equiv="Cache-Control" content="no-cache">
- <title> 用戶認證 </title>
- </head>
- <body>
- <center>
- <br>
- <form action="" method="post">
- <b>請使用公司的域賬戶進行認證</b>
- <br><br>
- <table bgcolor="#eefffff" border="0" cellspacing="1" cellpadding="6" width="350">
- <tr>
- <td align="right">Domain:</td> <td><input type="text" name="domain" value="<%=request.getParameter("domain") == null ? "" : request.getParameter("domain")%>" /></td>
- </tr>
- <tr>
- <td align="right">用戶名:</td> <td><input type="text" name="username" value="<%=request.getParameter("username") == null ? "" : request.getParameter("username")%>" /></td>
- </tr>
- <tr>
- <td align="right">密碼:</td> <td><input type="password" name="pswd" /></td>
- </tr>
- </table>
- <br>
- <input type="submit" name="login" value=" 登錄 " onclick="history.back();">
- </form>
- <%if(request.getParameter("login") != null) {
- /* “登錄”按鈕按下時 */
- %>
- <table width="260" bgcolor="#888888" border="0" cellspacing="1" cellpadding="8"><tr><td bgcolor="#ffff99" align="center" valign="middle">
- <b>
- <%
- //****************************************************
- // 第1步. 根據您的具體需要或業務,進行用戶登錄驗證處理
- //****************************************************
- boolean loginSuccess = true;
- String username = request.getParameter("username"); // 用戶名
- String pswd = request.getParameter("pswd"); // 密碼
- String domain = request.getParameter("domain"); // Domain
- /* 進行輸入參數的校驗 */
- if(username.equals("")) {
- out.print("用戶名不可爲空!<br>");
- loginSuccess = false;
- }
- if(pswd.equals("")) {
- out.print("密碼不可爲空!<br>");
- loginSuccess = false;
- }
- if(domain.equals("")) {
- out.print("Domain不可爲空!<br>");
- loginSuccess = false;
- }
- /* 域賬戶驗證 */
- if(loginSuccess == true) {
- String url = new String("ldap://" + host + ":" + port);
- String user = username.indexOf(domain) > 0 ? username : username +"@"+ domain;
- Hashtable<String, String> env = new Hashtable<String, String>();
- DirContext ctx;
- env.put(Context.SECURITY_AUTHENTICATION, "simple");
- env.put(Context.SECURITY_PRINCIPAL, user);
- env.put(Context.SECURITY_CREDENTIALS, pswd);
- env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
- env.put(Context.PROVIDER_URL, url);
- try {
- ctx = new InitialDirContext(env);
- ctx.close();
- loginSuccess = true;
- } catch (NamingException e) {
- //e.printStackTrace();
- loginSuccess = false;
- out.print("登錄失敗!"); //
- }
- }
- %>
- <br>
- </b></td></tr></table>
- <%
- if(loginSuccess == true) {
- //****************************************************
- // 第2步. 調用Wiwiz Auth API,進行預認證
- // 重要: 請在服務器端的程序中進行此處理(例如,ASP、C#、JSP/Servet、PHP...),
- // 而不要在直接客戶端代碼中進行此處理(例如,HTML/Javascript)
- //****************************************************
- // 參數 "action" : 必須!
- // 設置爲"1"將使用戶認證成功
- // 設置爲"1"將使用戶認證失敗
- String action = "1";
- // 參數 "tokencode": 必須!
- // 設置與同名傳入參數相同的值
- String tokencode = (String) session.getAttribute("tokencode");
- // 參數 "srvurl": 必須!
- // 設置與同名傳入參數相同的值
- String srvurl = (String) session.getAttribute("srvurl");
- // 參數 "endtime" : 可選
- // 格式: yyyy-mm-dd hh:MM:ss 例如: 2012-05-31 21:39:00
- // 設置此參數將使用戶的Internet連接在指定時間關閉
- // 注意: 對此參數的值必須進行url編碼
- String endtime = ""; //URLEncoder.encode("2012-05-31 21:39:00", "utf-8");
- // 參數 "postauth" : 可選
- // 例如: http://www.YourDomain.com
- // 設置此參數將設置用戶在通過認證後顯示的頁面地址
- String postauth = hrsys;
- String parameters =
- "wiwiz_auth_api=1&ver=1.0"+ // 參數 "wiwiz_auth_api" 與 "ver". 固定值
- "&tokencode="+ tokencode + // 參數 "tokencode". 設置方法參考上面的說明
- "&userkey="+ userkey + // 參數 "userkey". 設置方法參考上面的說明
- "&action="+ action + // 參數 "action". 設置方法參考上面的說明
- "&endtime="+ endtime + // 參數 "endtime". 設置方法參考上面的說明
- "&postauth="+ postauth; // 參數 "postauth". 設置方法參考上面的說明
- URL u = new URL(srvurl); // 使用傳入參數"srvurl"的值作爲請求地址
- URLConnection uc = u.openConnection();
- uc.setDoOutput(true);
- uc.setRequestProperty("Content-Type","application/x-www-form-urlencoded");
- PrintWriter pw = new PrintWriter(uc.getOutputStream());
- pw.println(parameters);
- pw.close();
- BufferedReader in = new BufferedReader(
- new InputStreamReader(uc.getInputStream()));
- String verifycode = in.readLine();
- in.close();
- if(verifycode.startsWith("ERR")) {
- // 如果報錯,則顯示錯誤代碼
- out.println("Error: "+ verifycode);
- } else {
- // 如沒有報錯則進行第3步。
- //****************************************************
- // 第3步. 調用Wiwiz Auth API,完成認證
- //****************************************************
- String redirectUrl = srvurl + // 使用傳入參數"srvurl"的值作爲跳轉地址的前綴
- "?wiwiz_auth_api_login=1"+ // 參數 "wiwiz_auth_api_login",固定值
- "&tokencode="+ tokencode + // 參數 "tokencode",設置與同名傳入參數相同的值
- "&verifycode="+ verifycode; // 參數 "verifycode",設置與同名傳入參數相同的值
- response.sendRedirect(redirectUrl); // 最後,進行跳轉
- }
- }
- }%>
- </center>
- </body>
- </html>