本文在上文的基础上完成用户登录验证功能。

四、获取数据请求业务处理封装

1、配置数据读取方式,它的作用是使用FrameDataGainer响应以.data结尾的请求,并把处理后的数据返回给客户端。打开D:\medical\war\WEB-INF\web.xml文件,填充如下内容:

<servlet><servlet-name>data</servlet-name><servlet-class>com.medical.frame.FrameDataGainer</servlet-class><load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping><servlet-name>data</servlet-name><url-pattern>*.data</url-pattern>
</servlet-mapping>

2、FrameDataGainer的作用说明:

当浏览器通过uri下发XXX.data请求时,web容器tomcat将该请求交由FrameDataGainer处理,它通过getDataName()方法获取请求名XXX后,然后再从全局缓存FrameCache中根据XXX获取定义业务类名称,接着根据定义业务类名称把其反射出FrameDefaultAction对象,最后调用FrameDefaultAction对象的execute()进行具体的业务处理,并返回给客户端。这个过程与请求跳转封装类似,接下来跟着思维一步步实现这个过程:

3、定义FrameDataGainer类,让其继承HttpServlet对象,同时在init()方法中初始化配置类

public class FrameDataGainer extends HttpServlet
{/*** 定义日志对象*/private static final Logger logger = LoggerFactory.getLogger(FrameDataGainer.class);@Overridepublic void init()throws ServletException{ServletContext context = getServletContext();// 加载业务配置文件try{FrameConfigUtil.loadDataBusiness(context);}catch (FrameException e){throw new ServletException("[FrameDataGainer] init error.", e);}}
}

4、在FrameConfigUtil中定义loadDataBusiness()方法,通过该方法把xxx-data.xml等文件加载进入内存

/*** 加载D:\medical\war\WEB-INF\config下的所有数据业务配置文件*/public static void loadDataBusiness(ServletContext context) throws FrameException{findDataFile(context, "/WEB-INF/config");parseDataBusiness(context);}

其中findDataFile()用于查找D:\medical\war\WEB-INF\config下所有的xxx-data.xml文件;parseDataBusiness()用于把xxx-data.xml中的配置加载到内存中。这里不再列出,感兴趣的话可以附件中查看源码。

5、FrameDataGainer.getDataName()方法用于把请求的动作名称从HTTP中解析出来:

public class FrameDataGainer extends HttpServlet
{@Overridepublic void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException{String dataName = getDataName(request);// 省略}
}

6、当dataName为空时通过FrameDataGainer.getErrorResult()返回异常JSON对象,所以这里引入gson.jar

(1)进入https://code.google.com/p/google-gson/downloads/detail?name=google-gson-2.2.4-release.zip下载

(2)解压后把gson-2.2.4.jar复制到应用运行环境下的WEB-INF\lib下。我的环境位置为D:\medical\war\WEB-INF\lib

(3)在Eclipse工程中引入gson-2.2.4.jar

(4)定义com.medical.frame.bean.FrameResultBean.java对象

public class FrameResultBean
{/*** 错误码*/private int errorCode = 0;/*** 错误描述*/private String errorDesc = null;public int getErrorCode(){return errorCode;}public void setErrorCode(int errorCode){this.errorCode = errorCode;}public String getErrorDesc(){return errorDesc;}public void setErrorDesc(String errorDesc){this.errorDesc = errorDesc;}
}

(5)定义FrameDataGainer.getErrorResult()方法

/*** 返回请求异常Json对象*/
private String getErrorResult()
{FrameResultBean resultBean = new FrameResultBean();resultBean.setErrorCode(FrameErrorCode.REQUEST_BUS_NOT_EXIST);String errorCode = String.valueOf(FrameErrorCode.REQUEST_BUS_NOT_EXIST);String errorDesc = FrameCache.getInstance().getResourceValue(errorCode);resultBean.setErrorDesc(errorDesc);return gson.toJson(resultBean);
}

(6)请求数据名称没有配置返回异常JSON对象

public class FrameDataGainer extends HttpServlet
{@Overridepublic void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException{String dataName = getDataName(request);if (dataName == null){response.getWriter().write(getErrorResult());return;}}
}

7、当请求数据名称存在时,根据数据名称从缓存中读取业务对象

public class FrameDataGainer extends HttpServlet
{@Overridepublic void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException{// 省略FrameDataBusiness business = FrameCache.getInstance().getDataBusinessMap(dataName);if (business == null){response.getWriter().write(getErrorResult());return;}}
}

8、从业务配置中反射出数据请求动作处理类对象,并把相应的处理结果写入Response对象中

public class FrameDataGainer extends HttpServlet
{@Overridepublic void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException{// 省略FrameDefaultAction action = FrameActionFactory.getInstance().implement(business.getBusinessClass());action.setRequest(request);action.setResponse(response);action.setSession(request.getSession());String resultData = action.execute();response.getWriter().write(resultData == null ? getErrorResult() : resultData);}
}

由于描述可能不清楚,可以参见流程,其处理过程与页面跳转的封装类似:

五、开发注册和登录功能

1、为让用户输入的密码不以明文显示,把D:\medical\war\module\login\login.html中的密码输入组件更改为password,如下:

<inputtype="password"class="login_user_input"placeholder="请输入密码"id="login_dynamic_user_pass"/>

2、修改login.html文件中注册、登录部分代码如下:

<div class="login_button_wrapper"><a class="login_confirm_button" href="javascript:systemUserLogin()">登录</a><a class="login_regist_button"  href="javascript:systemUserRegist()">注册</a>
</div>

3、在D:\medical\war\js\login\login.js文件中完成按钮响应动作:

(function( window){/*** 用户登录方法*/function systemUserLogin(isRegist){var userName = $("#login_dynamic_user_name").val();var userAuth = $("#login_dynamic_user_pass").val();//1. 用户名或密码为空if(!userName || !userAuth){return;}//2. 用户名或密码长度不能超过20if(userName.length > 20 || userAuth.length > 20){return;}//3. 下发请求var data = {"isRegist": isRegist, "userName": userName, "userAuth": userAuth};asyncRequest("login.data", data, function(result){// TODOalert(1);});}/*** 对外公开方法*/window.systemUserLogin = systemUserLogin;window.systemUserRegist = systemUserRegist;
})( window );

4、由于在第3步中调用了common.js中封装的asyncRequest()方法,这里把代码粘贴如下:

/*** 下发AJAX异步请求*/
function asyncRequest(action, param, callback)
{$.ajax({type: "GET",dataType: "JSON",url: action,data: param,success: function(result){callback(result);}});
}

5、定义数据业务处理配置文件login-data.xml,并把其放置到D:\medical\war\WEB-INF\config\login下

<?xml version="1.0" encoding="UTF-8"?>
<business-config><business name="login" business-class="com.medical.server.data.UserLoginDataAction" />
</business-config>

6、通过第四步的封装和login-data.xml的定义,当在客户端输入用户名和密码后,点击登录或注册按钮,就可以调用到UserLoginDataAction业务处理类,其定义如下:

/*** 斗医系统用户登录逻辑处理类** @author qingkechina*/
public class UserLoginDataAction extends FrameDefaultAction
{/*** 全局Gson对象*/private final static Gson gson = new Gson();/*** 缺省响应动作*/public String execute()throws FrameException{// 1.从客户端获取用户名和密码String userName = getParameter("userName");String userAuth = getParameter("userAuth");if (FrameUtil.isEmpty(userName) || FrameUtil.isEmpty(userAuth)){UserLoginBean loginBean = new UserLoginBean();loginBean.setErrorCode(FrameErrorCode.USER_LOGIN_ERROR);loginBean.setErrorDesc(FrameUtil.getErrorDescByCode(loginBean.getErrorCode()));return gson.toJson(loginBean);}// 2.用户名和密码的长度不能超过数据库字段的长度if (userName.length() > 20 || userAuth.length() > 64){UserLoginBean loginBean = new UserLoginBean();loginBean.setErrorCode(FrameErrorCode.USER_LOGIN_ERROR);loginBean.setErrorDesc(FrameUtil.getErrorDescByCode(loginBean.getErrorCode()));return gson.toJson(loginBean);}// 3.用户名和密码一般都是汉字、字母、数字和特殊字符// 这里留一个雷区,后面再讲解SQL注入时再进行完善// 4.判断是注册还是登录String isRegistUser = getParameter("isRegist");if ("TRUE".equalsIgnoreCase(isRegistUser)){return doRegistAction(userName, userAuth);}else{return doLoginAction(userName, userAuth);}}// 略去doRegistAction()和doLoginAction()方法
}

【备注】:这里暂时不考虑用户信息安全,自棱镜门之后网络安全在国内得到越来越多的重视。用户注册登录的用户信息在客户端输入、传输、服务端验证、加解密等角度都需要考虑,这里暂时留一个彩蛋,以备后续安全专题时使用。

7、用户注册的处理逻辑:

先拿输入的用户名去数据库查找

(1)若查到说明存在重名的用户,请注册用户再选一个名称

(2)若查不到则把用户数据插入数据库,并返回注册成功信息

/*** 用户注册处理方法*/
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);UserLoginBean loginBean = new UserLoginBean();loginBean.setErrorCode(FrameErrorCode.USER_REGIST_SUCCESS);loginBean.setErrorDesc(FrameUtil.getErrorDescByCode(loginBean.getErrorCode()));loginBean.setForwardPath("main.act");return gson.toJson(loginBean);
}

【备注】:

(1)、在《【斗医】【9】Web应用开发50天》文中,UserDao对象的数据映射文件主键定义了如下的样式:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC"-//Hibernate/Hibernate Mapping DTD 3.0//EN""http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"><hibernate-mapping package="com.medical.server.dao"><class name="UserDAO" table="USERTABLE"><id name="userId" column="userId" type="string"><generator class="uuid.hex" /></id><property name="userAuth" column="userAuth" /><property name="userSign" column="userSign" /><property name="attention" column="attention" /></class>
</hibernate-mapping>

请留意<generator class="uuid.hex" />,它表明在向数据库插入记录时,Hibernate会调用uuid.hex的方式生成一个32位的主键;另一方面在创建数据表usertable时,指定userId字段的长度为20,所以入库时会抛字错内容这长的异常提示,这里把映射文件中的<generator class="uuid.hex" />删除掉,具体见附件。

(2)、再说一下Hibernate的保存问题,我在UserUtil.java中定义了保存UserDAO的一个方法,如下:

/*** 把用户入库*/
public static void insertUser(String userName, String userAuth)
{UserDAO userDao = new UserDAO();userDao.setUserId(userName);userDao.setUserAuth(userAuth);Session session = FrameDBUtil.openSession();Transaction transaction = session.beginTransaction();session.save(userDao);transaction.commit();FrameDBUtil.closeSession();
}

网上有网友讨论保存不成功,因为他的写法如下:

Session session = FrameDBUtil.openSession();

session.save(userDao);

FrameDBUtil.closeSession();

因为这样的做法只是把userDao对象放入Hibernate缓存,而没有真正的入库。

8、用户登录的处理逻辑。先拿输入的用户名和密码去数据库查找,若查找到说明用户合法,否则用户非法

/*** 用户登录处理方法*/
private String doLoginAction(String userName, String userAuth)
{// 1. 判断数据库中是否已存在该用户名boolean isValideUser = UserUtil.isValideUser(userName, userAuth);if (isValideUser == false){UserLoginBean loginBean = new UserLoginBean();loginBean.setErrorCode(FrameErrorCode.USER_NOT_EXIST_ERROR);loginBean.setErrorDesc(FrameUtil.getErrorDescByCode(loginBean.getErrorCode()));return gson.toJson(loginBean);}// 2.返回用户登录成功JSON对象UserLoginBean loginBean = new UserLoginBean();loginBean.setErrorCode(FrameErrorCode.USER_LOGIN_SUCCESS);loginBean.setErrorDesc(FrameUtil.getErrorDescByCode(loginBean.getErrorCode()));loginBean.setForwardPath("main.act");return gson.toJson(loginBean);
}

【备注】:

由于涉及到UserUtil.java的相关方法,没有在文中专门指出,这里列出相关代码

/*** 通过用户名称查找用户DAO对象*/
public static UserDAO getUserByName(String userName)
{Session session = FrameDBUtil.openSession();Criteria criteria = session.createCriteria(UserDAO.class);criteria.add(Restrictions.eq("userId", userName));List<?> userList = criteria.list();FrameDBUtil.closeSession();if (FrameUtil.isEmpty(userList)){return null;}UserDAO userDao = (UserDAO)userList.get(0);return userDao;
}
/*** 通过用户名和密码验证用户是否合法*/
public static boolean isValideUser(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 false;}return true;
}

8、客户端JS收到服务端返回的结果后进行相应的逻辑处理

(1)若登录或注册失败,用户一定要看到失败信息,所以需要在war\etc\local\zh\resource.properties中定义相关的错误资源

500=用户名或密码错误
505=系统中已有同名用户
515=系统中不存在该用户

(2)完善war\js\login\login.js的异步请求,把从服务端收到的结果进行处理。当用户登录或注册成功后进入系统首页,否则弹框提示用户

/*** 用户登录方法*/
function systemUserLogin(isRegist)
{var userName = $("#login_dynamic_user_name").val();var userAuth = $("#login_dynamic_user_pass").val();//1. 用户名或密码为空if(!userName || !userAuth){return;}//2. 用户名或密码长度不能超过20if(userName.length > 20 || userAuth.length > 20){return;}//3. 下发请求var data = {"isRegist": isRegist, "userName": userName, "userAuth": userAuth};asyncRequest("login.data", data, function(result){var resultJson = eval(result);if(resultJson.errorCode != 510){alert(resultJson.errorDesc);return;}// 跳转到相应页面top.location = resultJson.forwardPath;});
}

到此为至,用户的注册和登录已处理完毕,在Eclipse中运行Tomcat,把http://localhost:8080/medical敲入浏览器中后,点击登录出现如下界面:

在该输入框中注册用户后页面可以正确地跳转到系统的首页,但细心的用户会发现系统首页的用户名那儿还是显示“登录”,而不是真正的用户名,由于51cto的这个编辑组件写的内容过长时,系统运行非常迟顿,所以在下一文处理这个问题。

【备注】:

(1)用户若在使用Hibernate过程中出现如下错误,说明是缺少antlr-3.1.2-1.jar.zip包

java.lang.ClassNotFoundException: antlr.SemanticException

at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1462)

(2)截止目前系统所使用的jar包有如下,由于附件大小有限制,所以没有把jar悉数上传,若有需要的用户可以单独找我联系。

转载于:https://blog.51cto.com/qingkechina/1386941

【斗医】【11】Web应用开发20天相关推荐

