Cookie的由来(浏览器端的会话技术——保存数据技术)

Cookie的需求分析

场景:
Cookie和Session - 图1
现象:进入淘宝的登陆页面,显示了我上次登录的用户名
显示上次登录用户名这个功能,如何实现?
1、上一次登录的时候,肯定将用户名记录下来
2、肯定是在服务器处理登录的时候,将用户名记录下来
3、那么这个数据到底存在哪里了呢?
Cookie和Session - 图2

Cookie介绍&Cookie案例

Cookie快速入门(创建Cookie对象,发送给浏览器;服务器接收请求中cookie数据)

Cookie和Session - 图3
1、如何创建Cookie对象?查询构造函数
Cookie和Session - 图4
2、在哪里创建Cookie对象?根据之前的分析,记住用户名的动作发生在服务器端,所以在服务器创建Cookie
Cookie和Session - 图5
3、如何将cookie发送给浏览器?给浏览器发出响应的是response对象,将cookie交给response发送
Response API:
Cookie和Session - 图6
代码操作:
Cookie和Session - 图7

  1. // 调用构造函数,创建Cookie对象
  2. Cookie cookie = new Cookie("username", "hehe");
  3. // 通过响应对象将cookie发送给浏览器
  4. response.addCookie(cookie);

效果:
Cookie和Session - 图8
4、如何在浏览器查看cookie(第二种查看cookie的方式)?
Cookie和Session - 图9
Cookie和Session - 图10
Cookie和Session - 图11
Cookie和Session - 图12
Cookie和Session - 图13
ok了

那么接下来,记录在cookie中的数据,服务器如何使用?
1、浏览器将cookie发送给服务器需要我们有特别的操作吗?不需要,浏览器发送http请求会自动带上cookie
2、浏览器自动发送cookie给服务器,服务器如何获取到这个cookie?通过request对象获取

Request API:
Cookie和Session - 图14
代码演示:

// 获取cookie数组(因为在response发送cookie的时候,就可以发送多个)
Cookie[] cookies = request.getCookies();
// 循环判断cookie,与指定用户名一致,就取出
for(Cookie cookie : cookies) {
    // cookie.getName() 获取cookie的名称
    if(cookie.getName().equals("username")) {
        // 取出cookie的值
        String value = cookie.getValue();
        System.out.println(value);
    }
}

效果:
Cookie和Session - 图15
补充:通过浏览器开发者工具,查看cookie
Cookie和Session - 图16

Cookie的生命周期

问题1: cookie可以记录数据,但是数据需要一直记录永远不删除吗?
答:数据不删除,数据量越来越大,影响存储空间,数据量越大网络传输cookie慢,服务器解析cookie,也是效率不高。
问题2:那么如何删除cookie数据?
答:我们在服务器创建cookie给浏览器,但是我们无法操作浏览器,因此,在cookie创建的时候设置cookie的生存时间,时间一到自动消失

如果不设置过期时间,默认是多少?
答:浏览会话结束时——浏览器关闭的时候

Cookie API:
Cookie和Session - 图17
代码演示:

// 调用构造函数,创建Cookie对象
Cookie cookie = new Cookie("username222", "haha");
// 设置生存时间,单位秒
cookie.setMaxAge(30);
// 通过响应对象将cookie发送给浏览器
response.addCookie(cookie);

效果:
数据确实发送了
Cookie和Session - 图18
30秒后:username222消失了
Cookie和Session - 图19
chrome://settings/content/cookies——打开查看谷歌浏览器cookie

Cookie的路径设置

现象:
第一张图:
Cookie和Session - 图20
第二张图:
Cookie和Session - 图21
问题:cookie认路!!!为什么cookie认路?
Cookie和Session - 图22
那么这个默认的操作,调用的是哪个API呢?
Cookie和Session - 图23
测试setPath方法一:同级别的不同目录是否可以获取cookie?
Cookie和Session - 图24
效果:获取不到cookie
访问demo2
Cookie和Session - 图25
Cookie和Session - 图26
测试setPath方法二:当前目录的子目录是否可以获取数据?
设置成子目录:
Cookie和Session - 图27
测试效果:
Cookie和Session - 图28
Cookie和Session - 图29
结论:setPath方法可以设置当前目录和旗下子目录servlet都获取cookie,一般setPath(“/”),表示当前项目所有目录都可以获取cookie
一般路径不去设置,或者,设置为“/”;

删除Cookie(马上消失)

