手把手教你验证码检验的登录

在网站实际应用过程中,为了防止网站登录接口被机器人轻易地使用,产生一些没有意义的用户数据,所以,采用验证码进行一定程度上的拦截,当然,我们采用的还是一个数字与字母结合的图片验证码形式,后续会讲到更加复杂的数字计算类型的图片验证码,请持续关注我的博客。

实现思路

博主环境:springboot3 、java17、thymeleaf

  1. 访问登录页面

  1. 登录

  • 验证验证码

  • 验证账号、密码

  • 验证成功时,生成登录凭证,发放给客户端

  • 验证失败时,跳转回登录信息,并保留原有填入信息

  1. 退出

  • 将登录凭证修改为失效状态

  • 跳转至首页

访问登录页面的方法已经在前文说明过了,就不多加赘述了,展示一下代码:

// 登录页面@RequestMapping(path = "/login", method = RequestMethod.GET)public String getLoginPage() {return"/site/login";
}
复制代码

访问完登录页面,我们就要进行信息输入,然而,现在,还没有把验证码信息正确展现出来,所以,接下来,我们先来实现验证码的部分。

所需两个数据表 SQL 代码如下:

注:注册流程可看前文。一文教你学会实现以邮件激活的注册账户代码_yumuing的博客-CSDN博客

