jsp实例(一)

本文介绍了一个使用JSP、Servlet、Filter、Listener、JDBC和MySQL实现的用户登录系统,强调了用户只能在一个地方登录的功能。通过创建数据库、设置项目结构、编写POJO类和JDBC工具类,然后实现登录业务逻辑,包括登录验证、session管理和异常处理。文章还详细讲解了如何处理HttpSession超时销毁的异常,以及添加验证码功能来增强安全性,最后介绍了用户退出登录的实现方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

需求

实现用户登录与退出登录功能,要求一个用户只能在一处登录。 完成对用户表的 CRUD 操作。
使用技术: JSP、Servlet、Filter、Listener、JDBC、MySQL

实现项目

创建数据库

CREATE TABLE `user3` ( `userid` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(30) DEFAULT NULL, `userpwd` varchar(30) DEFAULT NULL, `usersex` varchar(2) DEFAULT NULL, `phonenumber` varchar(30) DEFAULT NULL, `qqnumber` varchar(20) DEFAULT NULL, PRIMARY KEY (`userid`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

在这里插入图片描述

搭建项目

创建项目

在这里插入图片描述
在这里插入图片描述

添加jar包

在这里插入图片描述
添加的jar包:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

添加 jstl 标签库的约束文件

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

在这里插入图片描述
在这里插入图片描述

创建包

在这里插入图片描述

创建 POJO用户类

package com.bjsxt.pojo;

public class Users{
    private int userid;
    private String username;
    private String userpwd;
    private String usersex;
    private String phonenumber;
    private String qqnumber;

    public Users(int userid, String username, String userpwd, String usersex, String phonenumber, String qqnumber) {
        this.userid = userid;
        this.username = username;
        this.userpwd = userpwd;
        this.usersex = usersex;
        this.phonenumber = phonenumber;
        this.qqnumber = qqnumber;
    }
    public Users(){
        super();
    }
    @Override
    public String toString() {
        return "Users{" +
                "userid=" + userid +
                ", username='" + username + '\'' +
                ", userpwd='" + userpwd + '\'' +
                ", usersex='" + usersex + '\'' +
                ", phonenumber='" + phonenumber + '\'' +
                ", qqnumber='" + qqnumber + '\'' +
                '}';
    }

    public int getUserid() {
        return userid;
    }

    public void setUserid(int userid) {
        this.userid = userid;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getUserpwd() {
        return userpwd;
    }

    public void setUserpwd(String userpwd) {
        this.userpwd = userpwd;
    }

    public String getUsersex() {
        return usersex;
    }

    public void setUsersex(String usersex) {
        this.usersex = usersex;
    }

    public String getPhonenumber() {
        return phonenumber;
    }

    public void setPhonenumber(String phonenumber) {
        this.phonenumber = phonenumber;
    }

    public String getQqnumber() {
        return qqnumber;
    }

    public void setQqnumber(String qqnumber) {
        this.qqnumber = qqnumber;
    }

}

创建 JDBC 工具类

db.properties:
这个db文件,要放到src的目录下

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3307/test?useUnicode=true&charac terEncoding=utf8
jdbc.username=root
jdbc.password=1234567

创建JdbcUtils工具类
放到commons文件夹内:

package com.bjsxt.commons;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.ResourceBundle;

public class JdbcUtils {
    private static String driver;
    private static String url;
    private static String username;
    private static String password;
    static {
        try{
            ResourceBundle bundle = ResourceBundle.getBundle("db");
            driver = bundle.getString("jdbc.driver");
            url = bundle.getString("jdbc.url");
            username = bundle.getString("jdbc.username");
            password = bundle.getString("jdbc.password");
            Class.forName(driver);
        }catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
    //获取连接方法
    public static Connection getConnection(){
        Connection conn = null;
        try {
            conn = DriverManager.getConnection(url,username,password);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return conn;
    }
    //关闭连接
    public static void closeConnection(Connection conn){
        try {
            conn.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

注意:

在实践的过程内,出现了报错:at java.lang.Class.forName()
本次问题是因为mysql的jar包出现了问题
解决方法:
点击跳转链接
在这里插入图片描述

实现业务

用户登录

导入登录页面的js,css以及图片文件夹
在这里插入图片描述
在这里插入图片描述

创建登录页面

创建名为login.jsp的登录页面:


<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>欢迎登录后台管理系统</title>
    <link href="css/style.css" rel="stylesheet" type="text/css" />
    <script language="JavaScript" src="js/jquery.js"></script>
    <script src="js/cloud.js" type="text/javascript"></script>

    <script language="javascript">
        $(function(){
            $('.loginbox').css({'position':'absolute','left':($(window).width()-692)/2});
            $(window).resize(function(){
                $('.loginbox').css({'position':'absolute','left':($(window).width()-692)/2});
            })
        });
    </script>

</head>

<body style="background-color:#1c77ac; background-image:url(images/light.png); background-repeat:no-repeat; background-position:center top; overflow:hidden;">

<div id="mainBody">
    <div id="cloud1" class="cloud"></div>
    <div id="cloud2" class="cloud"></div>
</div>

<div class="logintop">
    <ul>
        <li><a href="#">回首页</a></li>
        <li><a href="#">帮助</a></li>
        <li><a href="#">关于</a></li>
    </ul>
</div>

<div class="loginbody">

    <div class="loginbox loginbox2">
        <form action="login.do" method="post">
            <ul>
                <li><input name="username" type="text" class="loginuser" value="admin" onclick="JavaScript:this.value=''"/></li>
                <li><input name="userpwd" type="text" class="loginpwd" value="密码" onclick="JavaScript:this.value=''"/></li>
                <li class="yzm">
                    <span><input name="" type="text" value="验证码" onclick="JavaScript:this.value=''"/></span><cite>X3D5S</cite>
                </li>
                <li><input name="" type="submit" class="loginbtn" value="登录"  onclick="javascript:window.location='main.html'"  /></li>
            </ul>
        </form>

    </div>

</div>

</body>
</html>

创建登录业的务持久层

在dao包内创建一个接口UserLoginDao

package com.bjsxt.dao;

import com.bjsxt.pojo.Users;

public interface UserLoginDao {
    public Users selectUsersByUserNameAndUserPwd(String username, String userpwd);
}

在dao包下的impl文件夹下,创建一个实现接口UserLoginDao的实现类 – UserLoginDaoImpl类

package com.bjsxt.dao.impl;

import com.bjsxt.commons.JdbcUtils;
import com.bjsxt.dao.UserLoginDao;
import com.bjsxt.pojo.Users;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * 对于用户登录的数据库查询
 */
public class UserLoginDaoImpl implements UserLoginDao {
    @Override
    public Users selectUsersByUserNameAndUserPwd(String username, String userpwd) {
        Users user = null;
        Connection conn = null;
        try {
            conn = JdbcUtils.getConnection();
            PreparedStatement ps = conn.prepareStatement("select * from user3 where username = ? and userpwd = ?");
            ps.setString(1,username);
            ps.setString(2,userpwd);
            ResultSet resultSet = ps.executeQuery();
            while (resultSet.next()){
                user = new Users();
                user.setUsersex(resultSet.getString("usersex"));
                user.setUserpwd(resultSet.getString("userpwd"));
                user.setUsername(resultSet.getString("username"));
                user.setUserid(resultSet.getInt("userid"));
                user.setPhonenumber(resultSet.getString("phonenumber"));
                user.setQqnumber(resultSet.getString("qqnumber"));
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {

        }
        return user;
    }
}

创建登录页的业务层

创建自定义异常的类,用于对于出现的异常进行自定义处理

package com.bjsxt.service;

/**
 * 异常包,用于自定义异常
 */
public class UserNotFoundException extends RuntimeException{
    public UserNotFoundException() {

    }
    public UserNotFoundException(String message) {
        super(message);
    }
    public UserNotFoundException(String message, Throwable cause) {
        super(message, cause);
    }
}

创建一个接口:

package com.bjsxt.service;

import com.bjsxt.pojo.Users;

public interface UserLoginService {
    Users userLogin(String username, String userpwd);
}

创建接口的实现类,用于实现登录页的用户登录检测部分

package com.bjsxt.service.impl;

import com.bjsxt.dao.UserLoginDao;
import com.bjsxt.dao.impl.UserLoginDaoImpl;
import com.bjsxt.pojo.Users;
import com.bjsxt.service.UserLoginService;
import com.bjsxt.service.UserNotFoundException;

/**
 * 用户登录业务
 */
public class UserLoginServiceImpl implements UserLoginService {
    /**
     * 用户登录
     */
    @Override
    public Users userLogin(String username, String userpwd) {
        /**实例化之前创建的持久层,让他获取到我们的用户名和密码去数据库内进行查询*/
        //创建持久层的对象
        UserLoginDao userLoginDao = new UserLoginDaoImpl();
        //将用户输入的用户名和密码交给持久层,和数据库进行比对
        Users users = userLoginDao.selectUsersByUserNameAndUserPwd(username, userpwd);
        //对返回的数据进行判断
        if(users == null){
            //使用自定义的异常处理方式对异常进行处理
            throw new UserNotFoundException("用户名或密码有误!");
        }
        return users;
    }
}

创建登录业务的servlet 实现简单的用户登录

创建Servlet

package com.bjsxt.web.servlet;

import com.bjsxt.commons.Constants;
import com.bjsxt.pojo.Users;
import com.bjsxt.service.UserLoginService;
import com.bjsxt.service.UserNotFoundException;
import com.bjsxt.service.impl.UserLoginServiceImpl;
 
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;

/**
 * 处理用户登录请求
 * 因为我们在login.jsp页面内请求的是login.do,
 * 所以我们在这里创建的WebServlet就是login.do
 */
@WebServlet("/login.do")
public class UserLoginServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
       this.doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("进入login.do");
        //当用户在from表单内点击提交请求后,会给我们发送过来两个数据:username userpwd
        String username = req.getParameter("username");
        String userpwd = req.getParameter("userpwd");
        String code = req.getParameter("code");

        System.out.println("获取到用户名和密码:"+username+" == "+userpwd+" == "+code);
        try{
            //创建业务层对象
            UserLoginService userLoginService = new UserLoginServiceImpl();
            Users users = userLoginService.userLogin(username, userpwd);
            System.out.println("从业务层获取到的users:"+users);
            //如果业务层找到了和登录用户匹配的密码,那么我们的登录时成功的
            //我们就可以建立客户端与服务端的会话状态
            //将查到的users用户放到session对象内,为了以后不会出现对每一个session的key进行修改的情况
            //我们将key值封装到一个抽象类内,需要时进行调用
            HttpSession session = req.getSession();
            session.setAttribute(Constants.USER_SESSION_KEY,users);
            //使用重定向方式去跳转到首页
            resp.sendRedirect("main.jsp");
        }catch(UserNotFoundException e){
            //将传递的值在main.jsp页面内进行尝试显示
            req.setAttribute("msg",e.getMessage());
            req.getRequestDispatcher("login.jsp").forward(req,resp);
        }catch (Exception e){
            e.printStackTrace();
            //基于重定向的跳转,直接跳转到一个报错的页面
            resp.sendRedirect("error.jsp");
        }
    }
}

添加登录后的首页:main.jsp

<%--
  Created by IntelliJ IDEA.
  User: Administrator
  Date: 2021/2/28 0028
  Time: 3:50
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>信息管理系统界面</title>
</head>
<frameset rows="*,31" cols="*" frameborder="no" border="0" framespacing="0">
    <frameset cols="187,*" frameborder="no" border="0" framespacing="0">
        <frame src="left.jsp" name="leftFrame" scrolling="No" noresize="noresize" id="leftFrame" title="leftFrame" />
        <frame src="index.jsp" name="rightFrame" id="rightFrame" title="rightFrame" />
    </frameset>
    <frame src="footer.jsp" name="bottomFrame" scrolling="No" noresize="noresize" id="bottomFrame" title="bottomFrame" />
</frameset>
<noframes><body>
</body></noframes>
</html>

其他导入的页面有:
error.jsp 报错页面:

<%--
  Created by IntelliJ IDEA.
  User: Administrator
  Date: 2021/2/28 0028
  Time: 3:55
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    出错了,请于管理员联系。。。
</body>
</html>


footer.jsp

<%--
  Created by IntelliJ IDEA.
  User: Administrator
  Date: 2021/2/28 0028
  Time: 3:54
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>无标题文档</title>
    <link href="css/style.css" rel="stylesheet" type="text/css" />

</head>

<body>
<div class="footer">
    <span></span>
    <i></i>
</div>
</body>
</html>

index.jsp:

<%--
  Created by IntelliJ IDEA.
  User: Administrator
  Date: 2021/2/26 0026
  Time: 18:28
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html><head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <title>无标题文档</title>
  <link href="css/style.css" rel="stylesheet" type="text/css" />
  <script type="text/javascript" src="js/jquery.js"></script>

</head>

<body>

<div class="place">
  <span>位置:</span>
  <ul class="placeul">
    <li><a href="#">首页</a></li>
  </ul>
</div>

<div class="mainindex">

  <div class="welinfo">
    <span><img src="images/sun.png" alt="天气" /></span>
    <b></b>
  </div>
</div>
</body>
</html>

left.jsp

<%--
  Created by IntelliJ IDEA.
  User: Administrator
  Date: 2021/2/28 0028
  Time: 3:52
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>无标题文档</title>
    <link href="css/style.css" rel="stylesheet" type="text/css" />
    <script language="JavaScript" src="js/jquery.js"></script>

    <script type="text/javascript">
        $(function(){
            //导航切换
            $(".menuson .header").click(function(){
                var $parent = $(this).parent();
                $(".menuson>li.active").not($parent).removeClass("active open").find('.sub-menus').hide();

                $parent.addClass("active");
                if(!!$(this).next('.sub-menus').size()){
                    if($parent.hasClass("open")){
                        $parent.removeClass("open").find('.sub-menus').hide();
                    }else{
                        $parent.addClass("open").find('.sub-menus').show();
                    }


                }
            });

            // 三级菜单点击
            $('.sub-menus li').click(function(e) {
                $(".sub-menus li.active").removeClass("active")
                $(this).addClass("active");
            });

            $('.title').click(function(){
                var $ul = $(this).next('ul');
                $('dd').find('.menuson').slideUp();
                if($ul.is(':visible')){
                    $(this).next('.menuson').slideUp();
                }else{
                    $(this).next('.menuson').slideDown();
                }
            });
        })
    </script>

</head>

<body style="background:#f0f9fd;">
<div class="lefttop"><span></span>导航菜单</div>

<dl class="leftmenu">
    <dd>
        <div class="title"><span><img src="images/leftico03.png" /></span>用户管理</div>
        <ul class="menuson">
            <li><cite></cite><a href="usermanager/addUser.jsp" target="rightFrame">添加用户</a><i></i></li>
            <li><cite></cite><a href="usermanager/findUser.jsp" target="rightFrame">查询用户</a><i></i></li>
            <li><cite></cite><a href="logout.do" target="rightFrame">退出登录</a><i></i></li>
        </ul>
    </dd>
</dl>

</body>
</html>

我们 输入 http://localhost:8080/webdemo/login.jsp 进入登录页面

在这里插入图片描述
输入错误的名称和密码
在这里插入图片描述
会给出报错提示
输入正确的用户名(oldliu) 和密码(123)
用户名和密码我们在数据库内进行设置
在这里插入图片描述

在这里插入图片描述

创建登录业务的Filter

我们实现的用户登录,在一个浏览器上登录之后,进入的main.jsp页面,在其他浏览器上也可以进行访问,这很明显是不允许的
这里缺乏一个对于客户端浏览器是否登录的状态的判断,这里依赖于一个filter去实现

实现思路:
当客户端浏览器再一次去请求服务端的资源时,我添加的过滤器要先拦截你的请求,查看当前的客户端浏览器是否是登录的状态,如果是登录的状态,那允许访问,如果不是则让其跳回登录页面。
Filter如何知道当前的客户端浏览器是否是一个登录状态呢?
如果用户登录成功,那么我们将用户的信息放到了客户端浏览器的session内,在客户端浏览器内获取到对应的session,再从session内取user对象,如果可以获取到这个user对象,那么也就说明这个浏览器已经被登录过,那么允许访问资源。
拦截检查的是动态访问资源,如.do .jsp 而不是静态资源 ,如:.css**

代码:

package com.bjsxt.web.filter;

import com.bjsxt.commons.Constants;
import com.bjsxt.pojo.Users;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.IOException;

/**
 * 判断当前客户端浏览器是否登录的Filter
 */
//对.do和.jsp的请求进行拦截
@WebFilter(urlPatterns = {"*.do","*.jsp"})
public class UserLoginFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        //获取用户请求的url,看看是否是login.jsp或者login.do
        HttpServletRequest request = (HttpServletRequest)servletRequest;
        String uri = request.getRequestURI();
        //判断当前请求的是否为login.jsp或者login.do,如果请求的是用户登录的资源那么需要放行。
        //!= -1说明找到了
        if(uri.indexOf("login.jsp") != -1 || uri.indexOf("login.do") != -1|| uri.indexOf("validateCode.do") != -1){
            filterChain.doFilter(servletRequest, servletResponse);
        }else{
            HttpSession session = request.getSession();
            //从session对象内获取到users对象  返回的是一个Object对象,需要进行强转
            Users users = (Users) session.getAttribute(Constants.USER_SESSION_KEY);
            if(users !=  null){
                filterChain.doFilter(servletRequest, servletResponse);
            }else{
                request.setAttribute(Constants.REQUEST_MSG,"不登录不好使!");
                request.getRequestDispatcher("login.jsp").forward(servletRequest, servletResponse);
            }
        }

    }

    @Override
    public void destroy() {

    }
}

利用之前在Constants.java内构建的抽象类


public abstract class Constants {
    public static String USER_SESSION_KEY="users";
    public static String REQUEST_MSG="msg";
    public static String VALIDATE_CODE_KEY="code";
}

对浏览器发来的请求进行判断
当不在一个浏览器登录时
在这里插入图片描述
左上角给出报错提示

实现用户只可以在一处登录

实现功能:
当前用户登录之后,在另一个浏览器上登录时,会将原来的用户登录状态挤掉

分析:
我们可以将每一个客户端浏览器登录后对应的Httpsession对象放到servletContext对象内进行缓存,放到vlaue的位置上,key使用的是用户的userid
例如我们在谷歌浏览器内使用oldliu进行登录,我们在servletContext对象的缓存的key查到oldliu的id。我们再次登录时,要先去servletContext对象去找,我们在IE浏览器内使用的也是oldliu登录,我们先去根据userid在servletContext对象内进行查找,看是否可以找到Httpsession,如果找到,那么说明登录过,我们要先把Httpsession对象销毁,在将IE浏览器端的session放到被销毁的Httpsession对象的位置上,原来的servletContext对象的Httpsession已经被销毁,想要在谷歌浏览器上再次请求时,已经获取不到原来对应的Httpsession,我们就让他回到登录页面。
这个业务需要在页面跳转之前完成
我们在UserLoginServlet.java内进行补充代码

   protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("进入login.do");
        //当用户在from表单内点击提交请求后,会给我们发送过来两个数据:username userpwd
        String username = req.getParameter("username");
        String userpwd = req.getParameter("userpwd");
        String code = req.getParameter("code");

        System.out.println("获取到用户名和密码:"+username+" == "+userpwd+" == "+code);
        try{
            //创建业务层对象
            UserLoginService userLoginService = new UserLoginServiceImpl();
            Users users = userLoginService.userLogin(username, userpwd);
            System.out.println("从业务层获取到的users:"+users);
            //如果业务层找到了和登录用户匹配的密码,那么我们的登录时成功的
            //我们就可以建立客户端与服务端的会话状态
            //将查到的users用户放到session对象内,为了以后不会出现对每一个session的key进行修改的情况
            //我们将key值封装到一个抽象类内,需要时进行调用
            HttpSession session = req.getSession();
            session.setAttribute(Constants.USER_SESSION_KEY,users);

            /**
             * 实现用户只可以在一处登录的效果
             */
            ServletContext servletContext = this.getServletContext();
            //users.getUserid()+""  :添加一个”“,将其转为一个字符串
            HttpSession temp = (HttpSession) servletContext.getAttribute(users.getUserid()+"");
            if (temp!=null){    //如果找到,说明这个账号已经登录过
                //在servletContext对象内先找到它,将其销毁
                servletContext.removeAttribute(users.getUserid()+"");

                temp.invalidate();
            }
            //将新的浏览器的HttpSession对象放到被销毁的Httpsession对象的位置上
            servletContext.setAttribute(users.getUserid()+"",session);

            //使用重定向方式去跳转到首页
            resp.sendRedirect("main.jsp");
        }catch(UserNotFoundException e){
            //将传递的值在main.jsp页面内进行尝试显示
            req.setAttribute("msg",e.getMessage());
            req.getRequestDispatcher("login.jsp").forward(req,resp);
        }catch (Exception e){
            e.printStackTrace();
            //基于重定向的跳转,直接跳转到一个报错的页面
            resp.sendRedirect("error.jsp");
        }
    }

关键代码:

            /**
             * 实现用户只可以在一处登录的效果
             */
            ServletContext servletContext = this.getServletContext();
            //users.getUserid()+""  :添加一个”“,将其转为一个字符串
            HttpSession temp = (HttpSession) servletContext.getAttribute(users.getUserid()+"");
            if (temp!=null){    //如果找到,说明这个账号已经登录过
                //在servletContext对象内先找到它,将其销毁
                servletContext.removeAttribute(users.getUserid()+"");

                temp.invalidate();
            }
            //将新的浏览器的HttpSession对象放到被销毁的Httpsession对象的位置上
            servletContext.setAttribute(users.getUserid()+"",session);

这样,业务功能被实现
我们完善一下jsp页面
left.jsp点击跳转的页面:

    <dd>
        <div class="title"><span><img src="images/leftico03.png" /></span>用户管理</div>
        <ul class="menuson">
            <li><cite></cite><a href="usermanager/addUser.jsp" target="rightFrame">添加用户</a><i></i></li>
            <li><cite></cite><a href="usermanager/findUser.jsp" target="rightFrame">查询用户</a><i></i></li>
            <li><cite></cite><a href="logout.do" target="rightFrame">退出登录</a><i></i></li>
        </ul>
    </dd>

addUser.jsp:

<%--
  Created by IntelliJ IDEA.
  User: Administrator
  Date: 2021/2/28 0028
  Time: 18:02
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>

<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>无标题文档</title>
    <link href="../css/style.css" rel="stylesheet" type="text/css" />
    <link href="../css/style.css" rel="stylesheet" type="text/css" />
    <link href="../css/select.css" rel="stylesheet" type="text/css" />
</head>

<body>

<div class="place">
    <span>位置:</span>
    <ul class="placeul">
        <li><a href="#">用户管理</a></li>
        <li><a href="#">添加用户</a></li>
    </ul>
</div>

<div class="formbody">

    <div class="formtitle"><span>基本信息</span></div>
    <form action="../userManager.do" method="post">
        <input type="hidden"  name="flag" value="addUser"/>
        <ul class="forminfo">
            <li>
                <label>用户名</label>
                <input name="username" type="text" class="dfinput" /></li>
            <li>
            <li>
                <label>用户密码</label>
                <input name="userpwd" type="text" class="dfinput" /><i></i></li>
            <li>
                <label>性别</label><cite>
                <input name="usersex" type="radio" value="1" checked="checked" />&nbsp;&nbsp;&nbsp;&nbsp;
                <input name="usersex" type="radio" value="0" /></li>
            <li>
                <label>联系方式</label>
                <input name="phonenumber" type="text" class="dfinput" />
            </li>
            <li>
                <label>QQ号</label>
                <input name="qqnumber" type="text" class="dfinput" />
            </li>
            <li>
                <label>&nbsp;</label>
                <input  type="submit"  class="btn" value="确认保存" />
            </li>
        </ul>
    </form>
</div>

</body>

</html>


findUser.jsp:

<%--
  Created by IntelliJ IDEA.
  User: Administrator
  Date: 2021/2/28 0028
  Time: 18:02
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>无标题文档</title>
    <link href="../css/style.css" rel="stylesheet" type="text/css" />
    <link href="../css/select.css" rel="stylesheet" type="text/css" />
</head>

<body>

<div class="place">
    <span>位置:</span>
    <ul class="placeul">
        <li><a href="#">用户管理</a></li>
        <li><a href="#">查询用户</a></li>
    </ul>
</div>

<div class="rightinfo">
    <form action="../userManager.do" method="post">
        <input type="hidden" name="flag" value="findUser"/>
        <ul class="prosearch">
            <li>
                <label>查询:</label>
                <i>用户名</i>
                <a>
                    <input name="username" type="text" class="scinput" />
                </a>
            </li>
            <li>
                <label>性别:</label>
                <input name="usersex" type="radio" value="1" checked="checked" />&nbsp;&nbsp;&nbsp;
                <input name="usersex" type="radio" value="0" />&nbsp;</li>
            <li>
                <label>手机号:</label>
                <a>
                    <input name="phonenumber" type="text" class="scinput" />
                </a>
            </li>
            <li>
                <label>QQ号:</label>
                <a>
                    <input name="qqnumber" type="text" class="scinput" />
                </a>
            </li>
            <a>
                <input type="submit" class="sure" value="查询"/>
            </a>
        </ul>
    </form>
</body>
</html>


出现这种问题,是因为没有跳回到上一个请求路径,在当前的显示区域内显示登陆页面
我们在main.jsp页面内添加部分代码:
在这里插入图片描述

        if (window.parent.length>0){
            //让他们的父窗口发送请求
            window.parent.location="login.jsp";
        }

这样,就实现了用户只可以在一处浏览器登录的效果

解决HttpSession超时销毁时的异常问题

问题产生的原因:
在谷歌浏览器登录过一次后,再次在其他浏览器登录,由于在谷歌浏览器登录时,session会被缓存到servletContext对象内,当设定的时间到达,我们的tomcat会销毁这个客户端谷歌浏览器对应的session对象,但在servletCOntext对象内仍然还缓存着谷歌浏览器的session对象,说明缓存的就是一个已经被销毁的httpSession对象。
因为servletCOntext对象内仍然还缓存着谷歌浏览器的session对象,所以在IE浏览器内仍然可以通过servletContext对象的userid获取到客户端浏览器对应的session,查到存在session对象,我们就要按照用户只可以登录一次来进行销毁,但是这个session已经被销毁了,我们对于一个已经被销毁的session再次进行销毁时,就会抛出异常。
解决思路:
我们可以创建一个httpSession的生命周期监听器,当session被销毁之前会触发这个监听方法,在这个监听方法内,我们获取到servletContext对象,从servletContext对象内,将对应的session删除掉。
这样我们用户在其他浏览器内登录,也不会在servletContext内获取到已经因为超时被删掉的session对象。

在web.xml设置一个定时:


    <!--添加一个超时时间的配置,添加一分钟的时间限制-->
    <session-config>
        <session-timeout>1</session-timeout>
    </session-config>

在启动登录页面一分钟以后,我们再次登录,会报一个异常:

java.lang.IllegalStateException: invalidate: Session already invalidated
	at org.apache.catalina.session.StandardSession.invalidate(StandardSession.java:1288)
	at org.apache.catalina.session.StandardSessionFacade.invalidate(StandardSessionFacade.java:176)
	at com.bjsxt.web.servlet.UserLoginServlet.doPost(UserLoginServlet.java:61)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:648)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:291)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
	at com.bjsxt.web.filter.UserLoginFilter.doFilter(UserLoginFilter.java:31)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:219)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106)
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:142)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
	at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:610)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:518)
	at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1091)
	at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:668)
	at org.apache.coyote.http11.Http11NioProtocol$Http11ConnectionHandler.process(Http11NioProtocol.java:223)
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1517)
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1474)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
	at java.lang.Thread.run(Thread.java:744)

