天天看點

【鬥醫】【12】Web應用開發20天

在上文中有意埋了幾個安全彩蛋,以便後面在聊網絡安全時使用。

書接前言,對上文做過實踐的朋友肯定會發現:當使用者注冊/登入成功後頁面跳轉到了系統首頁,但首頁的導航菜單并沒有顯示使用者名。本文重點實作這個特性,同時也談談系統的編碼問題。

六、注冊/登入成功後導航菜單顯示目前使用者名

   與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回車,按上面的使用者驗證,會發現前兩個英文用例成功,但中文用例存在亂碼問題,如下圖:  

【鬥醫】【12】Web應用開發20天

七、系統的編碼與亂碼

   系統出現亂碼的原因簡而言之是由于:輸入與輸出編碼不一緻,比如說浏覽器在windows中文作業系統下運作,chrome、firefox預設是以gbk編碼顯示,此時若服務端傳給浏覽器的編碼是utf-8,則就會形成亂碼。

【鬥醫】【12】Web應用開發20天

解釋:

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語句,如圖

【鬥醫】【12】Web應用開發20天

(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);

再測試一下用例三,結果如下:

【鬥醫】【12】Web應用開發20天

八、全局資訊提示欄

   做過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的使用者,此時應該有錯誤提示,如下圖:

【鬥醫】【12】Web應用開發20天

【備注】:截止目前登入部分算是完成,裡面還涉及一些不安全的處理,一些沒有考慮到的,這裡暫時保留。接下來完成“下戰書”部分業務。