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方法:

方法名 作用 getByName 用户登录时,使用用户名查找用户 add 用户注册时新增用户 getById 用户注册完成后根据id查找用户 updateCookie 用户登录后要生成一个cookie,保存到数据库中
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,根据需求创建如下方法:

方法名 作用 login 用户登录 register 用户注册 getById 根据id获得用户实例

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是:

url 功能 /user/login 用户登录页面 /user/login/do 登录表单提交处理 /user/register 用户注册页面 /user/register/do 注册表单提交处理 /user/logout 用户退出登录

在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>

这样已经登录过的用户就可以自动登录了。

阅读全文
0 0
原创粉丝点击