删除cookie其实是发送一个新的cookie,设置生成时间为0,而且设置数据为空字符或则null,通过response对象发送之后,会覆盖之前的cookie
注意,删除cookie时,path必须一致,否则不会删除
这个过程就是: 服务器通知浏览器删除自己管理的cookie.
1、将cookie的name(key)保持一致,value 设置为 “”;
cookie = new Cookie(“username”,””)
2、设置存活时间为0,
cookie.setMaxAge(0)
3、路径要发送cookie时保持一致,没有路径不需要设置。
cookie.setPath(“/“);
4、将cookie发送给浏览器。
response.addCookie(cookie)

需求:服务器让浏览器删除缓存中的cookie:
Cookie和Session - 图30

效果:再次获取时为空
Cookie和Session - 图31

cookie案例一:显示用户上次访问的时间

当用户访问某些web应用的时候,经常会显示出上次的访问时间。
例如:QQ登录成功后,会显示用户上次的登录时间。
画图分析:
Cookie和Session - 图32
代码演示:

// 分两次请求
//定义日期规则字符串
DateFormat format = new SimpleDateFormat("yyyy年MM月dd日HH:mm:ss");
String dateStr = format.format(new Date());

Cookie cookie = new Cookie("time", dateStr);
// 第一次将访问时间,保存到cookie
cookie.setMaxAge(60*60*24*7);
cookie.setPath("/");
response.addCookie(cookie);
// 第二次,获取上一次访问的cookie
Cookie[] cookies = request.getCookies();
if(cookies != null) {
    for(Cookie cookie2 : cookies) {
        // 判断当前的cookie是表示上一次的访问时间,才获取
        if(cookie2.getName().equals("time")) {
            // 将数据展示到页面
            response.setCharacterEncoding("utf-8");
            response.getWriter().write("<html><head><meta charset='utf-8' /></head><body>" + cookie2.getValue() + "</body></html>");
        }
    }
}

效果:
Cookie和Session - 图33

Session(服务器端的会话技术)

Cookie技术可以将用户的信息保存在各自的浏览器中,
优点:很明显实现了同一个用户不同请求中数据共享。
缺点:黑客可以利用脚本等手段窃取cookie中的重要数据,从而泄漏个人的隐私,存在巨大的安全隐患。

Cookie数据保存在用户电脑中,不是每个用户都是电脑高手,对数据安全措施,做的必然不完善。

诞生新技术:session,在服务器端保存用户的数据。(注意:session技术,还是依赖cookie技术)

会话:从第一次请求服务器开始,一直到关闭浏览器,这一段操作,称之为:会话。相当于平时打电话。

什么是session?(重点)

场景:
客户到KFC买东西,点了劲脆鸡腿堡、老北京鸡肉卷、可乐,服务员将三个菜品一次放入打包盒,然后让你带走。

如果使用Cookie解决需求:
使用三个cookie保存数据——但是有问题:cookie数量增多网络传输慢,解析慢。

最好的方式:
1、需要一个容器来存放数据
2、这个容器必然存在服务器
3、每个用户,需要分配一个容器

那么到底这个容器,在功能实现过程中,是怎么运行的?

这个技术,就是session:存储用户多次请求过程中的数据

Session技术原理图: 跟cookie有区别——cookie存数据(用户名称,用户密码:身份验证数据) session存数据(普通业务内容),
Cookie和Session - 图34
总结:
1、session是服务器开辟的一个用来存储数据的空间
2、服务器为每个浏览器单独开辟一个session
3、服务器根据浏览器发送过来的cookie,来确认当前浏览器使用哪个session

补充说明:

  • 在WEB开发中,服务器可以为每个用户浏览器创建一个会话对象(session对象),注意:一个浏览器独占一个session对象(默认情况下)。因此,在需要保存用户数据时,服务器程序可以把用户数据写到用户浏览器独占的session中,当用户使用浏览器访问其它程序时,其它程序可以从用户的session中取出该用户的数据,为用户服务。
  • Session和Cookie的主要区别在于:

Cookie是把用户的数据写给用户的浏览器。
Session技术把用户的数据写到用户独占的session中(服务器端)。

  • Session对象由服务器创建,开发人员可以调用request对象的getSession方法得到session对象。

    Session的快速入门

    获取Session对象

    学习第一步:获取session对象
    Cookie和Session - 图35
    人话:提供了一个容器,将多次请求中的数据,可以都存储在这个容器中。

接下来,按照面向对象的思路,查询构造函数,创建对象。但是查阅API发现,没有构造函数使用——注意:httpsession对象,有服务器自己创建,不需要程序员手动操作!!!

需要通过一种方式,获取httpSession对象,通过一个对象,他的方法,获取httpSession

通过request对象来获取session对象: 为什么必须通过request来获取session
Cookie和Session - 图36
第一个空参数:
Cookie和Session - 图37
人话:获取当前服务器中给浏览器开辟的Session容器,如果以前没有,创建一个新的给你,如果以前有了,将以前的Session给你

