(仿牛客论坛项目)03 - 登录界面的验证码实现

目录

  • 前言
  • 1、会话控制
    • 1.1 为什么要使用 Cookie?
    • 1.2 为什么要使用 session?
    • 1.3 cookie 和 session 的工作流程
    • 1.4 分布式下的 session 实现
  • 2、kaptcha验证码:
    • 2.1 导入 jar 包
    • 2.2 编写 Kaptcha 配置类
  • 3、登陆界面的验证码实现
    • 3.1 LoginController 层
    • 3.2 修改login.html页面(jQuery)
    • 3.3 测试页面


前言

  1. 之前在学习 JavaWeb 的时候就学习到了 Cookie 和 Kaptcha,(尚硅谷)JavaWeb新版教程11-Cookie-Kaptcha-Exp,这里就新学习的东西再做一点补充;
  2. HTTP Cookie;

1、会话控制

1.1 为什么要使用 Cookie?

(仿牛客论坛项目)03 - 登录界面的验证码实现_第1张图片

1.2 为什么要使用 session?

  1. cookie 缺点,不安全,而且每次客户端向服务器发请求时都携带,造成传输的压力;
  2. session 解决安全问题,将敏感信息保存在服务器。

1.3 cookie 和 session 的工作流程

(仿牛客论坛项目)03 - 登录界面的验证码实现_第2张图片
(仿牛客论坛项目)03 - 登录界面的验证码实现_第3张图片

  1. 响应给客户端的 cookie 是携带 sessionId 的,下次 cookie 传过来根据 sessionId 在服务器找是否有对应的 session,从中获取信息;

(仿牛客论坛项目)03 - 登录界面的验证码实现_第4张图片

(仿牛客论坛项目)03 - 登录界面的验证码实现_第5张图片

1.4 分布式下的 session 实现

  1. 能用 cookie 尽量用 cookie,分布式部署情况下,session 用的越来越少

存在的问题:

  1. 第一次使用 nginx 分布式负载均衡,传给服务器 1,那么创建并传回一个sessionId;
  2. 第二次发送请求,服务器 3 比较空闲,传给了服务器 3,这时找不到又创建并传回一个 sessionId;
  3. 这样的话对同一个客户端,每次都产生不同的 sessionId

(仿牛客论坛项目)03 - 登录界面的验证码实现_第6张图片

解决方法:
① 粘性session:固定的请求分给同一个服务器(负载不均衡)
② 同步session:某一个服务器创建session之后,同步到所有的服务器中(加重服务器负担,且服务器之间有耦合)

(仿牛客论坛项目)03 - 登录界面的验证码实现_第7张图片

③ 共享session:有一台服务器专门用来存 session 的,浏览器访问需要创建 session,都将 session 创建在该服务器中(单体session服务器挂了,之所以使用分布式,就是为了解决单体服务器的瓶颈)

(仿牛客论坛项目)03 - 登录界面的验证码实现_第8张图片

④ 目前主流:能存在 cookie 中就存在 cookie 中,敏感数据放到数据库中(传统数据库存放到硬盘,存取慢,所以存到 redis 非关系数据库中,这下数据在内存中,存取快)

(仿牛客论坛项目)03 - 登录界面的验证码实现_第9张图片

2、kaptcha验证码:

2.1 导入 jar 包


<dependency>
    <groupId>com.github.pengglegroupId>
    <artifactId>kaptchaartifactId>
    <version>2.3.2version>
dependency>

2.2 编写 Kaptcha 配置类

  1. Kaptcha 的核心文件是一个 Producer 接口,里面就两个方法,一个是生成文字,一个是生成图片;

(仿牛客论坛项目)03 - 登录界面的验证码实现_第10张图片
2. 这个接口有一个默认的实现类 DefaultKaptcha,我们通常用这个实现类就可以了;

在这里插入图片描述

  1. 生成 KaptchaConfig 配置类,设置验证码相关配置(可以在配置文件中定义然后引入,也可以直接set);
  2. 参数用 config 对象来设置,config 对象中需要一个 Porperties 对象,其实就是一个存放验证码各种配置的集合容器,这里使用 hashTable 实现的;

在这里插入图片描述
在这里插入图片描述

