草庐IT

day08-功能实现07

liyuelian 2023-04-17 原文

家居网购项目实现07

以下皆为部分代码,详见 https://github.com/liyuelian/furniture_mall.git

16.功能15-会员显示登录名

16.1需求分析/图解

  1. 会员登录成功
  2. login_ok.jsp显示欢迎信息
  3. 返回首页,显示登录相关菜单,如果有登录过,显示如上信息
  4. 如果用户没有登录过,网站首页就显示 登录/注册 超链接

16.2思路分析

16.3代码实现

dao和service层不变,在之前实现的MemberServlet中,修改login方法:

如果用户登录成功,创建session,在session中设置member信息,请求转发到登录成功页面login_ok.jsp,在该页面中显示用户信息。

MemberServlet.login():

/**
 * 处理会员登录业务
 *
 * @param request
 * @param response
 * @throws ServletException
 * @throws IOException
 */
public void login(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    //1.接收用户名和密码
    //如果前端输入的是null,后台接收的数据为空串""
    String username = request.getParameter("username");
    String password = request.getParameter("password");

    //构建一个member对象
    Member member = new Member(null, username, password, null);

    //2.调用MemberServiceImpl的login方法
    if (memberService.login(member) == null) {//数据库中没有该用户,返回登录页面
        //登录失败,将错误信息和登录会员名放入request域中
        request.setAttribute("errInfo", "登录失败,用户名或者密码错误");
        request.setAttribute("username", username);
        //注意路径
        request.getRequestDispatcher("/views/member/login.jsp")
                .forward(request, response);
    } else {//登录成功
        //创建session,将jsessionid作为cookie返回给浏览器
        HttpSession session = request.getSession();
        session.setMaxInactiveInterval(1800);//设置生命周期为30分钟
        //将得到的member对象放入session域对象中
        session.setAttribute("member", member);
        //跳转到登录成功页面
        request.getRequestDispatcher("/views/member/login_ok.jsp")
                .forward(request, response);
    }
}

在前端jsp页面中,如果没有在session域对象中获取到member对象,就显示登录注册链接,否则显示登录用户信息(这里先不实现过滤)

views/customer/index.jsp

<!-- Single Wedge Start -->
<%--根据用户登录的状态显示不同菜单--%>
<%--如果未登录--%>
<c:if test="${empty sessionScope.member}">
    <div class="header-bottom-set dropdown">
        <a href="views/member/login.jsp">登录|注册</a>
    </div>
</c:if>
<%--如果已登录--%>
<c:if test="${not empty sessionScope.member}">
    <div class="header-bottom-set dropdown">
        <a> 欢迎:${sessionScope.member.username}</a>
    </div>
    <div class="header-bottom-set dropdown">
        <a href="#">订单管理</a>
    </div>
    <div class="header-bottom-set dropdown">
        <a href="#">安全退出</a>
    </div>
</c:if>
<!-- Single Wedge End -->

login_ok.jsp同理

16.4完成测试

未登录访问首页:

登录后访问首页:

17.功能16-注销登录

17.1需求分析/图解

  1. 顾客登陆成功后
  2. login_ok.jsp中点击安全退出,注销登录
  3. 返回首index.jsp,也可以点击安全退出,注销登录

17.2思路分析

17.3代码实现

dao,service层不变

在MemberServlet中实现logout方法

/**
 * 处理用户注销登录的请求
 *
 * @param req
 * @param resp
 * @throws ServletException
 * @throws IOException
 */
protected void logout(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    //销毁当前用户的session
    req.getSession().invalidate();
    //重定向到index.jsp,目的是刷新首页
    //req.getContextPath()=>/项目名  -默认访问index.jsp
    resp.sendRedirect(req.getContextPath());
}

注意修改安全退出超链接的参数action=logout

17.4完成测试

18.功能17-注册验证码

18.1需求分析/图解

表单重复提交情况:

  1. 提交完表单,服务器使用请求转发进行页面跳转。用户刷新(F5),会发起最后一次的请求,造成表单重复提交问题。解决方案是使用重定向
  2. 用户正常提交,由于网络延迟等原因,未收到服务器响应,如果这时用户重复点击提交,也会造成表单重复提交问题。解决方案:使用验证码
  3. 用户正常提交,服务器没有延迟,但是提交完之后,用户回退浏览器重新提交,也会造成表单重复提交。解决方案:验证码
  4. 恶意注册,使用可以批量发送http的工具,比如Postman,Jemeter等,解决方案:仍是使用验证码防护

18.2思路分析

18.3代码实现

