在上文中有意埋了幾個安全彩蛋,以便後面在聊網絡安全時使用。
書接前言,對上文做過實踐的朋友肯定會發現:當使用者注冊/登入成功後頁面跳轉到了系統首頁,但首頁的導航菜單并沒有顯示使用者名。本文重點實作這個特性,同時也談談系統的編碼問題。
六、注冊/登入成功後導航菜單顯示目前使用者名
與JSP不同之處在于,《鬥醫》本着Web本質特點,讓不了解Web的朋友在腦海中有一個整體思路,不要把Web應用想的過于神秘,是以頁面展示部分放到了HTML中,服務端部分僅提供查詢頁面和提供資料,它們之間通過HTTP協定貫通。
正是這個原因當使用者注冊/登入成功後跳轉到系統首頁,浏覽器開始渲染main.html頁面,由于此時還沒有讓Javascript去服務端讀取使用者資訊,是以目前使用者名沒有顯示。
下面實作這個特性:
1、系統首頁調用common.js的公共接口
(function(window){
$(document).ready(function(){
// 生成系統菜單
generateSystemMenu();
// 設定首頁菜單被選中
selectSystemMenu("system_home_menu");
// 擷取使用者資訊
getBreifUserInfo();
});
})(window);
2、common.js中定義getBreifUserInfo()方法,以實作異步向服務端擷取資料
/**
* 擷取使用者的資訊:使用者名
*/
function getBreifUserInfo(){
asyncRequest("userBrief.data", null, function(result){
var briefUser = eval(result); // 其中eval是JS的不安全方法,不建議使用,這裡留個彩蛋
$("#system_login_user_name").text(briefUser.userId);
}
3、配置擷取使用者資訊的業務
在war\WEB-INF\config\sm下定義system-data.xml檔案,裡面配置如下業務:
<?xml version="1.0" encoding="UTF-8" ?>
<business-config>
<!--擷取使用者資訊,導航菜單使用-->
<business name="userBrief" business-class="com.medical.server.data.UserBriefDataAction" />
</business-config>
4、定義com.medical.server.data.UserBriefDataAction.java類,它繼承FrameDefaultAction類,同時重寫execute()方法
@Override
public String execute() throws FrameException
{
UserDAO loginUser = FrameCache.getInstance().getUserBySession(session);
if(loginUser == null)
{
loginUser = new UserDAO();
loginUser.setUserId("遊客");
}
return gson.toJson(loginUser);
這個方法中使用了FrameCache.getInstance().getUserBySession(session)方法,這個方法是從系統全局緩存中讀取會話中的使用者。既然有讀取那麼也有對應的設定,方法如下:
public class FrameCache
public UserDAO getUserBySession(HttpSession session)
{
return (UserDAO)session.getAttribute(FrameConstant.SYSTEM_CURRENT_LOGIN_USER);
}
public void setUserBySession(HttpSession session, UserDAO currentUser)
session.setAttribute(FrameConstant.SYSTEM_CURRENT_LOGIN_USER, currentUser);
這裡又涉及到一個常量定義,其具體為:FrameConstant.SYSTEM_CURRENT_LOGIN_USER = “systemCurrentLoginUser”;
(1)修改UserUtil.isValideUser()方法,把它更名為getUserDAO(),同時傳回值由原來的boolean改為UserDAO
public static UserDAO getUserDAO(String userName, String userAuth)
Session session = FrameDBUtil.openSession();
Criteria criteria = session.createCriteria(UserDAO.class);
criteria.add(Restrictions.eq("userId", userName)).add(Restrictions.eq("userAuth", userAuth));
List<?> userList = criteria.list();
FrameDBUtil.closeSession();
if(FrameUtil.isEmpty(userList))
return null;
return (UserDAO)userList.get(0);
(2)修改UserLoginDataAction的使用者注冊方法
private String doRegistAction(String userName, String userAuth)
// 1. 判斷資料庫中是否已存在該使用者名
UserDAO user = UserUtil.getUserByName(userName);
if (user != null)
UserLoginBean loginBean = new UserLoginBean();
loginBean.setErrorCode(FrameErrorCode.USER_SAME_ERROR);
loginBean.setErrorDesc(FrameUtil.getErrorDescByCode(loginBean.getErrorCode()));
return gson.toJson(loginBean);
// 2. 把使用者入庫
UserUtil.insertUser(userName, userAuth);
// 3. 存入會話對應的記憶體
user = new UserDAO();
user.setUserId(userName);
FrameCache.getInstance().setUserBySession(session, user);
// 4. 傳回使用者注冊成功JSON對象
UserLoginBean loginBean = new UserLoginBean();
loginBean.setErrorCode(FrameErrorCode.USER_LOGIN_SUCCESS);
loginBean.setErrorDesc(FrameUtil.getErrorDescByCode(loginBean.getErrorCode()));
loginBean.setForwardPath("index.act");
return gson.toJson(loginBean);
(3)修改UserLoginDataAction的使用者登入方法
private String doLoginAction(String userName, String userAuth)
UserDAO cacheUser = UserUtil.getUserDAO(userName, userAuth);
if (cacheUser == null)
loginBean.setErrorCode(FrameErrorCode.USER_NOT_EXIST_ERROR);
// 2. 存入會話對應的記憶體
cacheUser.setUserAuth(null);
FrameCache.getInstance().setUserBySession(session, cacheUser);
// 3. 傳回使用者登入成功JSON對象
【備注】:由于用戶端并不需要使用者的密碼,是以沒有必要把使用者資訊暴露,增加網絡安全風險
(4)用例驗證
用例1:
前提:系統中沒有qingkechina使用者
操作:進入系統的登入頁面,注冊名為qingkechina的使用者
期望:注冊成功且系統的菜單右上角能顯示qingkechina使用者名
用例2:
前提:系統中已有qingkechina使用者
操作:進入系統的登入頁面,以qingkechina使用者登入
期望:登入成功且系統的菜單右上角能顯示qingkechina使用者名
用例3:
前提:系統中沒有“陳許諾”使用者
操作:進入系統的登入頁面,注冊名為“陳許諾”的使用者
期望:注冊成功且系統的菜單右上角能顯示“陳許諾”使用者名
打開Eclipse啟動Tomcat成功後,在浏覽器中輸入http://localhost:8080/medical回車,按上面的使用者驗證,會發現前兩個英文用例成功,但中文用例存在亂碼問題,如下圖:
<a href="http://s3.51cto.com/wyfs02/M02/23/83/wKioL1M5JCPgxu-WAAAe6wn9Xh4060.png" target="_blank"></a>
七、系統的編碼與亂碼
系統出現亂碼的原因簡而言之是由于:輸入與輸出編碼不一緻,比如說浏覽器在Windows中文作業系統下運作,Chrome、FireFox預設是以GBK編碼顯示,此時若服務端傳給浏覽器的編碼是UTF-8,則就會形成亂碼。
<a href="http://s3.51cto.com/wyfs02/M01/23/82/wKiom1M5KiCy38apAAAbDg1Siy0785.png" target="_blank"></a>
解釋:
IE浏覽器根據作業系統預設選擇編碼,可通過“檢視 > 編碼”來檢視;FireFox浏覽器在中文windows作業系統下預設使用unicode編碼,可通過“檢視 > 字元編碼”檢視;Chrome浏覽器在中文windows作業系統下預設使用GBK編碼,可通過選擇“設定 > 進階設定 > 網絡内容 > 自定義字型 > 編碼”檢視;
Tomcat在中文windows作業系統下預設GBK編碼;但Java依賴于JVM的具體環境,一般都是以unicode編碼;
mysql安裝時預設以latin1編碼;
properties檔案當時設定時以utf-8編碼;
看着是不是有點暈了?是以若想不讓其亂碼,最好統一成同一個編碼,這裡使用UTF-8。
1、浏覽器顯示使用UTF-8編碼
這一點在前面寫HTML頁面時已指定頁面的編碼為UTF-8,如打開main.html在它的<head>中已說明
<!--設定字元集-->
<meta http-equiv="content-type" content="text/html;charset=utf-8" />
2、浏覽器與Tomcat之間可以通過filter過濾器的方式,讓所有的請求和響應都使用UTF-8編碼
(1)在war\WEB-INF\web.xml配置編碼過濾器
<filter>
<filter-name>encoder</filter-name>
<filter-class>com.medical.frame.FrameEncoderFilter</filter-class>
</filter>
<filter-mapping>
<url-pattern>/*</url-pattern>
</filter-mapping>
【備注】:由于url-pattern配置為/*,它表明所有的請求都經過FrameEncoderFilter
(2)定義FrameEncoderFilter,讓其實作Filter接口
public class FrameEncoderFilter implements Filter
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
request.setCharacterEncoding("utf-8");
response.setCharacterEncoding("utf-8");
chain.doFilter(request, response);
(3)檢視Mysql的編碼,發現它以latin1編碼的(具體檢視辦法可以問google)
I、關閉mysql程序。以我用的windows作業系統為例,進入“任務管理器 > 程序”,右鍵mysqld.exe結束程序樹
II、打開C:\Program Files\MySQL\MySQL Server 5.5\my.ini檔案,找到如下内容修改為utf8
default-character-set=utf8
character-set-server=utf8
【備注】:因為我安裝在C槽,請讀者根據自己的實際情況處理
III、重新開機Mysql程序。進入C:\Program Files\MySQL\MySQL Server 5.5\bin,輕按兩下mysqld.exe可執行檔案
(4)重建立資料庫medical和資料表usertable
I、打開所有程式 > MySQL > MySQL Server 5.5 > MySQL 5.5 Command Line Client指令工具
II、在視窗中輸入密碼進入
III、執行如下SQL語句,如圖
<a href="http://s3.51cto.com/wyfs02/M00/23/82/wKiom1M5Mxix3xs7AAA44DZYCx0047.png" target="_blank"></a>
(5)把Java的unicode轉換為utf-8編碼
* 把ISO編碼的字元串轉換為UTF-8編碼
public static String convert2UTF8(String resource)
String descStr = resource;
try
byte[] resourceArray = resource.getBytes("ISO-8859-1");
descStr = new String(resourceArray, "utf-8");
catch (UnsupportedEncodingException e)
e.printStackTrace();
return descStr;
* 由錯誤碼擷取錯誤描述資訊
public static String getErrorDescByCode(int errorCode)
String errorCodeStr = String.valueOf(errorCode);
String errorDesc = FrameCache.getInstance().getResourceValue(errorCodeStr);
if (isEmpty(errorDesc))
return convert2UTF8("系統異常,錯誤碼:" + errorCode);
return convert2UTF8(errorDesc);
再測試一下用例三,結果如下:
<a href="http://s3.51cto.com/wyfs02/M01/23/83/wKiom1M5PJmjwRrUAAAUdNw1KrI980.png" target="_blank"></a>
八、全局資訊提示欄
做過C/S架構開發的肯定知道模态對話框和非模态對話框的概念,提示資訊正是通過對話框來展現給使用者的;當然B/S架構也不離外,它也有模态和非模态的對話框。
但細心的您肯定關注到了:現在的網站展現形式越來越“Web化”。以前大家都用翻頁突然一天各大中型網站好像都不翻頁了,而改為拖拽樣式,像浏覽百度圖檔等。
這種樣式的變化不是必然的,它更符合使用者的操作習慣。
全局資訊提示框的大概思路,由showSystemGlobalInfo()方法生成一個div,并把它追加到<body> Dom元素上,然後由navigation.css全局樣式渲染它,當使用時直接調用showSystemGlobalInfo()方法,5鈔鐘之後div自動隐藏掉。
1、在common.js中定義公共方法showSystemGlobalInfo()
* 全局資訊提示:在螢幕最下方顯示
var sytemGlobalInfoDiv = null;
function showSystemGlobalInfo(message)
// 隻初始化一次
if(!sytemGlobalInfoDiv)
sytemGlobalInfoDiv = $("<div />").attr("class", "system_global_info").text(message);
sytemGlobalInfoDiv.appendTo($("body"));
// 停留5s鐘後提示框自動消失
sytemGlobalInfoDiv.text(message).show();
setTimeout(function(){
sytemGlobalInfoDiv.hide();
}, 5000);
2、在navigation.css中定義樣式
.system_global_info{
width: 100%;
height: 45px;
line-height: 45px;
color: #FFF;
font-size: 14px;
font-weight: 600;
text-align: center;
background-color: #0767C8;
position: absolute;
bottom: 0;
3、把使用者注冊/登入失敗的地方更換為調用此方法,涉及login.js的systemUserLogin()方法
var resultJson = eval(result);
if(resultJson.errorCode != 510)
alert(resultJson.errorDesc);
showSystemGlobalInfo(resultJson.errorDesc);
return;
4、功能測試
(1)先在系統中建立名稱為qingkechina的使用者,確定建立成功
(2)再次建立名稱為qingkechina的使用者,此時應該有錯誤提示,如下圖:
<a href="http://s3.51cto.com/wyfs02/M01/23/AC/wKioL1M-JcDAUE5dAAAY4LkjVr0258.png" target="_blank"></a>
【備注】:截止目前登入部分算是完成,裡面還涉及一些不安全的處理,一些沒有考慮到的,這裡暫時保留。接下來完成“下戰書”部分業務。
<a href="http://down.51cto.com/data/2364257" target="_blank">附件:http://down.51cto.com/data/2364257</a>
本文轉自qingkechina 51CTO部落格,原文連結:http://blog.51cto.com/qingkechina/1390410,如需轉載請自行聯系原作者