第二个带参数:
Cookie和Session - 图38
人话:如果这个函数参数为true和前面的getSession(),效果一致,如果参数为false,并且,以前没有Session对象,那么返回null。
Cookie和Session - 图39
代码演示:使用API

// 获取session对象的操作
HttpSession session = request.getSession();
System.out.println(session);

效果:
Cookie和Session - 图40
补充说明:
Session是基于用户的请求,而把用户的重要信息在服务器端针对这个用户(浏览器)创建了一个容器。
而这个Session容器是由web服务器(tomcat)帮助我们创建的,在程序中我们只能去获取到这个容器,然后给容器添加数据或者取出数据,或者删除数据,而我们是不能创建这个容器对象。

在Session中存取删数据(重点)

查询HttpSession对象中存数据,取数据,删数据的API

存数据:
Cookie和Session - 图41
Cookie和Session - 图42
人话:指定一个对象保存到会话中,name参数,数据的名称,value参数:具体的值

取数据:
Cookie和Session - 图43
人话:获取指定名称的数据,如果没有,返回null

删数据:
Cookie和Session - 图44
人话:删除Session容器中的数据,删除根据指定的名称(name参数),如果没有数据了,不做任何处理。

演示存取删操作:

package cn.igeek.web;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

public class SessionServlet extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //演示获取session对象(容器,用来保存数据)
        HttpSession session = request.getSession();
        //org.apache.catalina.session.StandardSessionFacade@4838bd9d : 这个是httpsession接口的实现类,这个类由tomcat实现
        System.out.println(session);
        //设置数据的方法
        /*session.setAttribute("addr", "法国");
        //获取数据的方法
        String addr = (String)session.getAttribute("addr");
        System.out.println(addr);
        //删除数据的方法
        session.removeAttribute("addr");
        String addr1 = (String)session.getAttribute("addr");
        System.out.println(addr1);*/
    }

    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

}

记录sessionid的cookie

通过谷歌浏览器的工具查看,SessionId的cookie:
Cookie和Session - 图45
这个cookie明显是服务器创建的,那么是在哪里创建的呢?
测试前先清空cookie数据!

创建记录sessionid的cookie由tomcat中:org.apache.catalina.core.ApplicationSessionCookieConfig
Cookie和Session - 图46

测试:关闭浏览器之后,session对象,还是同一个吗?

第一次访问:
org.apache.catalina.session.StandardSessionFacade@5e7eb260
Cookie和Session - 图47
关闭浏览器,再次访问:
org.apache.catalina.session.StandardSessionFacade@4107e577
Cookie和Session - 图48
问题:为什么,关闭浏览器之后,session对象就换了一个?

结论:关闭浏览器之后,重新访问项目,被分配一个新的session对象,原因——用来寻找session对象的cookie已经不存在了,随着浏览器关闭消失了。

需求:关闭浏览器之后,还要之前的session,如何实现呢?
解决方案:自己创建一个cookie,要求被浏览器持久化保存起来(setMaxAge(10000))放便后期使用
代码演示自己手动持久化cookie:
要使用的API:
Cookie和Session - 图49
人话:获取session的id。

package cn.igeek.web;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

public class SessionServlet2 extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        HttpSession session = request.getSession();
        System.out.println(session);
        //自己创建一个cookie,要求被浏览器持久化保存起来(setMaxAge(10000))放便后期使用
        Cookie cookie = new Cookie("JSESSIONID", session.getId());
        //活的久一点
        //cookie.setMaxAge(10000);
        cookie.setPath("/");
        response.addCookie(cookie);
    }

    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

}

效果:
Cookie和Session - 图50
效果二:
Cookie和Session - 图51
总结:session容器的获取全部依赖于cookie,服务器自动解析cookie,根据cookie中jsessionid,获取指定的容器对象。

一般情况下,关闭浏览器之后,再次访问,是无法获取到Session中的数据的。
因此在服务器针对当前用户的第一次请求创建的唯一的Session容器对象,而在给这次请求的之后,服务器需要给用户响应数据,在响应的时候,服务器把当前Session容器对象的JSESSIONID以Cookie的形式发送给了浏览器。而这个Cookie并没有设置有效时间,那么这个Cookie就属于临时Cookie,在关闭浏览器之后,再次打开浏览器的时候,上次的Cookie已经消失了,用户虽然拿同一个浏览器访问服务器,可是这时没有JSESSIONID,服务器端的Session容器依然存在,但是没有JSESSIONID,服务器内部无法获取到这个session对象
把包含了JSESSIONID的Cookie在客户端持久化。

Session的生命周期(面试)

