使用Servlet实现用户登录功能,并使用过滤器防止用户未登录直接访问管理员后台页面
作者:互联网
使用Servlet实现用户登录功能,并使用过滤器防止用户未登录直接访问管理员后台页面
1、连接数据库(两种方式)
第一种:读取数据库配置文件,获得数据库连接
(1)创建数据库配置文件(database.properties)
(2)util包创建配置文件管理类(ConfigManage)
package cn.kgc.util; import java.io.IOException; import java.io.InputStream; import java.util.Properties; /** * @author J * 读取数据库配置文件,获取数据库连接信息 * 如何让用户只能创建一个ConfigManage?单例模式!1、把构造方法设为私有 2、程序提供给用户唯一一个对象 * 单例模式的两种实现方式: * 懒汉方式(线程不安全):类名.调用getInstance静态方法时,再new ConfigManage对象,保证只能new一个对象 * 饿汉方式(线程安全):不管调不调用getInstance方法,都只会new一个ConfigManage对象 */ public class ConfigManage { //声明私有的静态ConfigManage对象 单例模式:懒汉方式(线程不安全) private static ConfigManage configManage; //创建私有的静态ConfigManage对象,只要加载ConfigManage类到JVM,就要加载静态代码,就会new ConfigManage对象 单例模式:饿汉方式(线程安全) //private static ConfigManage configManage = new ConfigManage(); //声明私有的properties对象 private Properties properties; /** * 无参构造方法 */ private ConfigManage(){ //定义配置文件的名称 String configFile = "database.properties"; //配置文件通过ConfigManage类加载器的getResourceAsStream方法将资源读到输入流里面 InputStream inputStream = ConfigManage.class.getClassLoader().getResourceAsStream(configFile); //创建Properties类型的对象 properties = new Properties(); try { //输入流加载到properties对象中 properties.load(inputStream); //释放资源 inputStream.close(); } catch (IOException e) { e.printStackTrace(); } } /** * 单例模式:懒汉方式(线程不安全)可加同步锁解决线程不安全问题 * 提供给用户唯一一个ConfigManage对象 * @return * 为什么用static修饰? * 1、如果不用static修饰,用户会new ConfigManage对象调用getString方法 * 但是无参构造定义为私有的,new不了ConfigManage对象,也就调不了getString方法 * 2、用static修饰,静态方法可以类名.直接调用getInstance方法 * 如果ConfigManage对象为空,则new一个ConfigManage对象;如果ConfigManage对象不为空,return返回 * 以此保证用户只能new一个ConfigManage对象 */ public static synchronized ConfigManage getInstance(){ if(configManage == null){ configManage = new ConfigManage(); } return configManage; } /** * 单例模式:饿汉方式(线程安全) * 不管调不调用getInstance方法,都只会new一个ConfigManage对象 * @return */ /*public static ConfigManage getInstance(){ return configManage; }*/ /** * 根据配置文件中的键获得对应的值 * @param key * @return */ public String getString(String key){ return properties.getProperty(key); } }
(3)创建数据库操作基类(BaseDao)获得数据库连接
/** * @author J * 数据库操作基类 */ public class BaseDao { Connection connection = null; PreparedStatement preparedStatement = null; ResultSet resultSet = null; /** * 获得数据库连接 * @return */ public boolean getConnection(){ try { //1、加载MySQL数据库厂商提供的驱动 Class.forName(ConfigManage.getInstance().getString("driver")); //2、建立数据库连接Connection String url = ConfigManage.getInstance().getString("url"); String user = ConfigManage.getInstance().getString("user"); String password = ConfigManage.getInstance().getString("password"); connection= DriverManager.getConnection(url,user,password); } catch (ClassNotFoundException | SQLException e) { e.printStackTrace(); return false; } return true; } /** * 释放资源 先开后关 * @return */ public boolean closeResource(){ if(resultSet != null){ try { resultSet.close(); } catch (SQLException throwables) { throwables.printStackTrace(); return false; } } if(preparedStatement != null){ try { preparedStatement.close(); } catch (SQLException throwables) { throwables.printStackTrace(); return false; } } if(connection != null){ try { connection.close(); } catch (SQLException throwables) { throwables.printStackTrace(); return false; } } return true; } }
第二种:JNDI方式创建数据源(DataSource),获得数据库连接
(1)首先要配置数据源的相关连接信息,也就是数据源连接池。该配置应该在Tomcat安装目录下的conf/context.xml文件中配置。配置如下:
(2)创建数据库基类(BaseDao)获得数据库连接
/** * @author J * 数据库操作基类 */ public class BaseDao { Connection connection = null; PreparedStatement preparedStatement = null; ResultSet resultSet = null; /** * 通过数据源获得数据库连接 * @return */ public boolean getConnection2(){ try { //初始化上下文对象 Context context = new InitialContext(); //获取与逻辑名称相关联的数据源对象 DataSource dataSource = (DataSource) context.lookup("java:comp/env/jdbc/news"); //通过数据源获取数据库连接 connection = dataSource.getConnection(); } catch (NamingException | SQLException e) { e.printStackTrace(); } return true; } /** * 释放资源 先开后关 * @return */ public boolean closeResource(){ if(resultSet != null){ try { resultSet.close(); } catch (SQLException throwables) { throwables.printStackTrace(); return false; } } if(preparedStatement != null){ try { preparedStatement.close(); } catch (SQLException throwables) { throwables.printStackTrace(); return false; } } if(connection != null){ try { connection.close(); } catch (SQLException throwables) { throwables.printStackTrace(); return false; } } return true; } }
2、pojo包编写用户信息的实体类
package cn.kgc.pojo; /** * @author J * @Date 2021/10/9 2:19 * 封装用户信息的实体类(也就是封装数据的JavaBean) */ public class User { /** * 定义私有的成员属性(对应数据表news_user中的字段) */ private String userName ; private String password; /** * 创建公有的getter/setter方法,用于属性的读写 * @return */ public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }
3、编写公用的查询方法(BaseDao)
/** * 查询方法 * @param sql 因为查询要有SQL语句,所以将SQL语句作为参数 * @param params 因为不确定SQL语句中占位符?的个数,所以将数组params作为参数;不确定填充占位符?的参数类型,所以数组类型为Object类型 * @return 返回值类型是ResultSet 结果集 */ public ResultSet executeQuery(String sql,Object[] params){ //获得数据库连接 if(this.getConnection()){ try { //创建PreparedStatement对象发送SQL语句到数据库执行 preparedStatement = connection.prepareStatement(sql); //for循环遍历params数组 数组长度=占位符的个数 for(int i = 0; i < params.length; i++){ //填充占位符? key,value 在第几个?,数组下标为几的值 preparedStatement.setObject(i+1,params[i]); } //获得ResultSet结果集 resultSet = preparedStatement.executeQuery(); } catch (SQLException throwables) { throwables.printStackTrace(); } } return resultSet; }
4、编写用户信息数据操作的接口(UserDao)
package cn.kgc.dao; import cn.kgc.pojo.User; /** * @author J * @Date 2021/10/9 10:51 * 用户信息数据操作的接口 */ public interface UserDao { /** * 根据用户名和去数据库查询有没有该用户 * @param userName * @return */ public User getUserByInfo(String userName); }
5、实现用户信息数据操作接口(UserDaoImpl)
package cn.kgc.dao.impl; import cn.kgc.dao.BaseDao; import cn.kgc.dao.UserDao; import cn.kgc.pojo.User; import java.sql.ResultSet; /** * @author J * @Date 2021/10/9 22:15 * 用户信息操作接口的实现类 */ public class UserDaoImpl extends BaseDao implements UserDao { /** * 根据用户名和密码去数据库查询有没有该用户 * @param userName * @return */ @Override public User getUserByInfo(String userName) { //创建User对象 User user = new User(); try { //编写SQL语句 String sql = "SELECT * FROM news_user WHERE userName = ?"; //定义Object类型的数组Params并将参数赋值 Object[] params = {userName}; //调用executeQuery()方法传入参数(sql语句,params数组) 返回results结果集 ResultSet resultSet = this.executeQuery(sql,params); //处理执行结果 while (resultSet.next()){ //把在数据库查询到的用户名和密码赋值给user对象 user.setUserName(resultSet.getString("userName")); user.setPassword(resultSet.getString("password")); } } catch (Exception exception) { exception.printStackTrace(); } finally { //释放资源 this.closeResource(); } return user; } }
6、编写用户信息业务逻辑操作的接口(UserService)
package cn.kgc.service; import cn.kgc.pojo.User; /** * @author J * @Date 2021/10/9 23:19 * 用户信息业务逻辑操作的接口 */ public interface UserService { /** * 登录判断 * @param userName * @param password * @return */ public int judgeLogin(String userName,String password); }
7、实现用户信息业务逻辑操作接口(UserServiceImpl)
package cn.kgc.service.impl; import cn.kgc.dao.UserDao; import cn.kgc.dao.impl.UserDaoImpl; import cn.kgc.pojo.User; import cn.kgc.service.UserService; /** * @author J * @Date 2021/10/9 23:23 * 用户信息业务逻辑操作接口的实现类 */ public class UserServiceImpl implements UserService { /** * 登录判断 * @param userName * @param password * @return */ @Override public int judgeLogin(String userName, String password) { //初始化登录失败 int i = 0; //如果用户名和密码是空/NULL 登录失败 if(userName == "" || userName == null || password == "" || password == null){ return i; } //实例化UserDao接口的实现类 UserDao userDao = new UserDaoImpl(); //调用userDao.getUserByInfo()方法,将表单提交的userName作为参数,返回user对象 User user = userDao.getUserByInfo(userName); //判断:如果表单提交的密码与user对象中的密码不相同,登录失败;否则登录成功。 if(!password.equals(user.getPassword())){ i = 0; }else { i = 1; } return i; } }
8、编写处理用户登录的Servlet
package cn.kgc.web.servlet; import cn.kgc.pojo.News; import cn.kgc.pojo.User; import cn.kgc.service.NewsService; import cn.kgc.service.UserService; import cn.kgc.service.impl.NewsServiceImpl; import cn.kgc.service.impl.UserServiceImpl; import org.apache.commons.fileupload.FileItem; import org.apache.commons.fileupload.FileItemFactory; import org.apache.commons.fileupload.disk.DiskFileItemFactory; import org.apache.commons.fileupload.servlet.ServletFileUpload; 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 java.io.File; import java.io.IOException; import java.util.Date; import java.util.Iterator; import java.util.List; /** * 处理用户登录的Servlet: * 接收用户表单提交的用户名和密码,去数据库查有没有这个用户。如果有,跳转到首页。如果没有,跳转到登录失败页面。 * * Servlet:接收请求,调用JavaBean处理请求 */ @WebServlet(name = "AddServlet") public class LoginServlet extends HttpServlet { /** * 处理Post请求 * @param request * @param response * @throws ServletException * @throws IOException */ protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //实例化User对象 User user = new User(); //获取用户表单提交的用户名、密码 String userName = request.getParameter("userCode"); String password = request.getParameter("userPassword"); //实例化userService接口的实现类 UserService userService = new UserServiceImpl(); //调用userService.judgeLogin()方法,将表单提交的用户名和密码作为参数,返回int类型的值 int i = userService.judgeLogin(userName,password); //若i>0,则登录成功;否则登录失败。 if(i > 0){ System.out.println("登录成功!"); //将表单提交的用户名和密码赋值给user对象 user.setUserName(userName); user.setPassword(password); //把use对象放进session作用域里面,考虑到其他页面也可能用到用户信息 request.getSession().setAttribute("user",user); //重定向跳转到首页 绝对路径 相对于整个Web应用 response.sendRedirect("/news/jsp/admin/admin.jsp"); } else { System.out.println("登录失败!请点击返回跳转到登录页面"); //重定向跳转到登录失败页面 response.sendRedirect("/news/jsp/error.jsp"); } } /** * 处理Get请求 * @param request * @param response * @throws ServletException * @throws IOException */ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //不管是Get请求还是Post请求,Servlet针对一个请求只做一个响应。如果是Get请求,调用doPost()方法,如果是Post请求,直接进doPost()方法 this.doPost(request,response); } }
9、web.xml中配置LoginServlet
<!--创建LoginServlet实例--> <servlet> <servlet-name>LoginServlet</servlet-name> <servlet-class>cn.kgc.web.servlet.LoginServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>LoginServlet</servlet-name> <url-pattern>/servlet/LoginServlet</url-pattern> </servlet-mapping>
10、前台登录页面(login.jsp)
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Insert title here</title> </head> <script type="text/javascript"> function validate(){ //验证 var userCode = document.getElementById("userCode").value; var userPassword = document.getElementById("userPassword").value; var userCodeSpan = document.getElementById("userCodeSpan"); var userPasswordSpan = document.getElementById("userPasswordSpan"); var flag = true; if(userCode == null || userCode == ''){ userCodeSpan.innerHTML = "请输入用户名"; flag = false; } if(userPassword == null || userPassword == ''){ userPasswordSpan.innerHTML = "请输入密码"; flag = false; } //提交 var actionForm = document.getElementById("actionForm"); if(flag){ actionForm.submit(); } } </script> <body> <form action="${pageContext.request.contextPath }/servlet/LoginServlet" name="actionForm" id="actionForm" method="post" > <dl> <dt>用户名:</dt> <dd><input type="text" id="userCode" name="userCode"/> <span id="userCodeSpan"></span> </dd> <dt>密 码:</dt> <dd><input type="password" id="userPassword" name="userPassword"/><span id="userPasswordSpan"></span></dd> </dl> <div class="buttons"> ${error } <input type="button" value="登录系统" onclick="validate();" /> <input type="reset" value="重 填" class="input-button" /> </div> </form> </body> </html>
11、启动服务器,浏览器访问http://localhost:8080/news/jsp/login.jsp 进入登录页面
如果直接点登录,前台JavaScript非空验证提醒
若表单输入的用户名和密码与数据库中news_user表的数据不匹配,则登录失败进入error.jsp,点击返回跳转到登录页面
若表单输入的用户名和密码与数据库中news_user表的数据匹配,登录成功,跳转到首页
截至目前,登录功能已完成。
存在问题:如果用户不登录而直接访问http://localhost:8080/news/jsp/admin/admin.jsp管理员页面呢?是可以直接访问到,但如何避免这种情况的发生?
回答:使用过滤器防止用户未登录直接访问管理员后台页面
1、编写用户登录的过滤器(LoginFilter)
package cn.kgc.web.filter; import cn.kgc.pojo.User; import javax.servlet.*; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * @author J * @Date 2021/10/10 3:17 * 用户登录的过滤器 */ public class LoginFilter implements Filter { /** * 初始化方法 * @param filterConfig * @throws ServletException */ @Override public void init(FilterConfig filterConfig) throws ServletException { } /** * 销毁方法 */ @Override public void destroy() { } /** * 实现过滤行为 * @param servletRequest * @param servletResponse * @param filterChain * @throws IOException * @throws ServletException */ @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { //判断用户是否登录-->判断session中是否有用户 //首先将servletRequest和servletResponse强转为HttpServletRequest和HttpServletResponse HttpServletRequest httpServletRequest = (HttpServletRequest)servletRequest; HttpServletResponse httpServletResponse = (HttpServletResponse)servletResponse; //获得当前request中的HttpSession,通过建(user)拿到对应的值,返回值类型是Object类型。将Object类型强转为User类型 User user = (User)httpServletRequest.getSession().getAttribute("user"); //判断:若对象为null,重定向跳转到error页面,绝对路径。若对象不为null,调用下一个过滤器或Web资源 if(user == null){ httpServletResponse.sendRedirect("/news/jsp/error.jsp"); } else { filterChain.doFilter(servletRequest,servletResponse); } } }
2、web.xml中配置LoginFilter
<!--配置LoginFilter--> <filter> <!--过滤器名--> <filter-name>LoginFilter</filter-name> <!--过滤器的完全限定名--> <filter-class>cn.kgc.web.filter.LoginFilter</filter-class> </filter> <filter-mapping> <!--过滤器映射名--> <filter-name>LoginFilter</filter-name> <!--过滤器映射的Web资源--> <url-pattern>/jsp/admin/*</url-pattern> </filter-mapping>
3、重新启动服务,浏览器直接访问http://localhost:8080/news/jsp/admin/admin.jsp管理员页面,就会重定向跳转到失败页面(error.jsp),解决了用户未登录直接访问管理员后台页面的问题。若用户输入正确的用户名和密码,则可以进入系统的首页。
标签:ConfigManage,return,cn,登录,用户,kgc,import,Servlet,public 来源: https://www.cnblogs.com/mimi123118/p/15388442.html