在listener文件夹内创建文件:

package com.bjsxt.web.listener;

import com.bjsxt.commons.Constants;
import com.bjsxt.pojo.Users;

import javax.servlet.ServletContext;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

/**
 * 解决HttpSession被反复销毁的问题
 */
@WebListener
public class HttpSessionLifecycleListener implements HttpSessionListener {
    @Override
    public void sessionCreated(HttpSessionEvent se) {

    }

    @Override
    public void sessionDestroyed(HttpSessionEvent se) {
        //获取ServletContext对象,然后将所对应的HttpSession删除掉
        HttpSession session = se.getSession();
        ServletContext servletContext = session.getServletContext();
        Users users = (Users) session.getAttribute(Constants.USER_SESSION_KEY);
        servletContext.removeAttribute(users.getUserid()+"");
    }
}

这样,异常就会消除

添加验证码功能

在commons文件夹下创建一个生成验证码和干扰线的文件:

package com.bjsxt.commons;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;

import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


@WebServlet("/validateCode.do")
public class ValidateCodeServlet extends HttpServlet {


    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        // 在内存中创建图象
        int width = 70, height = 45;
        BufferedImage image = new BufferedImage(width, height,
                BufferedImage.TYPE_INT_RGB);
        // 获取图形上下文
        Graphics g = image.getGraphics();
        // 生成随机类
        Random random = new Random();
        // 设定背景色
        g.setColor(getRandColor(200, 250));
        g.fillRect(0, 0, width, height);
        // 设定字体
        g.setFont(new Font("Times New Roman", Font.PLAIN, 20));
        // 随机产生155条干扰线,使图象中的认证码不易被其它程序探测到
        g.setColor(getRandColor(160, 200));
        for (int i = 0; i < 155; i++) {
            int x = random.nextInt(width);
            int y = random.nextInt(height);
            int xl = random.nextInt(12);
            int yl = random.nextInt(12);
            g.drawLine(x, y, x + xl, y + yl);
        }
        // 取随机产生的认证码(4位数字)
        String sRand = "";
        for (int i = 0; i < 4; i++) {
            String rand = String.valueOf(random.nextInt(10));
            sRand += rand;
            // 将认证码显示到图象中
            g.setColor(new Color(20 + random.nextInt(110), 20 + random
                    .nextInt(110), 20 + random.nextInt(110)));
            // 调用函数出来的颜色相同,可能是因为种子太接近,所以只能直接生成
            g.drawString(rand, 13 * i + 6, 16);
        }
        // 图象生效
        g.dispose();