-- user表DROPTABLE IF EXISTS `user`;SET character_set_client = utf8mb4 ;
CREATETABLE `user` (`id` int(11) NOTNULL AUTO_INCREMENT,`username` varchar(50) DEFAULTNULL,`password` varchar(50) DEFAULTNULL,`salt` varchar(50) DEFAULTNULL,`email` varchar(100) DEFAULTNULL,`type` int(11) DEFAULTNULL COMMENT '0-普通用户; 1-超级管理员; 2-版主;',`status` int(11) DEFAULTNULL COMMENT '0-未激活; 1-已激活;',`activation_code` varchar(100) DEFAULTNULL,`header_url` varchar(200) DEFAULTNULL,`create_time` timestampNULLDEFAULTNULL,PRIMARY KEY (`id`),KEY `index_username` (`username`(20)),KEY `index_email` (`email`(20))
) ENGINE=InnoDB AUTO_INCREMENT=101DEFAULT CHARSET=utf8;-- 登录凭证表DROPTABLE IF EXISTS `login_ticket`;SET character_set_client = utf8mb4 ;
CREATETABLE `login_ticket` (`id` int(11) NOTNULL AUTO_INCREMENT,`user_id` int(11) NOTNULL,`ticket` varchar(45) NOTNULL,`status` int(11) DEFAULT'0' COMMENT '1-有效; 0-无效;',`expired` timestampNOTNULL,PRIMARY KEY (`id`),KEY `index_ticket` (`ticket`(20))
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
复制代码

Kaptcha 验证码设计和校验

目前使用图片验证码较为广泛的是 Kaptcha ,它只有一个版本:2.3.2,值得注意的是,在 springboot 3的环境下,使用该插件包大部分会使用到的 http 包,不能导入 javax 包内的,而是应该导入jakarta 包内的。

它能够实现以下效果:水纹有干扰、鱼眼无干扰、水纹无干扰、阴影无干扰、阴影有干扰

其中,它们的文字内容限制、背景图片、文字颜色、大小、干扰样式颜色、整体(图片)高度、宽度、图片渲染效果、干扰与否都是可以进行自定义的。我们只要按需配置好对应的 configuration 即可。当然,它并没有默认集成进 springboot 中,使用之前必须先导入对应依赖,如下:

<dependency><groupId>com.github.penggle</groupId><artifactId>kaptcha</artifactId><version>2.3.2</version></dependency>复制代码

导包成功之后,我们就需要进行按需设置配置类了,它相关配置属性如下:

配置类模板如下:

package top.yumuing.community.config;import com.google.code.kaptcha.Producer;
import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.google.code.kaptcha.util.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import java.util.Properties;@ConfigurationpublicclassKaptchaConfig {@Beanpublic Producer kaptchaProduce(){Properties properties=newProperties();//图片的宽度properties.setProperty("kaptcha.image.width","100");//图片的高度properties.setProperty("kaptcha.image.height","40");//字体大小properties.setProperty("kaptcha.textproducer.font.size","32");//字体颜色(RGB)properties.setProperty("kaptcha.textproducer.font.color","0,0,0");//验证码字符的集合properties.setProperty("kaptcha.textproducer.char.string","123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ");//验证码长度(即在上面集合中随机选取几位作为验证码)properties.setProperty("kaptcha.textproducer.char.length","4");//图片的干扰样式:默认存在无规则划线干扰//无干扰:com.google.code.kaptcha.impl.NoNoiseproperties.setProperty("kaptcha.noise.impl","com.google.code.kaptcha.impl.NoNoise");//图片干扰颜色:默认为黑色properties.setProperty("kaptcha.noise.color", "black");//图片渲染效果:默认水纹// 水纹com.google.code.kaptcha.impl.WaterRipple 鱼眼com.google.code.kaptcha.impl.FishEyeGimpy 阴影com.google.code.kaptcha.impl.ShadowGimpy//properties.setProperty("kaptcha.obscurificator.impl", "com.google.code.kaptcha.impl.ShadowGimpy");DefaultKaptchaKaptcha=newDefaultKaptcha();Config config=newConfig(properties);Kaptcha.setConfig(config);return Kaptcha;}
}
复制代码

配置好相关属性之后,我们就可以进行验证码生成的接口开发了,首先,让 Producer 进入 Bean 工厂进行管理,之后,再生成验证码文本并传入 session 中,以便后续进行验证码校验,之后,再生成对应验证码图片,以 BufferedImage 的形式存储,并利用 HttpServletResponse 和 ImageIO 将图片传输给浏览器,其中,注意设置好图片返回类型,并且无需手动关闭 IO 流,springboot 会进行管理,实现自行关闭。此时以 Get 方法访问 域名/imageCode ,就会返回对应验证码图片了。

//验证码@RequestMapping(path = "/imageCode",method = RequestMethod.GET)publicvoidgetImgCode(HttpServletResponse response, HttpSession session){StringcodeText= imageCodeProducer.createText();BufferedImageimageCode= imageCodeProducer.createImage(codeText);// 将验证码文本存入 sessionsession.setAttribute("imageCode", codeText);//设置返回类型response.setContentType("image/jpg");try {OutputStreamos= response.getOutputStream();ImageIO.write(imageCode, "jpg", os);} catch (IOException e) {logger.error("响应验证码失败!"+e.getMessage());}}
复制代码

当然,有些浏览器为了节省用户访问流量,较为智能地将已获取的静态资源链接自动不再访问,所以,需要添加额外参数完成浏览器适配,这里采用的是利用 JavaScript 把每次访问验证码图片的链接添加一个随机数字的参数,以保证智能节省流量的问题。当然,我们不用去 controller 获取该参数,因为没有意义,也不要求一定要所有参数都匹配到。代码如下:

functionrefresh_imageCode() {var path = "/imageCode?p=" + Math.random();$("#imageCode").attr("src", path);
}
复制代码

获取到验证码,我们就必须对其进行校对,只有验证码通过之后,才能去校验账户和密码。而验证码校对最重要的一点就是,需要忽略大小写,不能苛求用户的耐心。校验验证码不通过的情况不仅仅需要考虑发送方的验证码文本为空或者文本不一致导致的错误,还需要考虑接受方(服务端)的验证码文本究竟有没有存储下来,以防通过接口工具直接 post 访问该接口产生的空数据。代码如下:

//登录@RequestMapping(path = "/login",method = RequestMethod.POST)public String login(String username, String password, String code,boolean rememberMe, Model model, HttpSession session, HttpServletResponse response){StringimageCode= (String) session.getAttribute("imageCode");// 验证码if (StringUtils.isBlank(imageCode) || StringUtils.isBlank(code) || !imageCode.equalsIgnoreCase(code)){model.addAttribute("codeMsg","验证码不正确!");return"/site/login";}
}
复制代码

记住我功能的实现

用户进行登录时,常常需要勾选是否记住的按钮,这是为了保证用户长时间使用该应用而不因为需要频繁登录,丧失用户量。当然,也有部分用户不希望自己的用户凭证长时间保存,希望通过经常性更新,保证一定程度上的用户数据安全。实现这个功能并不困难,只要发送数据时,多添加一个布尔参数而已。为了便于代码阅读,增加两个常量:登录默认状态超时时间常量、记住我登录状态超时时间常量,如下:

// 默认登录状态超时常量intDEFAULT_EXPIRED_SECONDS=3600 * 12;// 记住状态的登录凭证超时时间intREMEMBER_EXPIRED_SECONDS=3600 * 24 * 100;
复制代码

之后在登录接口进行判断就行,记住我布尔值为 true ,故代码如下:

// 是否记住我intexpiredSeconds= rememberMe ? REMEMBER_EXPIRED_SECONDS : DEFAULT_EXPIRED_SECONDS;
复制代码

校验账号和密码

按照标准流程,先从数据访问层开始写,我们校验账户和密码都是使用查询语句就行了,当然,一句查询语句就行,不用为了两个参数就建两个查询语句,因为我们已经获得了这个对象,直接使用映射方法里的 get 方法就行,再进行所需要的校验工作。这里采用的是 username 为参数的查询语句来获取 user 对象。具体代码如下:

userMapper.java

User selectOneByUsername(@Param("username") String username);
复制代码

userMapper.xml

<sqlid="Base_Column_List">id,username,password,salt,email,type,status,activation_code,header_url,create_time
</sql><selectid="selectOneByUsername"resultMap="BaseResultMap">select<includerefid="Base_Column_List"/>from userwhereusername = #{username,jdbcType=VARCHAR}
</select>复制代码

使用该查询语句之前,我们必须先保证传过来的账户和密码不能为空,查询才有意义,获取到 user 对象之后,我们先验证账户存不存在,如果不存在,返回错误信息就行了,如果存在的话,检查它的账户状态是否是激活状态,不是的话,返回错误信息,是的话,我们就能进行校验工作了,当然,账户存在,用户名就不用校验了,只需要校验密码就行了。代码如下:

//空值处理if(StringUtils.isBlank(username)){map.put("usernameMsg", "账号不能为空!");return map;
}
if (StringUtils.isBlank(password)){map.put("passwordMsg", "密码不能为空!");return map;
}//验证账号Useruser= userMapper.selectOneByUsername(username);
if (user == null){map.put("usernameMsg","该账号不存在");return map;
}//验证状态if (user.getStatus() == 0){map.put("usernameMsg","该账号未激活!");return map;
}//验证密码
password = CommunityUtil.md5(password+user.getSalt());
if(!user.getPassword().equals(password)){map.put("passwordMsg","密码不正确!");return map;
}
复制代码

