【斗医】【12】Web应用开发20天

本文涉及的产品
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
简介:
原创作品,允许转载,转载时请务必以超链接形式标明文章  原始出处 、作者信息和本声明。否则将追究法律责任。 http://qingkechina.blog.51cto.com/5552198/1390410

在上文中有意埋了几个安全彩蛋,以便后面在聊网络安全时使用。

书接前言,对上文做过实践的朋友肯定会发现:当用户注册/登录成功后页面跳转到了系统首页,但首页的导航菜单并没有显示用户名。本文重点实现这个特性,同时也谈谈系统的编码问题。


六、注册/登录成功后导航菜单显示当前用户名

   与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”;


5、在《【斗医】【11】Web应用开发50天》的注册和登录中没有把当前用户放入全局缓存,下面进行修改:

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

{

   // 1. 判断数据库中是否已存在该用户名

   UserDAO cacheUser = UserUtil.getUserDAO(userName, userAuth);

   if (cacheUser == null)

   {

       UserLoginBean loginBean = new UserLoginBean();

       loginBean.setErrorCode(FrameErrorCode.USER_NOT_EXIST_ERROR);

       loginBean.setErrorDesc(FrameUtil.getErrorDescByCode(loginBean.getErrorCode()));

       return gson.toJson(loginBean);

   }


   // 2. 存入会话对应的内存

   cacheUser.setUserAuth(null);

   FrameCache.getInstance().setUserBySession(session, cacheUser);


   // 3. 返回用户登录成功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);

}


【备注】:由于客户端并不需要用户的密码,因此没有必要把用户信息暴露,增加网络安全风险



(4)用例验证

用例1:

前提:系统中没有qingkechina用户

操作:进入系统的登录页面,注册名为qingkechina的用户

期望:注册成功且系统的菜单右上角能显示qingkechina用户名


用例2:

前提:系统中已有qingkechina用户

操作:进入系统的登录页面,以qingkechina用户登录

期望:登录成功且系统的菜单右上角能显示qingkechina用户名


用例3:

前提:系统中没有“陈许诺”用户

操作:进入系统的登录页面,注册名为“陈许诺”的用户

期望:注册成功且系统的菜单右上角能显示“陈许诺”用户名


打开Eclipse启动Tomcat成功后,在浏览器中输入http://localhost:8080/medical回车,按上面的用户验证,会发现前两个英文用例成功,但中文用例存在乱码问题,如下图:  

wKioL1M5JCPgxu-WAAAe6wn9Xh4060.png



七、系统的编码与乱码

   系统出现乱码的原因简而言之是由于:输入与输出编码不一致,比如说浏览器在Windows中文操作系统下运行,Chrome、FireFox缺省是以GBK编码显示,此时若服务端传给浏览器的编码是UTF-8,则就会形成乱码。

   各个国家的程序开发人员为了让系统支持各国的语言,都会采用UTF-8编码来解决全球化的问题。今天在网上搜索时也发现一个比较好玩的文章《大话编码》,感兴趣的可以看一看,就本系统来言存在如下边界:

wKiom1M5KiCy38apAAAbDg1Siy0785.png


解释:

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>

   <filter-name>encoder</filter-name>

   <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语句,如图

wKiom1M5Mxix3xs7AAA44DZYCx0047.png


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

}

再测试一下用例三,结果如下:

wKiom1M5PJmjwRrUAAAUdNw1KrI980.png


八、全局信息提示栏

   做过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的用户,此时应该有错误提示,如下图:

wKioL1M-JcDAUE5dAAAY4LkjVr0258.png


【备注】:截止目前登录部分算是完成,里面还涉及一些不安全的处理,一些没有考虑到的,这里暂时保留。接下来完成“下战书”部分业务。



本文出自 “青客” 博客,请务必保留此出处http://qingkechina.blog.51cto.com/5552198/1390410

相关实践学习
基于CentOS快速搭建LAMP环境
本教程介绍如何搭建LAMP环境,其中LAMP分别代表Linux、Apache、MySQL和PHP。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
19天前
|
监控 JavaScript 前端开发
《理解 WebSocket:Java Web 开发的实时通信技术》
【4月更文挑战第4天】WebSocket是Java Web实时通信的关键技术,提供双向持久连接,实现低延迟、高效率的实时交互。适用于聊天应用、在线游戏、数据监控和即时通知。开发涉及服务器端实现、客户端连接及数据协议定义,注意安全、错误处理、性能和兼容性。随着实时应用需求增加,WebSocket在Java Web开发中的地位将更加重要。
|
30天前
|
Web App开发 前端开发 开发工具
介绍Web开发的基础知识
介绍Web开发的基础知识
29 7
|
1月前
|
存储 资源调度 应用服务中间件
浅谈本地开发好的 Web 应用部署到 ABAP 应用服务器上的几种方式
浅谈本地开发好的 Web 应用部署到 ABAP 应用服务器上的几种方式
26 0
|
1月前
|
存储 前端开发 JavaScript
从前端到后端,探索现代Web开发技术
本文探索了现代Web开发技术的各个方面,包括前端和后端开发以及多种编程语言的应用。通过对JavaScript、Java、Python、C、PHP和Go等语言的介绍,深入探讨了前端和后端开发的基本原理和常用工具。同时,还涵盖了数据库技术在Web开发中的重要性和应用场景。无论你是初学者还是有经验的开发者,本文都能为你提供全面的视角和实用的知识,帮助你在Web开发领域取得更好的成果。
|
1月前
|
缓存 关系型数据库 API
后端开发:构建高效、可扩展的Web应用程序的关键
后端开发:构建高效、可扩展的Web应用程序的关键
20 0
|
5天前
|
JSON Java fastjson
Spring Boot 底层级探索系列 04 - Web 开发(2)
Spring Boot 底层级探索系列 04 - Web 开发(2)
15 0
|
5天前
|
安全 编译器 PHP
PHP 8.1版本发布:引领Web开发新潮流
PHP编程语言一直是Web开发的主力军,而最新发布的PHP 8.1版本则为开发者们带来了更多创新和便利。本文将介绍PHP 8.1版本的主要特性,包括更快的性能、新的语言功能和增强的安全性,以及如何利用这些功能来提升Web应用程序的质量和效率。
|
8天前
|
PHP
web简易开发——通过php与HTML+css+mysql实现用户的登录,注册
web简易开发——通过php与HTML+css+mysql实现用户的登录,注册
|
8天前
|
前端开发 数据挖掘 API
使用Python中的Flask框架进行Web应用开发
【4月更文挑战第15天】在Python的Web开发领域,Flask是一个备受欢迎的轻量级Web框架。它简洁、灵活且易于扩展,使得开发者能够快速地构建出高质量的Web应用。本文将深入探讨Flask框架的核心特性、使用方法以及在实际开发中的应用。
|
17天前
|
安全 前端开发 Java
Java Web开发知识点学习总结
Java Web开发涉及Java基础、Servlet、JSP、数据库操作(SQL+JDBC)、MVC设计模式、Spring框架、Hibernate ORM、Web服务(SOAP&RESTful)、安全认证(HTTP Basic/Digest/OAuth)及性能优化(缓存、异步、负载均衡)。
17 3