        try {
            ImageIO.write(image, "JPEG", response.getOutputStream());
        } catch (Exception e) {
            System.out.println("验证码图片产生出现错误:" + e.toString());
        }
        //保存验证码到Session
        request.getSession().setAttribute("randStr", sRand);

    }


    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        this.doGet(request, response);
    }
    /*
     * 给定范围获得随机颜色
     */
    private Color getRandColor(int fc, int bc) {
        Random random = new Random();
        if (fc > 255)
            fc = 255;
        if (bc > 255)
            bc = 255;
        int r = fc + random.nextInt(bc - fc);
        int g = fc + random.nextInt(bc - fc);
        int b = fc + random.nextInt(bc - fc);
        return new Color(r, g, b);
    }
}

在login.jsp文件内的验证码区域,进行修改:

<li class="yzm">
                    <span><input name="code" type="text" value="验证码" onclick="JavaScript:this.value=''"/></span><cite><img id="code" src="validateCode.do" onclick="change1()"></cite>
                </li>

在login.jsp文件上面的js区域,添加一个change1()方法

        $(function(){
            $('.loginbox').css({'position':'absolute','left':($(window).width()-692)/2});
            $(window).resize(function(){
                $('.loginbox').css({'position':'absolute','left':($(window).width()-692)/2});
            });
        }); 
            //实现点击图片动态的更新验证码
            function change1(){
                $("#code").attr("src","validateCode.do?"+Math.random());
            }