当账户密码校验成功时,将登录凭证存入 cookie 即可,设置好全局可用,以及失效时间,只要设置好登录凭证失效时间,后续客户端会自动在时间到达,将登录凭证注销掉,以便我们把登录状态取消掉。如果校验不成功的话,就直接返回校验信息。在登录接口进行调用即可

// 检测账号密码
Map<String,Object> map = userServiceImpl.login(username,password,expiredSeconds);
if (map.containsKey("loginTicket")){//设置cookieCookiecookie=newCookie("loginTicket",map.get("loginTicket").toString());cookie.setPath("/");cookie.setMaxAge(expiredSeconds);response.addCookie(cookie);return"redirect:/index";
}else {model.addAttribute("usernameMsg",map.get("usernameMsg"));model.addAttribute("passwordMsg",map.get("passwordMsg"));return"/site/login";
}
复制代码

生成登录凭证

还是先从数据访问层说起,注意生成自增id即可。具体的 xml 语句如下:

<insertid="insertAll"parameterType="LoginTicket"keyProperty="id">insert into login_ticket(id, user_id, ticket,status, expired)values (#{id,jdbcType=NUMERIC}, #{userId,jdbcType=NUMERIC}, #{ticket,jdbcType=VARCHAR},#{status,jdbcType=NUMERIC}, #{expired,jdbcType=TIMESTAMP})
</insert>复制代码

采用的是字母和数字混合的随机字符串的形式,利用的是 java.util.UUID 来生成的。将需要的参数利用 set 方法存入对象里面,再利用对应插入语句插入数据库即可,注意默认生效状态为 1。具体生成登录凭证的登录接口代码如下:

//生成登录凭证LoginTicketloginTicket=newLoginTicket();
loginTicket.setUserId(user.getId());
loginTicket.setTicket(CommunityUtil.generateUUID());
loginTicket.setStatus(1);
loginTicket.setExpired(newDate(System.currentTimeMillis() + expiredSeconds * 1000));
loginTicketMapper.insertAll(loginTicket);
map.put("loginTicket",loginTicket.getTicket());
return map;
复制代码

不知道你们有没有察觉一个问题:失效时间到了,状态仍为生效状态的。我们的登录凭证生效状态是后续登录信息展示的关键,后续还会考虑,时间过期之后,生效状态该怎么去自动修改?或者不作修改该怎么去解决失效时间到了,状态仍为生效状态的问题,请持续关注博主,后续为你们解答。

将登录凭证发送给客户端,就基本完成了登录的实现。

相关代码资源已上传,可看:项目代码

相关 bug

No primary or single unique constructor found for interface javax.servlet.http.HttpServletResponse

springboot3 下导不了 javax.servlet.http 包,必须导 jakarta.servlet.http

也就是 http 包 又更改了。

import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
复制代码

不能导,不然会发生错误。

import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

作者:yumuing

链接:https://juejin.cn/post/7211415965240934461

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/53070.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何识别高级的验证码

http://sebug.net/paper/pst_WebZine/pst_WebZine_0x02/html/PSTZine_0x02_0x09.html Ph4nt0m Security TeamIssue 0x02, Phile #0x09 of 0x0A|---------------------------------------------------------------------------| |-----------------------[ 如何识别高级的验证码…

hcaptcha 我是人类验证码怎么跳过怎么验证自动识别

相信这个验证码很多人都见过&#xff0c;这个叫hcaptcha验证码 在网页上偶尔出现&#xff0c;提示需要你证明“我是人类” 这种验证码与谷歌的reCaptcha有异曲同工之处&#xff0c;但是其实hcaptcha与recaptcha是完全不同的产品&#xff0c;不是同一个公司出品的。 这种hcapt…

手把手教你识别FunCaptcha验证码

今天&#xff0c;我们将专注于FunCaptcha&#xff0c;这是一种独特而具有挑战性的CAPTCHA类型&#xff0c;在整个网络上越来越流行。我们将深入探讨FunCaptcha是什么&#xff0c;不同类型的FunCaptcha挑战&#xff0c;如何使用CapSolver解决它们等等。 什么是FunCaptcha&#…

基于openai chatgpt和embeddings制作私有知识库聊天机器人

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、原理、流程二、制作预料库三、制作问答功能总结 如果有问题可以联系我**&#xff1a;https://gitee.com/xiaoyuren/gpt3 前言 在当今信息爆炸的时代&#…

基于 Quivr 搭建个人知识库

目录 Quivr介绍 Quivr特性 Quivr演示 Demo with GPT3.5: Demo of the new version&#xff1a; Quivr实战 Quiv 使用的主要技术 Quiv 实践依赖 创建Supabase项目 部署Quiv项目 第一步&#xff1a;现在源码 第二步&#xff1a;设置环境变量 第三步&#xff1a;执行sql 第…

标书打印分册小技巧

标书打印出来后&#xff0c;一般都有很多本&#xff0c;去打印店胶装标书时&#xff0c;需要把每一本标书分出来&#xff0c;黑帽大师用便签纸就能方便的分出标书。 把便签纸贴在每本标书的最后一页上&#xff0c;这样就能方便的分出每一本了。

学校计算机维护投标书,信息化系统硬件及应用系统安全运维服务投标书范本

这是一份信息化系统硬件及应用系统安全运维服务投标书范本&#xff0c;含运维服务方案&#xff0c;word格式&#xff0c;可编辑&#xff0c;有需要的朋友可以参考学习。 信息化系统硬件及应用系统安全运维服务 本次服务范围为XX局信息化系统硬件及应用系统&#xff0c;各类软硬…

招投标小程序开发功能及源码

一般获取招投标信息的渠道主要有三种&#xff0c;一&#xff0c;来源于官方、正规的政府网站、公共资源交易中心等&#xff1b;二&#xff0c;能提供针对性的招投标信息平台&#xff1b;三是通过个人的人脉资源来获取项目信息。今天我们重点讲下招投标平台怎么运营的&#xff0…

python制作标书_爬取比比网中标标书,并保存为PDF格式文件

前言 本文的文字及图片来源于网络,仅供学习、交流使用,不具有任何商业用途,如有问题请及时联系我们以作处理。 以下文章来源于CSDN&#xff0c;作者嗨学编程 python开发环境 python 3.6 pycharm import requests import parsel import pdfkit import time 相关模块pip安装即可 …

python制作标书_Python爬取比比网中标标书并保存成PDF格式

前言 本文的文字及图片来源于网络,仅供学习、交流使用,不具有任何商业用途,如有问题请及时联系我们以作处理。 python开发环境 python 3.6 pycharm requests parsel pdfkit time 相关模块pip安装即可 目标网页分析 1、先从列表页中获取详情页的URL地址 是静态网站,可以直接请求…

第一次写标书

由于工作需要开始写起标书。前后大概花了五天时间。 经过自我学习和老师指导&#xff0c;知道了一件事情&#xff0c;不管做什么&#xff0c;其实都是能够有所学习的。 而学习&#xff0c;为了有所收获&#xff0c;需要用心再去体会每一个过程&#xff0c;并记录下来&#xf…

小程序投标书_快来学习招投标小技巧!中标率提高50%(建议收藏)

99%的投标人使用【建企同盟APP】都中标了&#xff01; 建企同盟APP 招标信息不遮挡 订阅推送零费用 从保证中标的因素来看&#xff0c;三个因素最为重要&#xff0c;首先是关系&#xff0c;其次是能力&#xff0c;最后才是价格。关系指与用户的关系&#xff0c;既有最终用户又包…

小程序投标书_程序员接私活常用哪些平台?

给大家推荐国内外几个接外包比较靠谱的平台&#xff0c;相对来说规模和专业性都还不错。 想要接外包或者积累行业人脉的小伙伴都可以收藏一波&#xff1a; 国外篇 如果打算接国外的软件外包&#xff0c;首先以下几点能力需要提前掌握&#xff1a; 基本的英语沟通能力(能够基本沟…

重磅:AI 的 “iPhone 时刻” 已经到来

大家好&#xff0c;我是校长。 上周英伟达 CEO 黄仁勋在 GTC 大会主题演讲火爆了全网。 一起来看看黄仁勋说了什么。 英伟达 CEO 黄仁勋在 GTC 大会主题演讲上开场时这么说&#xff1a; “近四十年来&#xff0c;摩尔定律一直是引领计算机行业动态发展的重要规律&#xff0c;而…

AI内容生成检查器

我的新书《Android App开发入门与实战》已于2020年8月由人民邮电出版社出版&#xff0c;欢迎购买。点击进入详情 AI进行内容生成已经是是当下的人们话题&#xff0c;那么怎么判断文本的内容是不是AI生成的呢&#xff1f;AI 生成的句子将被突出显示&#xff0c;目前工具可以检测…

ChatGPT生成文本检测器-task5

#### 任务五&#xff1a;使用TFIDF特征和XGBoost完成训练和预测 说明&#xff1a;在这个任务中&#xff0c;你需要使用TFIDF特征和XGBoost算法完成训练和预测&#xff0c;进一步提升文本分类的性能。实践步骤&#xff1a; 准备TFIDF特征矩阵和相应的标签。划分训练集和测试集。…

ChatGPT生成文本检测器-task2

#### 任务二&#xff1a;对数据集字符进行可视化&#xff0c;统计标签和字符分布 说明&#xff1a;在这个任务中&#xff0c;你需要使用Pandas库对数据集的字符进行可视化&#xff0c;并统计数据集中的标签和字符的分布情况&#xff0c;以便更好地理解数据集。实践步骤&#x…

ChatGPT生成文本检测器-task8

#### 任务八&#xff1a;使用Word2Vec词向量&#xff0c;搭建BILSTM模型进行训练和预测 说明&#xff1a;在这个任务中&#xff0c;你将使用Word2Vec词向量&#xff0c;搭建BILSTM模型进行文本分类的训练和预测&#xff0c;通过双向长短期记忆网络来进行文本分类。实践步骤&am…

ChatGPT生成文本检测器-task1

### 背景介绍 近年来人工智能在自然语言处理领域取得了巨大的进展。其中一项引人注目的技术是生成模型&#xff0c;如OpenAI的GPT-3.5。这类模型通过学习大量的文本数据&#xff0c;具备了生成高质量文本的能力&#xff0c;从而引发了一系列关于文本生成真实性的讨论。 正因为…

Mac GoLand打不开(闪退)也不报错

Mac用过GoLand&#xff0c;电脑应用初始化后就打不开了&#xff0c;下其他版本也不行 原因就是之前的配置文件还在需要清理&#xff1a; /Users/你的文件/Library/Preferences/ 配置文件在这个文件下