@Configuration
public class KaptchaConfig {
    @Bean
    public Producer kaptchaProducer() {
        Properties properties = new Properties();
        properties.setProperty("kaptcha.image.width", "100");
        properties.setProperty("kaptcha.image.height", "40");
        properties.setProperty("kaptcha.textproducer.font.size", "32");
        properties.setProperty("kaptcha.textproducer.font.color", "0,0,0");
        properties.setProperty("kaptcha.textproducer.char.string", "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYAZ");
        properties.setProperty("kaptcha.textproducer.char.length", "4");
        properties.setProperty("kaptcha.noise.impl", "com.google.code.kaptcha.impl.NoNoise");

        DefaultKaptcha kaptcha = new DefaultKaptcha();
        Config config = new Config(properties);
        kaptcha.setConfig(config);
        return kaptcha;
    }
}

3、登陆界面的验证码实现

3.1 LoginController 层

  1. 要生成一个验证码图片,这个验证码图片要我们自己通过 response 设置并传给客户端,并且存到服务器的 session 中;

  2. 使用 KaptchaProducer 的 bean 对象生成验证码:首先属性注入,其次生成一个字符串,之后生成一个图片

  3. 将验证码存入 session,以便后续验证用户输入的验证码是否正确

  4. 将图片输出给浏览器:setContentType("image/png")

  5. 通过 respomse 获取字节流输出流 OutputStream(getWriter() 方法是获取字符流), 同时使用 ImageIO 工具类中的 write 方法用来将图片输出给浏览器;

  6. 如果捕获到异常将日志记录一下,需要一个static final Logger logger对象;

private static final Logger logger = LoggerFactory.getLogger(LoginController.class);

@RequestMapping(path = "/kaptcha",method = RequestMethod.GET)
@ResponseBody
public void getKaptcha(HttpServletResponse response, HttpSession session){
    String text = kaptchaProducer.createText();
    BufferedImage image = kaptchaProducer.createImage(text);

    //将生成的验证码保存到session中,以便后续验证用户输入的验证码是否正确
    session.setAttribute("kaptcha",text);

    //将图片输出给浏览器
    response.setContentType("image/png");
    try {
        OutputStream os = response.getOutputStream();
        ImageIO.write(image,"png",os);
    } catch (IOException e) {
        logger.error("响应验证码失败:" + e.getMessage());
    }
}

注意:

  • 注入日志对象;
  • 因为我们自己通过 response 对象的输出流直接响应给浏览器,所以返回值为 void,这里不是响应一个字符串,也不是一个 html 页面,而是一张图片;
  • 浏览器响应 response 是由 SpringMVC 去维护的,自动会进行输出流的关闭,所以不用我们自己手动关闭了;
  • 注意 IO 流输出图片的使用方法(运用不熟练)

3.2 修改login.html页面(jQuery)

  1. 验证码的图片 src 地址换成动态的请求;th:src="@{/kaptcha}"
  2. 每次点击刷新验证码,都去刷新验证码,需要实现一个 js 功能,其中定义路径需要声明一个全局变量在 global.js 文件中;
var CONTEXT_PATH = "/community";
<script>
	function refresh_kaptcha() {
		var path = CONTEXT_PATH + "/kaptcha?p=" + Math.random();
		// 修改该节点的src属性
		$("#kaptcha").attr("src", path);
	}
</script>

解释:

  • 我们要访问的路径其实是 /community/kaptcha,但是如果写成这样的话,那么我们如果其他项目要用的话,路径就写死了,所以要在全局文件中配置一个上下文路径的变量;
  • 同时呢,如果你本来访问的 /community/kaptcha 这个路径去获取一个图片的静态资源,然后你刷新之后访问的是同一个路径,有一些浏览器比较智能,认为你不需要变化,所以不会重新发送请求,所以我们要加一个随机参数欺骗浏览器。

最终 login.html 界面的验证码部分,切记要给当前 img 图片一个 id 属性,这样 javascript 才能够通过调用该 DOM 节点然后执行动态功能:

<div class="col-sm-4">
	<img th:src="@{/kaptcha}" id="kaptcha" style="width:100px;height:40px;" class="mr-2"/>
	<a href="javascript:refresh_kaptcha();" class="font-size-12 align-bottom">刷新验证码a>
div>

3.3 测试页面

  1. 因为涉及到修改了静态资源,你光点 run 可能 target 文件中的静态资源不会被重新编译,所以需要 rebuild;
  2. 点击刷新验证码图片在变化就可以了。

(仿牛客论坛项目)03 - 登录界面的验证码实现_第11张图片

你可能感兴趣的:(java项目,服务器,java,springboot)