使用session存取数据,必须在session对象存活的时候,才可以使用,因此,有必要学习session生命周期(什么时候生,什么时候消失)

Session对象的创建时间:
当第一次调用request.getSession()的时候创建session容器.
如果第一次访问jsp页面,也会创建session容器

Session的销毁时间:
1、(自动消失)Session在服务器的存活时间,Session对象在服务器内部有默认的存活的时间,一般默认是30分钟。如果在30分钟内,用户没有再对当前这个服务器中的资源进行任何的访问操作,这时只要时间到达30分钟,服务器会自动的销毁这个session。
Session的存活时间我们可以在当前这个项目的web.xml中配置:
Cookie和Session - 图52
2、在程序执行中,手动销毁session容器, 使用 session.invalidate()(重点)
Cookie和Session - 图53
人话:销毁session对象,只是让这个对象无效,下次在来访问,给一个新的session对象。

两次调用同一个servlet测试,有销毁方法,获取到不同的session对象
Cookie和Session - 图54
3、不正常关闭服务器。直接点击控制台的红点,强制关闭tomcat
如果是正常关闭服务器,这时服务器内部会使用IO流中的序列化技术把这个Session对象保存在tomcat/work目录下面。Session文件:
Cookie和Session - 图55

Session应用:登陆验证码

什么是登陆验证吗?
防止黑客暴力破解用户的密码的一种技术。
什么是暴力破解用户的密码?
Cookie和Session - 图56
黑客这样的暴力破解,有什么办法解决呢?验证码
Cookie和Session - 图57
需求:在登录的时候,加上验证码校验

页面准备:

<!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>login</title>
<script type="text/javascript">

    function changeCode(){
        //设置src属性的地址如果和前一次相同,默认从浏览器缓存的图片中获取数据,不再发请求到浏览器
        //第一:地址不能变
        //第二:必须让浏览器,察觉地址的一些内容,产生了变化
        //第三:地址最后的参数,最好,每次都发生变化
        //Date();获取当前的时间
        //var date = new Date();
        //获取当前时间的毫秒值
        //var time = date.getMilliseconds()
        document.getElementById("img").src = "/study/getCode?r="+new Date().getTime();
        /**
            为什么要使用验证码?

            登陆功能——username password ——》登陆

            用户名密码暴力破解:黑客有一个密码库
            (一个数据库,存储了普通用户一般会使用的常见密码组合,甚至,将键盘上所有的字符组合通过遍历,一个一个存储到数据库)

            当黑客知道你的用户名之后,(cookie,可能被黑客利用,获取用户名)
            使用密码库中的密码,和用户的用户名组合,开始破解,用户的登陆

            解决方案:限制黑客暴力破解密码,使用一个随机生成的验证码,用户登陆,必须,手动输入,验证码的内容,才允许执行登陆


        */
    }

</script>
</head>
<body>
<form action="/study/login" method="post">
<table>
    <tr><td>用户名:</td><td><input type="text" name="username"></td></tr>
    <tr><td>密码:</td><td><input type="password" name="password"></td></tr>
    <tr><td>验证码:</td><td><input type="text" name="yanzhengma"></td></tr>
    <!-- 通过向服务器发送请求,从服务器获取验证码数据 -->
    <tr><td></td><td><img id="img" src="/study/getCode" onclick="changeCode();"/><a href="javascript:;" onclick="changeCode();">换一换</a></td></tr>
    <tr><td></td><td><input type="submit" value="登陆"></td></tr>
</table>
</form>
</body>
</html>

登陆流程分析:
Cookie和Session - 图58
CodeUtil

package cn.igeek.util;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;

import javax.imageio.ImageIO;

public class CodeUtil {
    private static int width = 90;// 定义图片的width
    private static int height = 20;// 定义图片的height
    private static int codeCount = 4;// 定义图片上显示验证码的个数
    private static int xx = 15;
    private static int fontHeight = 18;
    private static  int codeY = 16;
    private static char[] codeSequence = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
            'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };

