一、 案例需求

需求:

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

创建数据库表

  1. CREATE TABLE `users` (
  2. `userid` int(11) NOT NULL AUTO_INCREMENT,
  3. `username` varchar(30) DEFAULT NULL,
  4. `userpwd` varchar(30) DEFAULT NULL,
  5. `usersex` varchar(2) DEFAULT NULL,
  6. `phonenumber` varchar(30) DEFAULT NULL,
  7. `qqnumber` varchar(20) DEFAULT NULL,
  8. PRIMARY KEY (`userid`)
  9. ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

搭建环境

添加jar包

image.png

创建包

com.bjsxt
-commons 工具类
-exception 自定义异常
-pojo 实体
-dao 访问数据库持久层代码(Date Access Object)
-service 业务层代码
-web web组件
-filter
-servlet
-listener

创建POJO(实体)

  1. package com.bjsxt.pojo;
  2. public class Users {
  3. private int userid;
  4. private String username;
  5. private String userpwd;
  6. private String usersex;
  7. private String phonenumber;
  8. private String qqnumber;
  9. public int getUserid() {
  10. return userid;
  11. }
  12. public void setUserid(int userid) {
  13. this.userid = userid;
  14. }
  15. public String getUsername() {
  16. return username;
  17. }
  18. public void setUsername(String username) {
  19. this.username = username;
  20. }
  21. public String getUserpwd() {
  22. return userpwd;
  23. }
  24. public void setUserpwd(String userpwd) {
  25. this.userpwd = userpwd;
  26. }
  27. public String getUsersex() {
  28. return usersex;
  29. }
  30. public void setUsersex(String usersex) {
  31. this.usersex = usersex;
  32. }
  33. public String getPhonenumber() {
  34. return phonenumber;
  35. }
  36. public void setPhonenumber(String phonenumber) {
  37. this.phonenumber = phonenumber;
  38. }
  39. public String getQqnumber() {
  40. return qqnumber;
  41. }
  42. public void setQqnumber(String qqnumber) {
  43. this.qqnumber = qqnumber;
  44. }
  45. }

创建JDBC工具类

配置文件 db.properties

  1. jdbc.driver=com.mysql.jdbc.Driver
  2. jdbc.url=jdbc:mysql://localhost:3306/bjsxt?useUnicode=true&characterEncoding=utf8
  3. jdbc.username=root
  4. jdbc.password=yang1234

JdbcUtils JDBC连接工具类

  1. package com.bjsxt.commons;
  2. import java.sql.Connection;
  3. import java.sql.DriverManager;
  4. import java.sql.SQLException;
  5. import java.util.ResourceBundle;
  6. public class JdbcUtils {
  7. private static String driver;
  8. private static String url;
  9. private static String username;
  10. private static String userpwd;
  11. static {
  12. //1、解析db配置文件
  13. ResourceBundle resourceBundle = ResourceBundle.getBundle("db");
  14. //2、读取配置文件中4个基本信息
  15. driver = resourceBundle.getString("jdbc.driver");
  16. url = resourceBundle.getString("jdbc.url");
  17. username = resourceBundle.getString("jdbc.username");
  18. userpwd = resourceBundle.getString("jdbc.password");
  19. //3、加载mysql驱动
  20. try {
  21. Class.forName(driver);
  22. } catch (ClassNotFoundException e) {
  23. e.printStackTrace();
  24. }
  25. }
  26. /**
  27. * 获取连接的方法
  28. */
  29. public static Connection getConnection(){
  30. Connection conn = null;
  31. try {
  32. conn = DriverManager.getConnection(url,username,userpwd);
  33. } catch (SQLException e) {
  34. e.printStackTrace();
  35. }
  36. return conn;
  37. }
  38. /**
  39. * 断开连接的方法
  40. */
  41. public static void closeConnection(Connection conn){
  42. try {
  43. conn.close();
  44. } catch (SQLException e) {
  45. e.printStackTrace();
  46. }
  47. }
  48. }

二、业务实现

用户登录

创建用户登录页面

login.jsp登录页面

  1. <%--
  2. Created by IntelliJ IDEA.
  3. User: 20878
  4. Date: 2022/1/23
  5. Time: 12:11
  6. To change this template use File | Settings | File Templates.
  7. --%>
  8. <%@ page contentType="text/html;charset=UTF-8" language="java" %>
  9. <html>
  10. <head>
  11. <meta http-equiv=n"Content-Type" content="text/html; charset=utf-8" />
  12. <title>欢迎登录后台管理系统</title>
  13. <link href="css/style.css" rel="stylesheet" type="text/css" />
  14. <script language="JavaScript" src="js/jquery.js"></script>
  15. <script src="js/cloud.js" type="text/javascript"></script>
  16. <script language="javascript">
  17. if(window.parent.length>0){
  18. window.parent.location="login.jsp";
  19. }
  20. $(function(){
  21. $('.loginbox').css({'position':'absolute','left':($(window).width()-692)/2});
  22. $(window).resize(function(){
  23. $('.loginbox').css({'position':'absolute','left':($(window).width()-692)/2});
  24. })
  25. });
  26. </script>
  27. </head>
  28. <body style="background-color:#1c77ac; background-image:url(images/light.png); background-repeat:no-repeat; background-position:center top; overflow:hidden;">
  29. <div id="mainBody">
  30. <div id="cloud1" class="cloud"></div>
  31. <div id="cloud2" class="cloud"></div>
  32. </div>
  33. <div class="logintop">
  34. <ul>
  35. <li><a href="#">回首页</a></li>
  36. <li><a href="#">帮助</a></li>
  37. <li><a href="#">关于</a></li>
  38. </ul>
  39. </div>
  40. <div class="loginbody">
  41. <div class="loginbox loginbox2">
  42. <form action="login.do" method="post">
  43. <ul>
  44. <li><input name="username" type="text" class="loginuser" value="admin" onclick="JavaScript:this.value=''"/></li>
  45. <li><input name="userpwd" type="text" class="loginpwd" value="密码" onclick="JavaScript:this.value=''"/></li>
  46. <li class="yzm">
  47. <span><input name="" type="text" value="验证码" onclick="JavaScript:this.value=''"/></span><cite>X3D5S</cite>
  48. </li>
  49. <li><input name="" type="submit" class="loginbtn" value="登录" onclick="javascript:window.location='main.html'" /></li>
  50. </ul>
  51. </form>
  52. </div>
  53. </div>
  54. </body>
  55. </html>

创建业务持久层(数据库操作)

UserLoginDao接口

  1. package com.bjsxt.dao;
  2. import com.bjsxt.pojo.Users;
  3. public interface UserLoginDao {
  4. public Users SelectUsersByUserNameAndUserPwd(String username,String userpwd);
  5. }

用户登录的数据库查询

  1. package com.bjsxt.dao;
  2. import com.bjsxt.commons.JdbcUtils;
  3. import com.bjsxt.pojo.Users;
  4. import java.sql.Connection;
  5. import java.sql.PreparedStatement;
  6. import java.sql.ResultSet;
  7. public class UserLoginDaoImp1 implements UserLoginDao{
  8. /**
  9. * 用户登录的数据库查询
  10. * @param username
  11. * @param userpwd
  12. * @return
  13. */
  14. @Override
  15. public Users SelectUsersByUserNameAndUserPwd(String username, String userpwd) {
  16. Users users = null;
  17. Connection conn = null;
  18. try {
  19. //1、获取数据库的连接
  20. conn = JdbcUtils.getConnection();
  21. //2、预编译sql语句,返回PrepareStatement的实例
  22. PreparedStatement ps = conn.prepareStatement
  23. ("select * from users where username=? and userpwd = ?");
  24. //3、填充占位符
  25. ps.setString(1,username);
  26. ps.setString(1,userpwd);
  27. //4、执行
  28. ResultSet resultSet = ps.executeQuery();
  29. //5、将查询结果放入users对象
  30. while (resultSet.next()){
  31. users = new Users();
  32. users.setUsername(resultSet.getString("username"));
  33. users.setUserpwd(resultSet.getString("userpwd"));
  34. users.setUserid(resultSet.getInt("userid"));
  35. users.setUsersex(resultSet.getString("usersex"));
  36. users.setQqnumber(resultSet.getString("qqnumber"));
  37. users.setPhonenumber(resultSet.getString("phonenumber"));
  38. }
  39. } catch (Exception e) {
  40. e.printStackTrace();
  41. }finally {
  42. }
  43. return users;
  44. }
  45. }

创建登录业务的业务层

创建自定义异常

  1. package com.bjsxt.exception;
  2. /**
  3. * 用户登录状态的自定义异常
  4. */
  5. public class UserNotFoundException extends RuntimeException {
  6. public UserNotFoundException() {
  7. }
  8. public UserNotFoundException(String message) {
  9. super(message);
  10. }
  11. public UserNotFoundException(String message, Throwable cause) {
  12. super(message, cause);
  13. }
  14. }

创建业务层

  1. public interface UserLoginService {
  2. Users userLogin(String username,String userpwd);
  3. }
package com.bjsxt.service;

import com.bjsxt.dao.UserLoginDao;
import com.bjsxt.dao.UserLoginDaoImp1;
import com.bjsxt.exception.UserNotFoundException;
import com.bjsxt.pojo.Users;
/**
 * 用户登录业务
 */
public class UserLoginServiceImp1 implements UserLoginService {
    @Override
    public Users userLogin(String username, String userpwd) {
        //实例化业务持久层类
        UserLoginDao userLoginDao = new UserLoginDaoImp1();
        //调用业务持久层查询方法
        Users users = userLoginDao.SelectUsersByUserNameAndUserPwd(username, userpwd);

        if (users==null){
            throw new UserNotFoundException("用户名或密码有误!");
        }
        return users;
    }
}

创建登录业务的 web 层

创建 Servlet

package com.bjsxt.web.servlet;

import com.bjsxt.exception.UserNotFoundException;
import com.bjsxt.pojo.Users;
import com.bjsxt.service.UserLoginService;
import com.bjsxt.service.UserLoginServiceImp1;

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("/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 {
        //获取参数
        String username = req.getParameter("username");
        String userpwd = req.getParameter("userpwd");
        try {
            UserLoginService userLoginService = new UserLoginServiceImp1();
            Users users = userLoginService.userLogin(username, userpwd);
            //建立客户端服务端会话状态
            HttpSession session = req.getSession();
            session.setAttribute("users",users);
            //使用重定向跳转首页
            resp.sendRedirect("main.jsp");
        } catch (UserNotFoundException e) {
            //users未找到时 请求跳转 通过jsp页面响应错误信息
            req.setAttribute("msg",e.getMessage());
            req.getRequestDispatcher("login.jsp").forward(req,resp);
        }catch (Exception e){
            //使用重定向跳转页面
            resp.sendRedirect("error.jsp");
        }
    }
}

