评论

收藏

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

数据库 数据库 发布于:2021-07-04 09:25 | 阅读数:311 | 评论:0

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

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

  

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

    与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回车,按上面的用户验证,会发现前两个英文用例成功,但中文用例存在乱码问题,如下图:  
DSC0000.png

  

  

  七、系统的编码与乱码
  
      系统出现乱码的原因简而言之是由于:输入与输出编码不一致,比如说浏览器在Windows中文操作系统下运行,Chrome、FireFox缺省是以GBK编码显示,此时若服务端传给浏览器的编码是UTF-8,则就会形成乱码。
      各个国家的程序开发人员为了让系统支持各国的语言,都会采用UTF-8编码来解决全球化的问题。今天在网上搜索时也发现一个比较好玩的文章《大话编码》,感兴趣的可以看一看,就本系统来言存在如下边界:
DSC0001.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语句,如图
   DSC0002.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);
  }
  再测试一下用例三,结果如下:
DSC0003.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的用户,此时应该有错误提示,如下图:
   DSC0004.png
  
  【备注】:截止目前登录部分算是完成,里面还涉及一些不安全的处理,一些没有考虑到的,这里暂时保留。接下来完成“下战书”部分业务。

  
  

  

  
关注下面的标签,发现更多相似文章