    /**
     * 生成一个map集合
     * code为生成的验证码
     * codePic为生成的验证码BufferedImage对象
     * @return
     */
    public static Map<String,Object> generateCodeAndPic() {
        // 定义图像buffer
        BufferedImage buffImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        // Graphics2D gd = buffImg.createGraphics();
        // Graphics2D gd = (Graphics2D) buffImg.getGraphics();
        Graphics gd = buffImg.getGraphics();
        // 创建一个随机数生成器类
        Random random = new Random();
        // 将图像填充为白色
        gd.setColor(Color.WHITE);
        gd.fillRect(0, 0, width, height);

        // 创建字体,字体的大小应该根据图片的高度来定。
        Font font = new Font("Fixedsys", Font.BOLD, fontHeight);
        // 设置字体。
        gd.setFont(font);

        // 画边框。
        gd.setColor(Color.BLACK);
        gd.drawRect(0, 0, width - 1, height - 1);

        // 随机产生40条干扰线,使图象中的认证码不易被其它程序探测到。
        gd.setColor(Color.BLACK);
        for (int i = 0; i < 30; i++) {
            int x = random.nextInt(width);
            int y = random.nextInt(height);
            int xl = random.nextInt(12);
            int yl = random.nextInt(12);
            gd.drawLine(x, y, x + xl, y + yl);
        }

        // randomCode用于保存随机产生的验证码,以便用户登录后进行验证。
        StringBuffer randomCode = new StringBuffer();
        int red = 0, green = 0, blue = 0;

        // 随机产生codeCount数字的验证码。
        for (int i = 0; i < codeCount; i++) {
            // 得到随机产生的验证码数字。
            String code = String.valueOf(codeSequence[random.nextInt(36)]);
            // 产生随机的颜色分量来构造颜色值,这样输出的每位数字的颜色值都将不同。
            red = random.nextInt(255);
            green = random.nextInt(255);
            blue = random.nextInt(255);

            // 用随机产生的颜色将验证码绘制到图像中。
            gd.setColor(new Color(red, green, blue));
            gd.drawString(code, (i + 1) * xx, codeY);

            // 将产生的四个随机数组合在一起。
            randomCode.append(code);
        }
        Map<String,Object> map  =new HashMap<String,Object>();
        //存放验证码
        map.put("code", randomCode);
        //存放生成的验证码BufferedImage对象
        map.put("codePic", buffImg);
        return map;
    }

    public static void main(String[] args) throws Exception {
        //创建文件输出流对象
        OutputStream out = new FileOutputStream("D://img/"+System.currentTimeMillis()+".jpg");
        Map<String,Object> map = CodeUtil.generateCodeAndPic();
        ImageIO.write((RenderedImage) map.get("codePic"), "jpeg", out);
        System.out.println("验证码的值为:"+map.get("code"));
    }
}

CodeServlet

package cn.igeek.web;

import java.awt.image.RenderedImage;
import java.io.IOException;
import java.util.Map;

import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
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 cn.igeek.util.CodeUtil;

@WebServlet("/getCode")
public class CodeServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 调用工具类生成的验证码和验证码图片
        Map<String, Object> codeMap = CodeUtil.generateCodeAndPic();

        // 将四位数字的验证码保存到Session中。
        HttpSession session = req.getSession();
        session.setAttribute("code_session", codeMap.get("code").toString());

        // 禁止图像缓存。
        resp.setHeader("Pragma", "no-cache");
        resp.setHeader("Cache-Control", "no-cache");
        resp.setDateHeader("Expires", -1);

        resp.setContentType("image/jpeg");

        // 将图像输出到Servlet输出流中。
        ServletOutputStream sos;
        try {
            sos = resp.getOutputStream();
            ImageIO.write((RenderedImage) codeMap.get("codePic"), "jpeg", sos);
            sos.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

}

LoginServlet

package cn.igeek.web;

import java.io.IOException;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import cn.igeek.domain.User;
import cn.igeek.service.UserService;

public class LoginServlet extends HttpServlet {
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //用户请求中的验证码获取
        String yanzhengma = request.getParameter("yanzhengma");

        //与session中保存的验证码进行校验
        String code_session = (String)request.getSession().getAttribute("code_session");
        if(!code_session.equalsIgnoreCase(yanzhengma)){
            //验证码错误,告诉用户,页面提示
            request.setAttribute("msg", "验证码错误");
            System.out.println("验证码错误");
            request.getRequestDispatcher("/login.html").forward(request, response);
            return;
        }
        //验证码正确,登录逻辑执行
        //获取用户名和密码
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        //调用Service方法,登录用户
        UserService userService = new UserService();
        //ctrl+shift+O自动解决一些常见问题,如果解决不了,留着程序员解决
        User user = new User();
        user.setUsername(username);
        user.setPassword(password);
        User loginUser = userService.login(user);
        if(loginUser == null){
            request.setAttribute("msg", "用户名或则密码错误");
            System.out.println("用户名或则密码错误");
            request.getRequestDispatcher("/login.html").forward(request, response);
            return;
        }else{
            System.out.println("登录成功");
            //登陆成功,跳转主页
            response.sendRedirect(request.getContextPath());
            //不写return,而且后面,还要可以执行的代码,有可能发送一个错误:response对象已经提交了,无法再次提交
            return;
        }
    }

    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

}

UserService

package cn.igeek.service;