添加登录后的首页

<%--
  Created by IntelliJ IDEA.
  User: 20878
  Date: 2022/1/23
  Time: 17:15
  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>

创建登录业务的Filter

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
 */
@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 {
        HttpServletRequest httpServletRequest = (HttpServletRequest)servletRequest;
        //获取请求信息的url
        StringBuffer url = httpServletRequest.getRequestURL();
        //如果url为登录界面则放行,否则通过session的属性是否含有user判断是否放行
        if (url.indexOf("login.jsp")!=-1||url.indexOf("login.do")!=-1){
            filterChain.doFilter(servletRequest,servletResponse);
        }else {
            HttpSession session = httpServletRequest.getSession();
            Users user = (Users) session.getAttribute(Constants.USER_SESSION_KEY);
            if (user!=null){
                //过滤器通行
                filterChain.doFilter(servletRequest,servletResponse);
            }else {
                httpServletRequest.setAttribute(Constants.REQUEST_MSG,"不登陆不好使!");
                httpServletRequest.getRequestDispatcher("login.jsp").forward(servletRequest,servletResponse);
            }
        }
    }

    @Override
    public void destroy() {

    }
}

用户只能在一处登录

修改处理登录请求的 Servlet

package com.bjsxt.web.servlet;

import com.bjsxt.exception.UserNotFoundException;
import com.bjsxt.pojo.Users;
import com.bjsxt.service.UserLoginService;
import com.bjsxt.service.UserLoginServiceImp1;

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;
@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 {
        //获取参数
        String username = req.getParameter("username");
        String userpwd = req.getParameter("userpwd");
        try {
            UserLoginService userLoginService = new UserLoginServiceImp1();
            Users users = userLoginService.userLogin(username, userpwd);
            //建立客户端服务端会话状态
            HttpSession session = req.getSession();
            session.setAttribute("users",users);
            /**
             * 实现用户只能在一处登录
             */
            //获取全局容器

            ServletContext servletContext = this.getServletContext();
            //全局容器中存放 属性(Userid,session) ,登录之前通过Userid获取session
            // [菜鸟雷区]:同一个用户在不同浏览器的session会不一样
            HttpSession temp = (HttpSession)servletContext.getAttribute(users.getUserid() + "");
            //如果temp不为null,表明用户已经在另一处登录
            if(temp!=null){
                //将servletContext对应属性删除
                servletContext.removeAttribute(users.getUserid()+"");
                //将对应session销毁
                temp.invalidate();
            }
            //然后往 全局容器中添加(Userid,session)属性
            servletContext.setAttribute(users.getUserid()+"",session);
            //使用重定向跳转首页
            resp.sendRedirect("main.jsp");
        } catch (UserNotFoundException e) {
            //users未找到时 请求跳转 通过jsp页面响应错误信息
            req.setAttribute("msg",e.getMessage());
            req.getRequestDispatcher("login.jsp").forward(req,resp);
        }catch (Exception e){
            e.printStackTrace();
            //使用重定向跳转页面
            resp.sendRedirect("error.jsp");
        }
    }
}

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