在抽象类内的属性:


    public static String VALIDATE_CODE_KEY="code";

在我们的servlet类 ValidateCodeServlet.java内,有代码将验证码保存到Session:


//保存验证码到Session 
request.getSession().setAttribute(Constants.VALIDATE_CODE_KEY, sRand);

我们将用于于处理用户登录请求的UserLoginServlet.java重新进行有关验证码的修改
修改前:

package com.bjsxt.web.servlet;

import com.bjsxt.commons.Constants;
import com.bjsxt.pojo.Users;
import com.bjsxt.service.UserLoginService;
import com.bjsxt.service.UserNotFoundException;
import com.bjsxt.service.impl.UserLoginServiceImpl;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;

/**
 * 处理用户登录请求
 * 因为我们在login.jsp页面内请求的是login.do,
 * 所以我们在这里创建的WebServlet就是login.do
 */
@WebServlet("/login.do")
public class UserLoginServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
       this.doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("进入login.do");
        //当用户在from表单内点击提交请求后,会给我们发送过来两个数据:username userpwd
        String username = req.getParameter("username");
        String userpwd = req.getParameter("userpwd");
        String code = req.getParameter("code");

        System.out.println("获取到用户名和密码:"+username+" == "+userpwd+" == "+code);
        try{
            //创建业务层对象
            UserLoginService userLoginService = new UserLoginServiceImpl();
            Users users = userLoginService.userLogin(username, userpwd);
            System.out.println("从业务层获取到的users:"+users);
            //如果业务层找到了和登录用户匹配的密码,那么我们的登录时成功的
            //我们就可以建立客户端与服务端的会话状态
            //将查到的users用户放到session对象内,为了以后不会出现对每一个session的key进行修改的情况
            //我们将key值封装到一个抽象类内,需要时进行调用
            HttpSession session = req.getSession();
            session.setAttribute(Constants.USER_SESSION_KEY,users);

            /**
             * 实现用户只可以在一处登录的效果
             */
            ServletContext servletContext = this.getServletContext();
            //users.getUserid()+""  :添加一个”“,将其转为一个字符串
            HttpSession temp = (HttpSession) servletContext.getAttribute(users.getUserid()+"");
            if (temp!=null){    //如果找到,说明这个账号已经登录过
                //在servletContext对象内先找到它,将其销毁
                servletContext.removeAttribute(users.getUserid()+"");

                temp.invalidate();
            }
            //将新的浏览器的HttpSession对象放到被销毁的Httpsession对象的位置上
            servletContext.setAttribute(users.getUserid()+"",session);

            //使用重定向方式去跳转到首页
            resp.sendRedirect("main.jsp");
        }catch(UserNotFoundException e){
            //将传递的值在main.jsp页面内进行尝试显示
            req.setAttribute("msg",e.getMessage());
            req.getRequestDispatcher("login.jsp").forward(req,resp);
        }catch (Exception e){
            e.printStackTrace();
            //基于重定向的跳转,直接跳转到一个报错的页面
            resp.sendRedirect("error.jsp");
        }
    }
}