import cn.igeek.dao.UserDao;
import cn.igeek.domain.User;

public class UserService {
    private UserDao userDao = new UserDao();
    /**
     * 用户登陆的方法
     * @param user
     * @return
     * @throws Exception 
     */
    public User login(User user) {
        return userDao.login(user);
    }

    public boolean register(User user) {
        User findByname = userDao.findByName(user.getUsername());
        if(findByname == null){
            userDao.register(user);
            return true;
        }else{
            return false;
        }
    }
}

UserDao

package cn.igeek.dao;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;

import cn.igeek.domain.User;

public class UserDao {
    private Connection conn = null;
    private QueryRunner qr = new QueryRunner();


    public UserDao() {
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/stock?useUnicode=true&characterEncoding=utf-8&useSSL=FALSE&serverTimezone=UTC", "root", "123456");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 处理用户登陆的方法
     * @param user
     * @return
     * @throws Exception 
     */
    public User login(User user) {
        String sql = "select * from user where username = ? and password = ?";
        //为什么要抓取异常?
        try {
            return qr.query(conn, sql, new BeanHandler<User>(User.class), user.getUsername(),user.getPassword());
        } catch (SQLException e) {
            e.printStackTrace();
            //对上面这些异常信息,做了一个中文注释
            throw new RuntimeException("用户登陆失败");
        }
        //try catch finally  throw  throws
    }

    public User findByName(String username) {
        String sql = "select * from user where username = ?";
        try {
            return qr.query(conn, sql, new BeanHandler<User>(User.class), username);
        } catch (SQLException e) {
            e.printStackTrace();
            return null;
        }
    }

    public void register(User user) {
        String sql = "insert into user values(null,?,?,?,?,?,?,?,?,?)";
        List<Object> list= new ArrayList<>();
        list.add(user.getUsername());
        list.add(user.getPassword());
        list.add(user.getAge());

        list.add(user.getSex());
        list.add(user.getEmail());
        list.add(user.getBirthday());
        list.add(user.getHobby());
        list.add(user.getAddress());
        list.add(user.getDescription());


        try {
            qr.update(conn, sql, list.toArray());
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

JavaWEB中三种数据范围(三种容器)

Request ServletContext Session

三个对象都有:setAttribute getAttribute方法,都可以存取数据

回答面试官问题套路:先回答基本概念,返回,跟上使用案例。
问题:什么时候使用request对象,保存数据?
答:一次请求中需要使用的数据,就保存在request。举例:商品数据,就存入请求中。

问题:什么时候使用ServletContext 对象,保存数据?
答:全局使用的数据,整个项目需要使用的数据,就是,要存入ServletContext 。举例:在线人数,存入ServletContext 。

问题:什么时候使用session对象,保存数据?
答:一次会话(多次请求——都发生在一次开启浏览器和关闭浏览器过程中)中需要使用的数据,都存入session。
举例:点餐商品

加密解密

<dependency>
  <groupId>commons-codec</groupId>
  <artifactId>commons-codec</artifactId>
  <version>1.15</version>
</dependency>
public class Test {
    public static void main(String[] args) {
        try {
//            System.out.println(URLEncoder.encode("我在igeek学习java", "UTF-8"));
//            System.out.println(URLEncoder.encode("我在igeek学习java", "GBK"));
//            System.out.println("====================================================");
//            myUrlEncode("我在igeek学习java", "UTF-8");
//            myUrlEncode("我在igeek学习java", "GBK");
         /*   
            System.out.println(org.apache.tomcat.util.codec.binary.Base64.encodeBase64String("测试".getBytes(StandardCharsets.UTF_8)));
            System.out.println(Base64.getEncoder().encodeToString("测试".getBytes(StandardCharsets.UTF_8)));*/

            //不可逆加密 - MD5
            String rs = DigestUtils.md5Hex("我在igeek学习java士大夫撒地方是的发送到发送到发发送到发送到阿斯蒂芬斯蒂芬asdfdsf");
            rs = DigestUtils.md5Hex(rs + "igeekhome");
            System.out.println("MD5->" + rs);
            //不可逆加密 - SHA-1
            rs = DigestUtils.sha1Hex("我在igeek学习java士大夫撒地方是的发送到发送到发发送到发送到阿斯蒂芬斯蒂芬asdfdsfsdfasdfasdfsadf撒旦法撒旦法说的");
            System.out.println("sha1->" + rs);

            //可逆加密 - 对称加密(AES)
            // 原文:
            String message = "我在igeek学习java啊啊发大水发顺丰达是打发士大夫撒地方手动阀";
            String keyStr = "1234567890abcdef";
            System.out.println("Message: " + message);
            System.out.println("key:" + keyStr);
            // 128位密钥 = 16 bytes Key:
            byte[] key = keyStr.getBytes("UTF-8");
            // 加密:
            byte[] encrypted = encrypt(key, message.getBytes("UTF-8"));
            System.out.println("对称加密——Encrypted: " + Base64.getEncoder().encodeToString(encrypted));
            // 解密:
            byte[] decrypted = decrypt(key, encrypted);
            System.out.println("对称解密——Decrypted: " + new String(decrypted, "UTF-8"));


        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static String myUrlEncode(String s, String enc) throws Exception {
        byte[] bytes = s.getBytes(enc);
        System.out.println(Arrays.toString(bytes));

        StringBuilder sb = new StringBuilder();
        for(byte b : bytes) {
            if((b >='0' && b<='9') || (b>='a' && b<='z') || (b>='A' && b <='Z')) {
                sb.append((char)b);
            } else {
                sb.append("%");
                sb.append(String.format("%02x", b).toUpperCase());
            }
        }
        System.out.println(sb);
        return null;
    }

    // 加密:
    public static byte[] encrypt(byte[] key, byte[] input) throws GeneralSecurityException {
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
        SecretKey keySpec = new SecretKeySpec(key, "AES");
        cipher.init(Cipher.ENCRYPT_MODE, keySpec);
        return cipher.doFinal(input);
    }

    // 解密:
    public static byte[] decrypt(byte[] key, byte[] input) throws GeneralSecurityException {
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
        SecretKey keySpec = new SecretKeySpec(key, "AES");
        cipher.init(Cipher.DECRYPT_MODE, keySpec);
        return cipher.doFinal(input);
    }
}
public class EncodeCUtil {
    // 加密:
    public static String encrypt(String key, String input) {
        String rs = null;
        try {
            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
            SecretKey keySpec = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "AES");
            cipher.init(Cipher.ENCRYPT_MODE, keySpec);
            byte[] bytes = cipher.doFinal(input.getBytes(StandardCharsets.UTF_8));
            rs = Base64.getEncoder().encodeToString(bytes);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return rs;
    }

    // 解密:
    public static String decrypt(String key, String input) {
        String rs = null;
        try {
            Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
            SecretKey keySpec = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "AES");
            cipher.init(Cipher.DECRYPT_MODE, keySpec);
            byte[] bytes = cipher.doFinal(Base64.getDecoder().decode(input));
            rs = new String(bytes, "UTF-8");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return rs;
    }
}

非对称加密

package com.woniu.springsecurity.util;


import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.IOUtils;

import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;

public class RSAUtil {

    public static final String CHARSET = "UTF-8";
    public static final String RSA_ALGORITHM = "RSA";


    public static Map<String, String> createKeys(int keySize){
        //为RSA算法创建一个KeyPairGenerator对象(KeyPairGenerator,密钥对生成器,用于生成公钥和私钥对)
        KeyPairGenerator kpg;
        try{
            kpg = KeyPairGenerator.getInstance(RSA_ALGORITHM);
        }catch(NoSuchAlgorithmException e){
            throw new IllegalArgumentException("No such algorithm-->[" + RSA_ALGORITHM + "]");
        }

        //初始化KeyPairGenerator对象,密钥长度
        kpg.initialize(keySize);
        //生成密匙对
        KeyPair keyPair = kpg.generateKeyPair();
        //得到公钥
        Key publicKey = keyPair.getPublic();
        String publicKeyStr = Base64.encodeBase64URLSafeString(publicKey.getEncoded()); //返回一个publicKey经过二次加密后的字符串
        //得到私钥
        Key privateKey = keyPair.getPrivate();
        String privateKeyStr = Base64.encodeBase64URLSafeString(privateKey.getEncoded()); //返回一个privateKey经过二次加密后的字符串

        Map<String, String> keyPairMap = new HashMap<String, String>();
        keyPairMap.put("publicKey", publicKeyStr);
        keyPairMap.put("privateKey", privateKeyStr);

        return keyPairMap;
    }

    /**
    * 得到公钥
    * @param publicKey 密钥字符串(经过base64编码)
    * @throws Exception
    */
    public static RSAPublicKey getPublicKey(String publicKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
        //通过X509编码的Key指令获得公钥对象
        KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(Base64.decodeBase64(publicKey));
        RSAPublicKey key = (RSAPublicKey) keyFactory.generatePublic(x509KeySpec);
        return key;
    }

    /**
    * 得到私钥
    * @param privateKey 密钥字符串(经过base64编码)
    * @throws Exception
    */
    public static RSAPrivateKey getPrivateKey(String privateKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
        //通过PKCS#8编码的Key指令获得私钥对象
        KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKey));
        RSAPrivateKey key = (RSAPrivateKey) keyFactory.generatePrivate(pkcs8KeySpec);
        return key;
    }

    /**
    * 公钥加密
    * @param data
    * @param publicKey
    * @return
    */
    public static String publicEncrypt(String data, RSAPublicKey publicKey){
        try{
            Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
            return Base64.encodeBase64URLSafeString(rsaSplitCodec(cipher, Cipher.ENCRYPT_MODE, data.getBytes(CHARSET), publicKey.getModulus().bitLength()));
        }catch(Exception e){
            throw new RuntimeException("加密字符串[" + data + "]时遇到异常", e);
        }
    }

    /**
    * 私钥解密
    * @param data
    * @param privateKey
    * @return
    */

    public static String privateDecrypt(String data, RSAPrivateKey privateKey){
        try{
            Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, privateKey);
            return new String(rsaSplitCodec(cipher, Cipher.DECRYPT_MODE, Base64.decodeBase64(data), privateKey.getModulus().bitLength()), CHARSET);
        }catch(Exception e){
            throw new RuntimeException("解密字符串[" + data + "]时遇到异常", e);
        }
    }

    /**
    * 私钥加密
    * @param data
    * @param privateKey
    * @return
    */

    public static String privateEncrypt(String data, RSAPrivateKey privateKey){
        try{
            Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
            cipher.init(Cipher.ENCRYPT_MODE, privateKey);
            return Base64.encodeBase64URLSafeString(rsaSplitCodec(cipher, Cipher.ENCRYPT_MODE, data.getBytes(CHARSET), privateKey.getModulus().bitLength()));
        }catch(Exception e){
            throw new RuntimeException("加密字符串[" + data + "]时遇到异常", e);
        }
    }

    /**
    * 公钥解密
    * @param data
    * @param publicKey
    * @return
    */

    public static String publicDecrypt(String data, RSAPublicKey publicKey){
        try{
            Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, publicKey);
            return new String(rsaSplitCodec(cipher, Cipher.DECRYPT_MODE, Base64.decodeBase64(data), publicKey.getModulus().bitLength()), CHARSET);
        }catch(Exception e){
            throw new RuntimeException("解密字符串[" + data + "]时遇到异常", e);
        }
    }

    private static byte[] rsaSplitCodec(Cipher cipher, int opmode, byte[] datas, int keySize){
        int maxBlock = 0;
        if(opmode == Cipher.DECRYPT_MODE){
            maxBlock = keySize / 8;
        }else{
            maxBlock = keySize / 8 - 11;
        }
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offSet = 0;
        byte[] buff;
        int i = 0;
        try{
            while(datas.length > offSet){
                if(datas.length-offSet > maxBlock){
                    buff = cipher.doFinal(datas, offSet, maxBlock);
                }else{
                    buff = cipher.doFinal(datas, offSet, datas.length-offSet);
                }
                out.write(buff, 0, buff.length);
                i++;
                offSet = i * maxBlock;
            }
        }catch(Exception e){
            throw new RuntimeException("加解密阀值为["+maxBlock+"]的数据时发生异常", e);
        }
        byte[] resultDatas = out.toByteArray();
        IOUtils.closeQuietly(out);
        return resultDatas;
    }

    public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeySpecException {
        Map<String, String> keyMap = RSAUtil.createKeys(1024);
        String  publicKey = keyMap.get("publicKey");
        String  privateKey = keyMap.get("privateKey");
        System.out.println("公钥: \n\r" + publicKey);
        System.out.println("私钥: \n\r" + privateKey);

        //        System.out.println("公钥加密——私钥解密");
        //        String str = "寒山月初℃";
        //        System.out.println("\r明文:\r\n" + str);
        //        System.out.println("\r明文大小:\r\n" + str.getBytes().length);
        //        String encodedData = RSAUtil.publicEncrypt(str, RSAUtil.getPublicKey(publicKey));
        //        System.out.println("密文:\r\n" + encodedData);
        //        String decodedData = RSAUtil.privateDecrypt(encodedData, RSAUtil.getPrivateKey(privateKey));
        //        System.out.println("解密后文字: \r\n" + decodedData);


        System.out.println("私钥加密——公钥解密");
        String str = "寒山月初℃";
        System.out.println("\r明文:\r\n" + str);
        System.out.println("\r明文大小:\r\n" + str.getBytes().length);
        String encodedData = RSAUtil.privateEncrypt(str, RSAUtil.getPrivateKey(privateKey));
        System.out.println("密文:\r\n" + encodedData);
        String decodedData = RSAUtil.publicDecrypt(encodedData, RSAUtil.getPublicKey(publicKey));
        System.out.println("解密后文字: \r\n" + decodedData);
    }
}