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编码之内。