修改后:

   protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("进入login.do");
        //当用户在from表单内点击提交请求后,会给我们发送过来两个数据:username userpwd
        String username = req.getParameter("username");
        String userpwd = req.getParameter("userpwd");
        String code = req.getParameter("code");

        System.out.println("获取到用户名和密码:"+username+" == "+userpwd+" == "+code);
        try{
            HttpSession session = req.getSession();
            String codeTemp = (String)session.getAttribute(Constants.VALIDATE_CODE_KEY);
            //输入的验证码和生成的验证码一样时
            if (codeTemp.equals(code)){

                //创建业务层对象
                UserLoginService userLoginService = new UserLoginServiceImpl();
                Users users = userLoginService.userLogin(username, userpwd);
                System.out.println("从业务层获取到的users:"+users);
                //如果业务层找到了和登录用户匹配的密码,那么我们的登录时成功的
                //我们就可以建立客户端与服务端的会话状态
                //将查到的users用户放到session对象内,为了以后不会出现对每一个session的key进行修改的情况
                //我们将key值封装到一个抽象类内,需要时进行调用
                session.setAttribute(Constants.USER_SESSION_KEY,users);

                /**
                 * 实现用户只可以在一处登录的效果
                 */
                ServletContext servletContext = this.getServletContext();
                //users.getUserid()+""  :添加一个”“,将其转为一个字符串
                HttpSession temp = (HttpSession) servletContext.getAttribute(users.getUserid()+"");
                if (temp!=null){    //如果找到,说明这个账号已经登录过
                    //在servletContext对象内先找到它,将其销毁
                    servletContext.removeAttribute(users.getUserid()+"");

                    temp.invalidate();
                }
                //将新的浏览器的HttpSession对象放到被销毁的Httpsession对象的位置上
                servletContext.setAttribute(users.getUserid()+"",session);

                //使用重定向方式去跳转到首页
                resp.sendRedirect("main.jsp");
            }else{  //输入的验证码不对
                req.setAttribute(Constants.REQUEST_MSG,"验证码有误,请重新输入");
                req.getRequestDispatcher("login.jsp").forward(req,resp);
            }
        }catch(UserNotFoundException e){
            //将传递的值在main.jsp页面内进行尝试显示
            req.setAttribute("msg",e.getMessage());
            req.getRequestDispatcher("login.jsp").forward(req,resp);
        }catch (Exception e){
            e.printStackTrace();
            //基于重定向的跳转,直接跳转到一个报错的页面
            resp.sendRedirect("error.jsp");
        }
    }

