javaWeb实战教程8-用户模块的编码实现
来源:互联网 发布:保留小数位数的函数sql 编辑:程序博客网 时间:2024/06/08 16:56
javaWeb实战教程
5.用户模块的开发
5.1 需求分析
用户可以注册、登录;登录完成的用户记录登录状态;用户下次访问自动使用cookie登录;
5.2 编码javaBean
在fy.test.model内新建User.java,按照数据库的字段编写javaBean:
public class User { private int id; private String username; private String pwd; private String cookie; private String address; private String tel; private String zip; private String realname;}
使用eclipse自动生成getter、setter。
5.3 编码dao
在fy.test.dao内新建UserDao.java,根据需求,需要getById方法、getByName方法、add方法、updateCookie方法:
public void updateCookie(int userId, String cookie){}public int add(String username, String pwd, String cookie){}public User getById(int id){}public User getByName(String username){}
在UserDao.java内新建全局变量private Connection connection;
,并生成set、get方法;connection的open、close由service管理,因为connection可以开启事务管理,通常事务管理都放在service层。
使用commons-dbutils-1.6.jar的QueryRunner来实现数据库操作:
public void updateCookie(int userId, String cookie) throws SQLException { new QueryRunner().update(connection, "update user set cookie = ? where id= ?", cookie, userId);}public int add(String username, String pwd, String cookie) throws SQLException { return new QueryRunner().insert(connection, "insert into user (username,pwd,cookie)values(?,?,?)", new ScalarHandler<Long>(), username, pwd, cookie).intValue();}public User getById(int id) throws SQLException { return new QueryRunner().query(connection, "select * from user where id=" + id, new BeanHandler<>(User.class));}public User getByName(String username) throws SQLException { return new QueryRunner().query(connection, "select * from user where username=?", new BeanHandler<>(User.class), username);}
dao里的每一个方法都抛出了SQLException,过去的实现是把这个错try{}catch{}掉,但在实际项目中,方法里的错误最好继续往上层抛,这样如果是因为数据库连接出错或sql语句出错,调用dao方法的人可以知道哪里出错,如果单纯try{}catch{}又不做任何处理,调用的人很难找到错误原因。向上层抛错的编程方法是java的一个特色。
在fy.test.test包内新建测试类UserDaoTest.java来测试一下刚刚写的dao方法是否正确:
@Beforepublic void before() { userDao=new UserDao(); userDao.setConnection(DBUtil.getConnection());}@Testpublic void test1(){ try { int id=userDao.add("xiaoming", "000", ""); User user=userDao.getById(id); System.out.println(user.getUsername()); user.setCookie(UUID.randomUUID().toString()); userDao.updateCookie(user.getId(), user.getCookie()); } catch (Exception e) { e.printStackTrace(); }}
5.4 编码service
在包fy.test.service内新建类UserService.java,根据需求创建如下方法:
public User login(String username, String pwd) {}
public User register(String username, String pwd, String repeatPwd) {}
public User getById(int id) {}
新建全局变量private UserDao userDao;
;在方法里要调用userDao前必须先初始化userDao,初始化userDao可以放在方法里、构造方法里、新建变量时,这里选择放在构造方法里:
public UserService() { userDao = new UserDao();}
5.4.1 用户登录-login
现在实现login方法,用户从浏览器提交表单,发送来用户名和密码字段,首先判断用户名、密码是否为空,这里在包fy.test.utils里新建一个工具类RegexUtil.java用于数据校验,在RegexUtil.java里写上判断字符串是否为空的静态方法:
public static boolean isEmpty(String data) { if (data == null) return true; if (data.trim().length() == 0) return true; return false;}
要判断密码是否大于6位,所以再写一个判断字符串长度的静态方法:
public static boolean checkSize(String data, int length) { if (isEmpty(data)) return false; if (data.length() <= length) return false; return true;}
在login方法里写判断语句:
if (RegexUtil.isEmpty(username)) throw new RuntimeException("请填写用户名");if (RegexUtil.checkSize(pwd, 6)) throw new RuntimeException("请填写密码");
开启数据库连接:
Connection connection = DBUtil.getConnection();userDao.setConnection(connection);
写上try{}catch{},保证在finally里关闭connection:
try {} catch (Exception e) { //将捕获到的错向上层抛,而不是默默吃掉 throw new RuntimeException(e);} finally { DBUtil.close(connection);}
使用表单发送来的username到数据库中找user,如果找不到代表用户名不存在,找到后再判断密码是否相同:
User user = userDao.getByName(username);if (user == null) throw new RuntimeException("用户名不存在");if (!user.getPwd().equals(pwd)) throw new RuntimeException("密码错误");
再给user设置一个cookie,可以使用java.util.UUID来生成随机字符串:
user.setCookie(UUID.randomUUID().toString());userDao.updateCookie(user.getId(), user.getCookie());
最后把user返回:
return user;
完整的代码:
public User login(String username, String pwd) { if (RegexUtil.isEmpty(username)) throw new RuntimeException("请填写用户名"); if (RegexUtil.checkSize(pwd, 6)) throw new RuntimeException("请填写密码"); Connection connection = DBUtil.getConnection(); userDao.setConnection(connection); try { User user = userDao.getByName(username); if (user == null) throw new RuntimeException("用户名不存在"); if (!user.getPwd().equals(pwd)) throw new RuntimeException("密码错误"); user.setCookie(UUID.randomUUID().toString()); userDao.updateCookie(user.getId(), user.getCookie()); return user; } catch (Exception e) { throw new RuntimeException(e); } finally { DBUtil.close(connection); }}
5.4.2 用户注册-register
现在写register注册方法,和刚刚的思维方法一样,先检查表单发来的字段是否完整:
if (RegexUtil.isEmpty(username)) throw new RuntimeException("请填写用户名");if (RegexUtil.checkSize(pwd, 6)) throw new RuntimeException("请填写密码");if (!pwd.equals(repeatPwd)) throw new RuntimeException("两次密码不一致");
判断用户名是否已经存在:
if (userDao.getByName(username) != null) throw new RuntimeException("用户名已存在");
插入用户:
int id = userDao.add(username, pwd, UUID.randomUUID().toString());return userDao.getById(id);
完整代码是:
public User register(String username, String pwd, String repeatPwd) { if (RegexUtil.isEmpty(username)) throw new RuntimeException("请填写用户名"); if (RegexUtil.checkSize(pwd, 6)) throw new RuntimeException("请填写密码"); if (!pwd.equals(repeatPwd)) throw new RuntimeException("两次密码不一致"); Connection connection = DBUtil.getConnection(); userDao.setConnection(connection); try { if (userDao.getByName(username) != null) throw new RuntimeException("用户名已存在"); int id = userDao.add(username, pwd, UUID.randomUUID().toString()); return userDao.getById(id); } catch (Exception e) { throw new RuntimeException(e); } finally { DBUtil.close(connection); }}
5.4.3 getById
实现getById方法:
public User getById(int id) { Connection connection = DBUtil.getConnection(); userDao.setConnection(connection); try { return userDao.getById(id); } catch (Exception e) { throw new RuntimeException(e); } finally { DBUtil.close(connection); }}
在包fy.test.test里新建一个UserServiceTest.java类来测试一下:
public class UserServiceTest { @Test public void test(){ UserService userService=new UserService();// userService.register("小明", "000000", "000000");// userService.register("小明", "000000", "000000"); userService.login("小明", "000000"); }}
5.5 编码servlet
在包fy.test.servlet里新建类UserServlet.java并继承于HttpServlet,加上注解@WebServlet(“/user/*”),这样所有访问/user/xxx的连接都会被UserServlet捕获。
在用户模块,主要有注册、登录两个页面,对应的url是:
在servlet里,可以通过request.getRequestURI()
获得用户访问的url,如”/demo/user/login”;String url = request.getRequestURI().substring(getServletContext().getContextPath().length());
可以截获除去项目名称后的url,即”/user/login”。
5.5.1 用户登录页面-login
现在实现用户登录页面;在UserServlet里新建login方法,把request和response当作参数传进来:
public void login(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {}
当用户访问/user/login时,调用login方法;在doGet里做判断:
String url = request.getRequestURI().substring( getServletContext().getContextPath().length());if (url.equals("/user/login")) { login(request, response);}
在login方法里让servlet跳转到jsp:
request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response);
在/WEB-INF/jsp/目录下新建一个login.jsp,把jsp的编码都设置成utf-8,把静态页面login.html的内容复制进来。
<form action="user/login/do" method="post"> <span class="title">用户登录</span> <input type="text" name="username"/> <input type="password" name="pwd"/> <div class="link"> <a href="user/register">快速注册</a> </div> <input type="submit" value="登录" /></form>
因为用户访问的url是/user/login,这导致jsp里的资源文件,比如css都会访问到/user/login下,所以在jsp最开头的部分加上basePath,让html的资源文件可以得到正确的路径:
<%String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+request.getContextPath()+"/";%><base href="<%=basePath %>">
这样用户登录页面就完成了,打开浏览器访问http://localhost/demo/user/login来试试看。
在查看所有页面发现,每个页面的页头、页尾都是相同的,项目中可以把相同的部分分离成head.jsp、bottom.jsp,使用jsp:include来引入jsp。并不是所有的项目都可以这样做,只是这个商城的html部分正好有这个特点。
head.jsp:
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%><%String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+request.getContextPath()+"/";%><%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %><!DOCTYPE html><html><head> <base href="<%=basePath %>"> <meta charset="utf-8" /> <meta name="renderer" content="webkit"> <title>纷云商城·JAVA实战教学</title> <link rel="stylesheet" href="css/pc.css" /></head><body> <!--网站顶部,所有页面引入--> <div class="fy-header"> <div class="top"> <div class="ctx"> <c:if test="${user!=null}"> <a href="order/my"><icon class="icon-order"></icon>我的订单</a> <a href="user/logout"><icon class="icon-logout"></icon>退出</a> <a><icon class="icon-user"></icon>170****1230</a> <a>欢迎光临纷云商城,实战JAVA的教学项目</a> </c:if> <c:if test="${user==null}"> <a href="user/register"><icon class="icon-register"></icon>注册</a> <a href="user/login"><icon class="icon-login"></icon>登录</a> </c:if> </div> </div> <div class="bottom"> <a href="" class="logo"> <img src="img/logo.png" /> </a> <img src="img/logo_ad.jpg" class="ad" /> </div> <div class="menu"> <div class="ctx"> <div class="bigmenu">全部商品分类<img src="img/down.png" /></div> <a href="" class="homemenu">首页</a> <a href="cart/list" class="cart"><img src="img/cart.png" />购物车</a> <div class="floatmenu"> <div class="padding-top"> <div class="box"> <div class="title">休闲食品</div> <div class="link"> <c:forEach items="${types1}" var="t"><a href="product/list?typeId=${t.id }">${t.name}</a></c:forEach> </div> </div> <div class="box"> <div class="title">粮油米面</div> <div class="link"> <c:forEach items="${types2}" var="t"><a href="product/list?typeId=${t.id }">${t.name}</a></c:forEach> </div> </div> <div class="box"> <div class="title">山珍特产</div> <div class="link"> <c:forEach items="${types3}" var="t"><a href="product/list?typeId=${t.id }">${t.name}</a></c:forEach> </div> </div> <div class="box"> <div class="title">中外名酒</div> <div class="link"> <c:forEach items="${types4}" var="t"><a href="product/list?typeId=${t.id }">${t.name}</a></c:forEach> </div> </div> </div> </div> </div> </div> </div>
bottom.jsp:
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> <div class="fy-bottom"> <span>版权所有©讲诉纷云科技有限责任公司 | 苏ICP备1400000号-2 | 苏ICP备1400000号</span> </div> </body></html>
这样login.jsp可以简化成:
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%><jsp:include page="/WEB-INF/jsp/head.jsp"></jsp:include> <div class="fy-login"> <div class="left"></div> <div class="right"> <form action="user/login/do" method="post"> <span class="title">用户登录</span> <input type="text" name="username"/> <input type="password" name="pwd"/> <div class="link"> <a href="user/register">快速注册</a> </div> <input type="submit" value="登录" /> </form> </div> </div> <script type="text/javascript" src="js/jquery.min.js"></script> <script type="text/javascript"> $(".fy-header .menu .ctx .bigmenu").bind("mouseover",function(){ $(".fy-header .menu .floatmenu").fadeIn() }); $(".fy-header .menu .floatmenu").bind("mouseleave",function(){ $(this).fadeOut() }); </script><jsp:include page="/WEB-INF/jsp/bottom.jsp"></jsp:include>
5.5.2 用户登录表单提交处理-loginDo
下面实现登录表单提交处理,即/user/login/do;在UserServlet里写方法loginDo:
public void loginDo(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {}
用户提交的表单是post的,需要在doPost里截获,可以在doPost里调用doGet(request, response);,将处理再转交给doGet:
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response);}
在doGet里判断url,调用loginDo方法:
if (url.equals("/user/login")) { login(request, response);} else if (url.equals("/user/login/do")) { loginDo(request, response);}
在loginDo方法里实现登录:
User user = userService.login(request.getParameter("username"), request.getParameter("pwd"));
再把查询到的user加入到cookie和session中:
CookieUtil.addCookie(response, "cookie", user.getCookie(), 7 * 24 * 60 * 60);request.getSession().setAttribute("user", user);
到这里用户就算登录完成了,可以把用户跳转到首页:
response.sendRedirect(request.getContextPath() + "/home");
用户如果登录成功,这些代码运行正常,但如果用户登录失败,就会导致一个500的错误,因为我们在service代码里往上层抛错,但servlet又没有处理这些错。可以在web
.xml里配置同一错误处理来截获这些错并提示用户;
在web.xml里配置:
<error-page> <exception-type>java.lang.Throwable</exception-type> <location>/WEB-INF/jsp/error.jsp</location></error-page>
在WEB-INF/jsp目录下新建error.jsp,在里面输出错误原因:
<%@ page language="java" contentType="text/html; charset=utf-8"pageEncoding="utf-8" isErrorPage="true"%><%=exception.getLocalizedMessage() %>
也可以写一个漂亮的错误页面来装饰。
5.5.3 用户注册页面-register
使用和登录相同的思路完成注册页面:
public void register(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.getRequestDispatcher("/WEB-INF/jsp/register.jsp").forward(request, response);}
在WEB-INF/jsp目录下新建register.jsp,把静态页面的内容复制进来:
<div class="fy-login"> <div class="left"></div> <div class="right"> <form action="user/register/do" method="post"> <span class="title">用户注册</span> <input type="text" name="username" placeholder="用户名称"/> <input type="password" name="pwd" placeholder="密码"/> <input type="password" name="repeatPwd" placeholder="再次输入密码"/> <div class="link"> <a href="user/login">快速登录</a> </div> <input type="submit" value="注册" /> </form> </div></div>
在doGet里跳转:
if (url.equals("/user/login")) { login(request, response);} else if (url.equals("/user/login/do")) { loginDo(request, response);} else if (url.equals("/user/register")) { register(request, response);}
5.5.4 用户注册表单提交处理-registerDo
下面实现注册表单提交处理registerDo,注册完成后默认认为用户登录,所以逻辑和登录相同,把查询到的user信息放到cookie和session中 :
public void registerDo(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { User user = userService.register(request.getParameter("username"), request.getParameter("pwd"), request.getParameter("repeatPwd")); CookieUtil.addCookie(response, "cookie", user.getCookie(), 7 * 24 * 60 * 60); request.getSession().setAttribute("user", user); response.sendRedirect(request.getContextPath() + "/home");}
在doGet里跳转:
if (url.equals("/user/login")) { login(request, response);} else if (url.equals("/user/login/do")) { loginDo(request, response);} else if (url.equals("/user/register")) { register(request, response);} else if (url.equals("/user/register/do")) { registerDo(request, response);}
5.5.5 用户退出登录-logout
再做一个退出登录的方法logout:
public void logout(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.getSession().removeAttribute("user"); CookieUtil.removeCookie(response, "cookie"); request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response);}
在doGet里跳转:
if (url.equals("/user/login")) { login(request, response);} else if (url.equals("/user/login/do")) { loginDo(request, response);} else if (url.equals("/user/register")) { register(request, response);} else if (url.equals("/user/register/do")) { registerDo(request, response);} else if (url.equals("/user/logout")) { logout(request, response);}
5.6 cookie自动登录的过滤器
最后我们做一个使用cookie自动登录的过滤器;在包fy.test.filter里新建过滤器CookieAutoLoginFilter.java,创建全局变量private UserService userService;
并在init方法里初始化userService = new UserService();
。
思路是:从cookie中得到名称为“cookie”的变量,从数据库里查找用户,如果找到了,就把用户信息加入到session中。
所以先在UserDao.java里加上一个方法getByCookie
:
public User getByCookie(String cookie) throws SQLException { return new QueryRunner().query(connection, "select * from user where cookie=?", new BeanHandler<>(User.class), cookie);}
再在UserService.java里加上方法public User getUserByCookie(String cookie)
:
public User getUserByCookie(String cookie){ Connection connection = DBUtil.getConnection(); userDao.setConnection(connection); try { return userDao.getByCookie(cookie); } catch (Exception e) { throw new RuntimeException(e); } finally { DBUtil.close(connection); }}
在CookieAutoLoginFilter.java里添加代码:
@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chian) throws IOException, ServletException { if (CookieUtil.getCookie((HttpServletRequest) request, "cookie") != null) { User user = userService.getUserByCookie(CookieUtil.getCookie( (HttpServletRequest) request, "cookie")); if (user != null) { ((HttpServletRequest) request).getSession().setAttribute("user", user); } } chian.doFilter(request, response);}
写好了filter还需要配置到web.xml才能运行,在web
.xml里配置:
<filter> <filter-name>CookieAutoLoginFilter</filter-name> <filter-class>fy.test.filter.CookieAutoLoginFilter</filter-class></filter><filter-mapping> <filter-name>CookieAutoLoginFilter</filter-name> <url-pattern>/*</url-pattern></filter-mapping>
这样已经登录过的用户就可以自动登录了。
- javaWeb实战教程8-用户模块的编码实现
- javaWeb实战教程9-商品分类模块的编码实现
- javaWeb实战教程10-商品模块的编码实现
- Android实战简易教程-第二十三枪(基于Baas的用户注册和登录模块实现!)
- javaweb用户的自动登录模块的实现
- JavaWeb实现用户登录的拦截
- javaWeb实战教程1-servlet
- Android实战简易教程-第二十四枪(基于Baas的用户表查询功能实现!)
- Android实战简易教程<二十四>(基于Baas的用户表查询功能实现!)
- JavaWeb的编码问题
- javaWeb实战教程0-环境配置
- javaWeb实战教程3-过滤器filter
- javaWeb实战教程5-fileupload文件上传
- javaWeb实战教程6-jsp和jstl
- javaWeb实战教程7-搭建项目框架
- 用户登录模块的实现过程
- JavaWeb的html页面编码
- 【项目实战】---用户模块,验证码程序
- XCode编译报错 Command /usr/bin/codesign failed with exit code 1
- hdu 2616 Kill the monster(dfs)
- javaWeb实战教程7-搭建项目框架
- 从源码剖析PopupWindow 兼容Android 6.0以上版本点击外部不消失
- android隐藏关闭软键盘
- javaWeb实战教程8-用户模块的编码实现
- 安卓进入页面避免软键盘弹出的解决办法
- 先搞一波kotlin,看它怎么说
- MVC模型简单解读
- javaWeb实战教程9-商品分类模块的编码实现
- javaWeb实战教程10-商品模块的编码实现
- 使用redis优化性能之注册和登录
- Kali的学习笔记
- android不用第三方如何打开word,ppt?