1. 利用session完成用户登录

实现步骤:

  1. 实现两个页面,首页、登录页面
  2. 实现登录、退出的Servlet,LoginServlet、LoginOutServlet
  3. 实现一个用户类
  4. 实现一个DB类,模拟数据库提供用户的信息

目录结构:

java session 初始化 session javaweb_java

 

首页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>

实现效果:

java session 初始化 session javaweb_html_02

 2. 利用Session防止表单重复提交

表单重复提交3种方式:

  1. 多次点击提交
  2. 提交后再次刷新页面,重复提交
  3. 提交后返回,重新提交

java session 初始化 session javaweb_java session 初始化_03

 

使用js表单提交后,让提交按钮灰化,可以解决第一种重复提交方式,第二、三种方式需要在服务器中处理。

服务端防止表单重复提交的方式:
表单提交的时候带一个随机数(令牌),在服务器端也存储这个随机数,在服务器看这个表单有没有提交过,如果没有提交过(随机数相同)则提交,然后服务器端把这个随机数删除掉,在提交的时候服务端就没有了,就阻止提交。

项目完整目录结构:

java session 初始化 session javaweb_html_04

 

首页:

<%@ 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>

实现效果:

java session 初始化 session javaweb_xml_05

 

附:

base64编码:将3个字节转换成4个字节(24位 -> 32位 )。转换后4个字节每个字节只有6位,所以高位补两个0,取值范围是0--63(2^6-1),然后对每个值进行编码,只需要64个编码字符即可,所以叫base64编码。
传递文件时一般都要指定开始和结束符,在开始和结束符中间的才是传输的数据,如果传递的文件中就包含了结束符,那么文件传送一半就会终止,导致文件缺损。所以一般情况下文件内容都用base64编码,而开始和结束符都不包含在base64编码之内。