dao层和service层不变

  1. 引入kaptcha-2.3.2.jar,在web.xml中配置KaptchaServlet

    (KaptchaServlet已经在jar包中写好了,只需要配置即可)

    KaptchaServlet源码:

    <servlet>
        <servlet-name>KaptchaServlet</servlet-name>
        <servlet-class>com.google.code.kaptcha.servlet.KaptchaServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>KaptchaServlet</servlet-name>
        <url-pattern>/kaptchaServlet</url-pattern>
    </servlet-mapping>
    
  2. 修改login.jsp的注册表单,生成正确的验证码图片,点击图片可以生成新的验证码图片

    <!--注册表单-->
    <form action="memberServlet" method="post">
    <input type="hidden" name="action" value="register"/>
    <input type="text" id="username" name="username"
           value="${requestScope.username}" placeholder="Username"/>
    <input type="password" id="password" name="password" placeholder="输入密码"/>
    <input type="password" id="repwd" name="repassword" placeholder="确认密码"/>
    <input name="email" id="email" placeholder="电子邮件" value="${requestScope.email}"
           type="email"/>
    <input type="text" name="code" style="width: 50%" id="code" placeholder="验证码"/>   
        <img id="codeImg" alt="" src="kaptchaServlet" style="width: 120px;height: 50px">
    <div class="button-box">
        <button type="submit" id="sub-btn"><span>会员注册</span></button>
    </div>
    </form>
    

    在function中给验证码图片,绑定单击事件

    //给验证码图片绑定单击事件,可以获取新的验证码
    $("#codeImg").click(function () {
        //在url没有变化的时候,图片不会发出新的请求(因为图片已经被缓存了)
        //为了防止不刷新,可以携带一个变化的参数
        this.src="<%=request.getContextPath()%>/kaptchaServlet?d="+new Date();
    })
    
  3. 前端校验代码: login.jsp增加注册表单的校验-验证码提交时不为空

    //点击绑定事件
    $("#sub-btn").click(function () {
        //编写正则表达式进行验证
        //1. 验证用户名
       		 ...
        
        //2. 验证密码
       		...
    
        //3.两次密码要相同
        	...
    
        //4. 邮箱格式验证
        	...
    
        //5.验证码不能为空
        var codeText=$("#code").val();
        //去掉验证码前后空格
        var codeText = $.trim(codeText);
        if (codeText==null||codeText==""){
            //提示
            $("span.errorMsg").text("验证码不能为空");
            return false;
        }
        
        //如果上面的信息格式都正确,就可以提交表单信息了
        $("span.errorMsg").text("验证通过...");
        return true;
    })
    
  4. 后端校验代码-修改MemberServlet.register(),增加在服务器端的校验代码

    /**
     * 处理会员注册业务
     *
     * @param request
     * @param response
     * @throws ServletException
     * @throws IOException
     */
    public void register(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //接收用户注册信息--参数名要以前端页面的变量名为准
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        String email = request.getParameter("email");
        //获取用户提交的验证码
        String code = request.getParameter("code");
        //从session中获取正确的验证码文本
        String token = (String) request.getSession().getAttribute(KAPTCHA_SESSION_KEY);
        //立即删除session的验证码,防止该验证码被重复使用
        request.getSession().removeAttribute(KAPTCHA_SESSION_KEY);
        //将提交的验证码和正确的验证码文本进行对比
        //如果token不为空,并且和用户提交的验证码一致,就继续执行业务
        if (token != null && token.equalsIgnoreCase(code)) {
    
            //如果返回false,说明该用户信息可以注册
            if (!memberService.isExistsUsername(username)) {
                //构建一个member对象
                Member member = new Member(null, username, password, email);
                if (memberService.registerMember(member)) {
                    //如果注册成功,请求转发到register_ok.html
                    request.getRequestDispatcher("/views/member/register_ok.html")
                            .forward(request, response);
                } else {
                    //注册失败,请求转发到register_fail.html
                    request.getRequestDispatcher("/views/member/register_fail.html")
                            .forward(request, response);
                }
            } else {//否则不能进行注册
                //请求转发到login.html
                //后面可以加入提示信息
                request.getRequestDispatcher("/views/member/login.jsp")
                        .forward(request, response);
            }
        } else {//验证码不正确
            request.setAttribute("errInfo", "验证码不正确");
            //回显注册信息
            request.setAttribute("username",username);
            request.setAttribute("email",email);
            request.getRequestDispatcher("/views/member/login.jsp")
                    .forward(request, response);
        }
    }
    
  5. 修改了一些前端代码

18.4完成测试

提交空验证码,前端返回提示信息

提交错误验证码,后端返回提示信息,并回显注册用户名和邮件

输入合法数据,注册成功