  1. 【斗医】【18】Web应用开发20天

    原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://qingkechina.blog.51cto.com/5552198/154492 ...

  2. 【斗医】【13】Web应用开发20天

    在上文中完成用户注册/登录后显示用户名的功能,同时也谈了谈系统编码问题,本文重点聊聊"下战书"功能,里面涉及第三方在线HTML编辑器的问题. 一.Navicat for MySQL ...

  3. 【斗医】【3】Web应用开发20天

    在上面提及异常的中英文从资源文件中读取,若读取失败需要日志记录,所以使用网上正在闹腾的Logback来记录.关于Logback与Log4j这里不做评判和说明,所有疑问可以请教谷歌. 一.Logback ...

  4. 【斗医】【10】Web应用开发20天

    本文在前面封装Hibernate工具的基础上重点完成用户登录功能,目前系统页面展示的名称是在HTML中写死的,所以下面要做的事:若用户已登录则显示用户名:若用户未登录则点击进入登录页面. 一.修改登录 ...

  5. 推荐20个很有帮助的 Web 前端开发教程

    在平常的搜索中,我碰到过很多有趣的信息,应用程序和文档,我把它们整理在下面这个列表.这是收藏的遇到的有用内容的一个伟大的方式,可以在你需要的时候方便查阅.相信你会在这个列表中发现对你很有用的资料. 您 ...