package com.bjsxt.web.listener;

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

import javax.servlet.ServletContext;
import javax.servlet.annotation.WebFilter;
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) {
        //监听session销毁
        HttpSession session = se.getSession();
        //获取 ServletContext
        ServletContext servletContext = session.getServletContext();
        //获取session的Users属性
        Users users = (Users) session.getAttribute(Constants.USER_SESSION_KEY);
        //删除即将销毁session对应的servletContext,防止二次销毁session报错
        servletContext.removeAttribute(users.getUserid()+"");
    }
}

用户退出登录

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 {
        //退出登录则销毁Session
        HttpSession session = req.getSession();
        session.invalidate();
        //重定向到登录界面
        resp.sendRedirect("login.jsp");
    }
}

添加用户业务

创建添加用户持久层

package com.bjsxt.dao;

import com.bjsxt.pojo.Users;

public interface UserManageDao {
    void insertUser(Users users);
}
package com.bjsxt.dao;

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

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

/**
 * 用户管理持久层
 */

public class UserManageDaoImpl implements UserManageDao {
    @Override
    public void insertUser(Users users) {
        //获取数据库连接
        Connection conn = JdbcUtils.getConnection();
        //
        try {
            PreparedStatement ps = conn.prepareStatement("insert into Users values(default ,?,?,?,?,?,)");
            //关闭事务自动提交(此处代码删除无影响,数据库操作要注意事务)
            conn.setAutoCommit(false);
            ps.setString(1,users.getUsername());
            ps.setString(2,users.getUserpwd());
            ps.setString(3,users.getUsersex());
            ps.setString(4,users.getPhonenumber());
            ps.setString(5,users.getQqnumber());
            ps.execute();
            conn.commit();
        } catch (SQLException e) {
            e.printStackTrace();
            JdbcUtils.rollbackConnection(conn);
        }finally {
            JdbcUtils.closeConnection(conn);
        }
    }
}