实现登录功能
-
步骤1:数据库当中添加一个用户表:t_user
t_user表当中存储的是用户的登录信息,最基本的也包括:登录的用户名和登录的密码。
密码一般在数据库表当中存储的是密文。一般不以明文的形式存储。(这里先使用明文方式。) -
步骤2:再实现一个登录页面。
登录页面上应该有一个登录的表单。有用户名和密码输入的框。
用户点击登录,提交表单,提交用户名和密码。form是post方式提交。 -
步骤3:后台要有一个对应的Servlet来处理登录的请求。
登录成功:跳转到登录成功页面。
登录失败:跳转到失败的页面。
index.jsp页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>欢迎登录</title>
</head>
<body>
<h1>用户登录</h1><hr>
<form action="<%=request.getContextPath()%>/user/login" method="post">
username:<input type="text" name="username"><br>
password:<input type="password" name="password"><br>
<input type="submit" value="login">
</form>
</body>
</html>
UserServlet
@WebServlet("/user/login")
public class UserServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
boolean success = false;
String username = request.getParameter("username");
String password = request.getParameter("password");
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = DButil.getConnection();
String sql = "select * from t_user where loginName=? and loginPwd=?";
ps = conn.prepareStatement(sql);
ps.setString(1,username);
ps.setString(2,password);
rs = ps.executeQuery();
if (rs.next()) {
success = true;
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
DButil.close(conn,ps,rs);
}
if (success) {
response.sendRedirect(""+request.getContextPath()+"/success.html");//跳成功页面
}else{
response.sendRedirect(""+request.getContextPath()+"/fail.html");//跳失败页面
}
}
}
登录功能实现了,目前存在的最大的问题:
如果在后面有一些登录后才能操作的功能,在浏览器地址栏输入http://localhost:8080/项目名/路径,只要知道路径就可以直接不用登录进行操作。
这个登录功能目前只是一个摆设,没有任何作用。只要用户知道后端的请求路径,照样可以在不登录的情况下访问。
为什么?
发现代码中登录只是一种请求,而request的生命周期很短,一次请求一个对象。
HttpServletRequest接口详解
如果用ServletContext保存的话,生命周期又很长,服务器启动时创建,关闭是销毁。
ServletContext应用域详解
会话机制(session)
在HttpServletReauest有一个获取会话的对象方法
public HttpSession getSession();
然后编写代码打印输出到浏览器
HttpSession session = request.getSession();
response.setContentType("text/html,charset=UTF-8");
PrintWriter out = response.getWriter();
out.print(session);
发现只要不关闭浏览器,无论打开多少个窗口,都会输出同一个对象,只要重新打开浏览器重新访问,输出的对象就不是同一个。
会话机制(session):用户打开浏览器,进行一系列操作,然后最终将浏览器关闭,这个整个过程叫做:一次会话。java对象叫做:session。
- session对象是存储在服务端的
- 一个session对象对应一个会话
- 一次会话包含多次请求
在java的servlet规范当中,session对应的类名:HttpSession(jarkata.servlet.http.HttpSession),session机制属于B/S结构的一部分。
为什么需要session对象来保存会话状态呢?
因为HTTP协议是一种无状态协议(请求的时候,B和S是连接的,但是请求结束之后,连接就断了。)。
会话实现原理
有这样一个问题,服务器有很多个session,那怎么保证,用户获取到的是同一个对象,不会获取到另一个用户的session呢?
在web服务器有一个session列表,类似于Map集合
Map集合key存储的是session的id,value存储的是对应的session对象
- 当用户发送第一次请求的时候:服务器会创建一个新的session对象,同时给它生成一个id,然后会将这个id发给浏览器,浏览器会把这个id保存在浏览器缓存中。
- 当用户发送第二次请求的时候:会自动将浏览器缓存中的session的id自动发送服务器,然后服务器根据id就会从session列表中查找对应的session对象。
- 当用户关闭浏览器之后,这个保存的缓存id就消失了,下次重新打开浏览器的之后,服务器自然就找不到session对象,等同于会话结束。
- 当用户关闭浏览器,会话结束,但是服务器里之前的session对象还存在,这时候会有个超时机制,当长时间未操作的时候,会自动销毁session对象。
- 当用户重新打开浏览器时,再次访问,就会生成新的session
超时时长可以在web.xml文件配置,例如配置60分钟
默认时长可以在服务器的web.xml查看
<session-config>
<session-timeout>60</session-timeout>
</session-config>
Cookie
还是上面的代码进行测试,打开浏览器,按F12,查看第一次访问
发现第一次访问,会响应一个Set-Cookie:JSESSIONID=XXX过来,点击刷新,再次发送请求
查看请求,发现会发送一个Cookie:JSESSIONID=XXX过去,且内容与第一次响应过来的一样。
所以JSESSIONID是以Cookie的形式保存在浏览器里面,浏览器只要关闭。这个Cookie就没有了。
Cookie禁用了,session还能找到吗?
- 服务器正常发送cookie给浏览器,但是浏览器拒收了。并不是服务器不发了。
- 找不到了。每一次请求都会获取到新的session对象。
- cookie禁用了,session机制还能实现吗?
- 可以。需要使用URL重写机制。
- http://localhost:8080/项目名/路径;jsessionid=XXX
- URL重写机制会提高开发者的成本。开发人员在编写任何请求路径的时候,后面都要添加一个sessionid,给开发带来了很大的难度,很大的成本。
session会话的常用方法
Object getAttribute(String name)//返回在此会话中绑定了指定名称的对象,或者如果该名称下没有绑定任何对象,则返回该对象null
void setAttribute(String name, Object value)//使用指定的名称将对象绑定到此会话。
void removeAttribute(String name)//从此会话中删除绑定了指定名称的对象。
void invalidate()//结束会话,然后取消绑定到它的任何对象。
改造登录功能
UserServlet
@WebServlet("/user/login")
public class UserServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
boolean success = false;
String username = request.getParameter("username");
String password = request.getParameter("password");
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = DButil.getConnection();
String sql = "select * from t_user where loginName=? and loginPwd=?";
ps = conn.prepareStatement(sql);
ps.setString(1,username);
ps.setString(2,password);
rs = ps.executeQuery();
if (rs.next()) {
success = true;
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
DButil.close(conn,ps,rs);
}
if (success) {
HttpSession session = request.getSession();//获取session,必须获取到
session.setAttribute("username",username);//把用户名存进去
response.sendRedirect(""+request.getContextPath()+"/user/success");//跳到另一个servlet进行处理
}else{
response.sendRedirect(""+request.getContextPath()+"/fail.html");
}
}
}
UserLoginServlet
public class UserLoginServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
HttpSession session = request.getSession(false);
if (session != null && session.getAttribute("username") != null) {//获取到,可以在里面写功能,这里直接就跳到登录成功页面
response.sendRedirect(request.getContextPath()+"/success.html");
}else {
response.sendRedirect(request.getContextPath());//重新跳到登录页面
}
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request,response);
}
}
登录成功之后,可以将用户的登录信息存储到session当中。也就是说session中如果有用户的信息就代表用户登录成功了。session中没有用户信息,表示用户没有登录过。则跳转到登录页面。