1. 利用session完成用户登录
实现步骤:
- 实现两个页面,首页、登录页面
- 实现登录、退出的Servlet,LoginServlet、LoginOutServlet
- 实现一个用户类
- 实现一个DB类,模拟数据库提供用户的信息
目录结构:
首页index.jsp:
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>首页</title> </head> <body> <%--EL表达式,用于获取当前登录的用户--%> 欢迎您:${user.username} <a href="/login.html">登录</a> <a href="/LogoutServlet">退出登录</a> </body> </html>
登录页面login.html:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>登录</title> </head> <body> <form action="/LoginServlet" method="post"> 用户名:<input type="text" name="username"><br> 密码:<input type="password" name="password"><br> <input type="submit" value="提交"> </form> </body> </html>
登录Servlet:
package com.servlet; import com.db.DB; import com.domain.User; 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.IOException; import java.io.PrintWriter; import java.util.List; @WebServlet(name = "LoginServlet") public class LoginServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String username = request.getParameter("username"); String password = request.getParameter("password"); List<User> users = DB.getUsers(); for (User user : users) { if (username.equals(user.getUsername()) && password.equals(user.getPassword())) { // 将当前用户存储在session中,可以在首页中使用EL表达式获取当前登录的用户名 request.getSession().setAttribute("user", user); response.sendRedirect("/index.jsp"); return; } } request.setCharacterEncoding("utf-8"); response.setContentType("text/html;charset=utf-8"); PrintWriter out = response.getWriter(); out.write("用户名或密码错误"); } }
退出Servlet:
package com.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(name = "LogoutServlet") public class LogoutServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { HttpSession session = request.getSession(false); if (session != null) { session.removeAttribute("user"); } response.sendRedirect("/index.jsp"); return; } }
用户类:
package com.domain; public class User { private String username; private String password; public User(String username, String password) { this.username = username; this.password = password; } 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; } }
模拟数据库:
package com.db; import com.domain.User; import java.util.ArrayList; import java.util.List; /** * 模拟数据库 */ public class DB { private static List<User> users = new ArrayList<>(); static { users.add(new User("aaa", "123")); users.add(new User("bbb", "123")); users.add(new User("ccc", "123")); } public static List<User> getUsers() { return users; } }
web.xml:
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <servlet> <servlet-name>LoginServlet</servlet-name> <servlet-class>com.servlet.LoginServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>LoginServlet</servlet-name> <url-pattern>/LoginServlet</url-pattern> </servlet-mapping> <servlet> <servlet-name>LogoutServlet</servlet-name> <servlet-class>com.servlet.LogoutServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>LogoutServlet</servlet-name> <url-pattern>/LogoutServlet</url-pattern> </servlet-mapping> </web-app>
实现效果:
2. 利用Session防止表单重复提交
表单重复提交3种方式:
- 多次点击提交
- 提交后再次刷新页面,重复提交
- 提交后返回,重新提交
使用js表单提交后,让提交按钮灰化,可以解决第一种重复提交方式,第二、三种方式需要在服务器中处理。
服务端防止表单重复提交的方式:
表单提交的时候带一个随机数(令牌),在服务器端也存储这个随机数,在服务器看这个表单有没有提交过,如果没有提交过(随机数相同)则提交,然后服务器端把这个随机数删除掉,在提交的时候服务端就没有了,就阻止提交。
项目完整目录结构:
首页:
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>首页</title> </head> <body> <a href="/FormServlet">注册</a> </body> </html>
表单页,使用js处理,表单提交后,使提交按钮灰化:
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>表单提交</title> </head> <body> <form action="/DoFormServlet" method="post" onsubmit="return doSubmit()"> <input type="hidden" name="token" value="${token}"> 用户名:<input type="text" name="username"><br> <input id="submit" type="submit"> </form> </body> <script> function doSubmit() { document.getElementById("submit").disabled = "disabled"; return true; } </script> </html>
令牌生成器:
package com.util; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Base64; import java.util.Random; /** * 令牌发生器,为了保证是唯一的,使用单例模式 * 1. 构造方法私有 * 2. 自己创建一个实例 * 3. 暴露一个方法,允许外部获取创建的对象 */ public class TokenProcessor { private static final TokenProcessor instance = new TokenProcessor(); private TokenProcessor() { } public static TokenProcessor getInstance() { return instance; } public String generateToke() { // 这个生成的随机数,长度不固定 String token = System.currentTimeMillis() + new Random().nextInt() + ""; try { // 得到数据摘要,128位(16字节) MessageDigest md = MessageDigest.getInstance("md5"); // 任意的二进制字节数组,如果直接转化成字符串的话,可能会出现乱码 byte[] md5 = md.digest(token.getBytes()); // 使用base64编码 Base64.Encoder encoder = Base64.getEncoder(); return encoder.encodeToString(md5); } catch (NoSuchAlgorithmException e) { throw new RuntimeException(e); } } }
生成表单:
package com.servlet; import com.util.TokenProcessor; 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.IOException; // 产生表单,转发到JSP @WebServlet(name = "FormServlet") public class FormServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request,response); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String token = TokenProcessor.getInstance().generateToke(); request.getSession().setAttribute("token", token); request.getRequestDispatcher("/form.jsp").forward(request, response); } }
表单校验:
package com.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 java.io.IOException; @WebServlet(name = "DoFormServlet") public class DoFormServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try { Thread.sleep(1000 * 3); } catch (InterruptedException e) { e.printStackTrace(); } if (!isTokenVailed(request)) { System.out.println("请不要重复提交"); return; } System.out.println("向数据库中插入数据"); } private boolean isTokenVailed(HttpServletRequest request) { String clientToken = request.getParameter("token"); if (clientToken == null) { return false; } String serviceToke = (String) request.getSession().getAttribute("token"); if (serviceToke == null || !serviceToke.equals(clientToken)) { return false; } request.getSession().removeAttribute("token"); return true; } }
web.xml:
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <servlet> <servlet-name>FormServlet</servlet-name> <servlet-class>com.servlet.FormServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>FormServlet</servlet-name> <url-pattern>/FormServlet</url-pattern> </servlet-mapping> <servlet> <servlet-name>DoFormServlet</servlet-name> <servlet-class>com.servlet.DoFormServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>DoFormServlet</servlet-name> <url-pattern>/DoFormServlet</url-pattern> </servlet-mapping> </web-app>
实现效果:
附:
base64编码:将3个字节转换成4个字节(24位 -> 32位 )。转换后4个字节每个字节只有6位,所以高位补两个0,取值范围是0--63(2^6-1),然后对每个值进行编码,只需要64个编码字符即可,所以叫base64编码。
传递文件时一般都要指定开始和结束符,在开始和结束符中间的才是传输的数据,如果传递的文件中就包含了结束符,那么文件传送一半就会终止,导致文件缺损。所以一般情况下文件内容都用base64编码,而开始和结束符都不包含在base64编码之内。