有关day08-功能实现07的更多相关文章

  1. ruby - 如何根据特征实现 FactoryGirl 的条件行为 - 2

    我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden

  2. ruby-on-rails - Cucumber 是否只是 rspec 的包装器以帮助将测试组织成功能? - 2

    只是想确保我理解了事情。据我目前收集到的信息,Cucumber只是一个“包装器”,或者是一种通过将事物分类为功能和步骤来组织测试的好方法,其中实际的单元测试处于步骤阶段。它允许您根据事物的工作方式组织您的测试。对吗? 最佳答案 有点。它是一种组织测试的方式,但不仅如此。它的行为就像最初的Rails集成测试一样,但更易于使用。这里最大的好处是您的session在整个Scenario中保持透明。关于Cucumber的另一件事是您(应该)从使用您的代码的浏览器或客户端的角度进行测试。如果您愿意,您可以使用步骤来构建对象和设置状态,但通常您

  3. 华为OD机试用Python实现 -【明明的随机数】 2023Q1A - 2

    华为OD机试题本篇题目:明明的随机数题目输入描述输出描述:示例1输入输出说明代码编写思路最近更新的博客华为od2023|什么是华为od,od薪资待遇,od机试题清单华为OD机试真题大全,用Python解华为机试题|机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为o

  4. 基于C#实现简易绘图工具【100010177】 - 2

    C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

  5. MIMO-OFDM无线通信技术及MATLAB实现(1)无线信道:传播和衰落 - 2

     MIMO技术的优缺点优点通过下面三个增益来总体概括:阵列增益。阵列增益是指由于接收机通过对接收信号的相干合并而活得的平均SNR的提高。在发射机不知道信道信息的情况下,MIMO系统可以获得的阵列增益与接收天线数成正比复用增益。在采用空间复用方案的MIMO系统中,可以获得复用增益,即信道容量成倍增加。信道容量的增加与min(Nt,Nr)成正比分集增益。在采用空间分集方案的MIMO系统中,可以获得分集增益,即可靠性性能的改善。分集增益用独立衰落支路数来描述,即分集指数。在使用了空时编码的MIMO系统中,由于接收天线或发射天线之间的间距较远,可认为它们各自的大尺度衰落是相互独立的,因此分布式MIMO

  6. 【Java入门】使用Java实现文件夹的遍历 - 2

    遍历文件夹我们通常是使用递归进行操作,这种方式比较简单,也比较容易理解。本文为大家介绍另一种不使用递归的方式,由于没有使用递归,只用到了循环和集合,所以效率更高一些!一、使用递归遍历文件夹整体思路1、使用File封装初始目录,2、打印这个目录3、获取这个目录下所有的子文件和子目录的数组。4、遍历这个数组,取出每个File对象4-1、如果File是否是一个文件,打印4-2、否则就是一个目录,递归调用代码实现publicclassSearchFile{publicstaticvoidmain(String[]args){//初始目录Filedir=newFile("d:/Dev");Datebeg

  7. ruby - Arrays Sets 和 SortedSets 在 Ruby 中是如何实现的 - 2

    通常,数组被实现为内存块,集合被实现为HashMap,有序集合被实现为跳跃列表。在Ruby中也是如此吗?我正在尝试从性能和内存占用方面评估Ruby中不同容器的使用情况 最佳答案 数组是Ruby核心库的一部分。每个Ruby实现都有自己的数组实现。Ruby语言规范只规定了Ruby数组的行为,并没有规定任何特定的实现策略。它甚至没有指定任何会强制或至少建议特定实现策略的性能约束。然而,大多数Rubyist对数组的性能特征有一些期望,这会迫使不符合它们的实现变得默默无闻,因为实际上没有人会使用它:插入、前置或追加以及删除元素的最坏情况步骤复

  8. ruby - "public/protected/private"方法是如何实现的,我该如何模拟它? - 2

    在ruby中,你可以这样做:classThingpublicdeff1puts"f1"endprivatedeff2puts"f2"endpublicdeff3puts"f3"endprivatedeff4puts"f4"endend现在f1和f3是公共(public)的,f2和f4是私有(private)的。内部发生了什么,允许您调用一个类方法,然后更改方法定义?我怎样才能实现相同的功能(表面上是创建我自己的java之类的注释)例如...classThingfundeff1puts"hey"endnotfundeff2puts"hey"endendfun和notfun将更改以下函数定

  9. ruby-on-rails - rails 功能测试 - 2

    在Rails自动生成的功能测试(test/functional/products_controller_test.rb)中,我看到以下代码:classProductsControllerTest我的问题是:方法调用products()在哪里/如何定义?products(:one)到底是什么意思?看代码,大概意思是“创建一个产品”,但是它是如何工作的呢?注意我是Ruby/Rails的新手,如果这些是微不足道的问题,我深表歉意。 最佳答案 如果您查看test/fixtures文件夹,您会看到一个products.yml文件。这是在您创建

  10. ruby - 实现k最近邻需要哪些数据? - 2

    我目前有一个reddit克隆类型的网站。我正在尝试根据我的用户之前喜欢的帖子推荐帖子。看起来K最近邻或k均值是执行此操作的最佳方法。我似乎无法理解如何实际实现它。我看过一些数学公式(例如k表示维基百科页面),但它们对我来说并没有真正意义。有人可以推荐一些伪代码,或者可以查看的地方,以便我更好地了解如何执行此操作吗? 最佳答案 K最近邻(又名KNN)是一种分类算法。基本上,您采用包含N个项目的训练组并对它们进行分类。如何对它们进行分类完全取决于您的数据,以及您认为该数据的重要分类特征是什么。在您的示例中,这可能是帖子类别、谁发布了该项

随机推荐