加入了对于验证码是否匹配的检测

也实现了点击图片,更换验证码的效果

用户退出登录

在left.jsp页面的退出登录:

<li><cite></cite><a href="logout.do" target="rightFrame">退出登录</a><i></i></li>

在servlet文件夹内,创建LogoutServlet文件

package com.bjsxt.web.servlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;

/**
 * 用户退出登录
 */
@WebServlet("/logout.do")
public class LogoutServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        HttpSession session = req.getSession();
        session.invalidate();
        resp.sendRedirect("login.jsp");
    }
}

这样,我们点击退出登录,就会返回到登录页
在这里插入图片描述
在这里插入图片描述

顺序如下: 1、多种字体大小显示 2、c:out标记输出 3、获取当前时间 4、include包含语句 5、建立错误处理页面的范例程序 6、jsp:forward 7、简单计数器 8、设置页面属性 9、使用GB2312编码 10、使用Big5编码 11、c:catch的用法 12、<c:forEach> begin、end和step的用法 13 、 <c:forEach> 循环 14、<c:forEach> varStatus 的四种属性 15、<c:forEach> 的用法 16、从客户端传送数据至服务端 17、使用Unicode转义字符 18、使用朝鲜语字符集 19、JSP中最简单的国际化程序 20、错误检测 21、抛出异常 22、<c:if> 的用法 23、<c:set>和<c:remove> 的用法 24、<c:import> 的用法 25、jsp-include的用法 26、汉字处理 27、网页重定向 28、自动更新网页 29、存取session 30、<c:redirect> 的用法 31、单选型列表框 32、jsp文件中定义类 33、取得 JSP Container 版本 34、javax.servlet.jsp.JspWriter - out 对象 35、page 隐含对象 36、application 对象 37、PageContext 对象 38、Page范围 - pageContext 39、测试要显示的中文 40、IF控制符的操作 41、HttpServletRequest 接口所提供的方法 42、 网上测验 43、HttpSession - session 对象 44、 多选型列表框 45、解决浏览器 cache 的问题 46、使用 EL、JSTL 处理表单数据 47、 EL隐含对象 param、paramValues 48、EL隐含对象 pageContext 49、EL算术运算符 50、EL关系运算符 51、EL的运算符 52、选择钮的使用 53、检查框的使用 54、群组检查框的使用 55、数字、货币、百分数格式化 56、日期格式化 57、JSTL设置语言地区 58、Cookie数据的存取 59、session有效时间的设置与取得 60、session时间与ID 61、Cookie有效时间的设置 62、利用隐藏字段传送数据 63、JSP 使用 JavaBean 的方法 64、JSP 使用 JavaBean 65、范围为 Page 的 JavaBean范例程序 66、范围为 Request的 JavaBean 67、范围为 Session 的 JavaBean 68、范围为 Application 的 JavaBean 69、删除 JavaBean 70、url重组 71、Switch语句 72、环境变量 73、forward重定向 74、文件的建立与删除 75、取得文件属性 76、取得目录中的文件 77、目录的建立与删除 78、自Cookie存取日期/时间数据 79、管理Session变量 80、数据库中的记录数与记录指针位置 81、利用absolute方法设置记录位置 82、使用jsp指令生成Word文档 83、JSP网页模板 84、判断是否空白文件 85、cookie 用户登录次数 86、获取用户的真实IP地址 87、获取用户的浏览器信息 88、在客户端进行数据检查 89、在JSP中获取当前绝对路径 90、读取表单中所有参数 91、分行写入数据 92、显示请求URL 93、判断session是否过期 94、参数式查询数据库 95、取得数据库中各栏名称 96、使用JavaBean、设置和获取Bean的属性 97、设置Bean的个属性与输入参数关联 98、实现基于数据库的站内搜索 99、DOM读取XML文档 100、SAX读取XML文档
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值