  6. 给 Web 前端开发人员推荐20款 CSS 编辑器

    CSS 和 HTML,JavaScript 是网页的基础,作为前端开发人员,对这三者都要很熟悉.特别是未来流行全栈开发的时代,每项技术都是你知识结构中必要的一个节点. 在开发中,选择好工具是非常重要的 ...

  7. 初学Web前端开发,你需要掌握的11项技能

    你也许会觉得Web前端开发是一个很简单的工作,但当你深入其中时,一定会发现好像Web前端开发不是那么简单,光网站性能优化.响应式.框架就让你焦头烂额.确实,做前端开发就是先易后难,想成为一个优秀的We ...

  8. 转发:11个web前端开发实战项目案例+源码!拿走就是了

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/weixin_43793782/arti ...

  9. python开发web项目_Django2:Web项目开发入门笔记(20)

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 这一篇教程,我们一起来了解如何在CentOS系统中将Django2的Web项目部署到Nginx服务器. CentOS系统虽然和Ubuntu系统都是Linu ...

最新文章

  1. 你离开学只差这个视频:李宏毅机器学习2020版正式开放上线
  2. Hashtable类
  3. [设计模式随意链接]——命令模式
  4. 2016-6-28 工作总结
  5. 每个Java开发人员都必须避免的9个安全错误
  6. 每个计算机系的学生都学离散数学,离散数学一阶逻辑精要.ppt
  7. 设计模式--请参阅本质
  8. opencv用python进行物体识别_教你用Python+opencv来识别物体的轮廓并标识显示
  9. 数据库系统工程师(软考)
  10. html悬浮客服代码,js QQ客服悬浮效果实现代码
  11. D. Graph and Queries (并查集+线段树)
  12. Python 豆瓣TOP250 电影爬取
  13. 我的U盘终于中招啦:U盘快捷方式病毒
  14. 超市管理系统-2(数据库代码以及数据库逻辑)
  15. [转]结合php5与zend_search_lucene来创建一个全文搜索引擎
  16. 基于React的富文本编辑器——Braft Editor使用
  17. keil c51如何添加STC芯片的固件库文件
  18. 别把“IT信息化”不当“超级工程”
  19. codeforce 141A
  20. 《MySQL高级篇》数据库建模工具---PowderDesigner的使用教程

热门文章

  1. 纹理特征:灰度共生矩阵
  2. 2016 Top 10 Android Library
  3. Mysql多表查询笔记
  4. PHP 简单计算器代码实现
  5. 从头开始搭建一个dubbo+zookeeper平台
  6. Yii Framework2.0开发教程(5)数据库mysql性能
  7. Android——Fragment介绍
  8. 模拟器显示屏上方的信号和电池图标不显示设置
  9. NameError: name 'url' is not defined django
  10. 大话数据结构03:静态链表