网站建设消费调查问卷,wordpress login form,wordpress繁体转简体,建设银行官方网站官网今天给项目换了一个登录页面#xff0c;而这个登录页面设计了验证码#xff0c;于是想着把这个验证码功能实现一下吧。 这篇文章就如何实现登录时的验证码的验证功能结合代码进行详细地介绍#xff0c;以及介绍功能实现的思路。 目录
页面效果
实现思路
生成验证码的控制… 今天给项目换了一个登录页面而这个登录页面设计了验证码于是想着把这个验证码功能实现一下吧。 这篇文章就如何实现登录时的验证码的验证功能结合代码进行详细地介绍以及介绍功能实现的思路。 目录
页面效果
实现思路
生成验证码的控制器类
前端页面代码
localStorage.js
login.html
login.js
后端登录代码
UserLoginDTO.java
UserController.java
UserServiceImpl.java
潜在问题
改进方案 页面效果 登录的时候会把用户名、密码和验证码一起传到后端并对验证码进行验证只有验证码正确才能登录。 实现思路
那么具体是如何实现的呢首先大概介绍一下我实现这个功能的思路
验证码图片的url由后端的一个Controller生成前端请求这个接口的时候根据当前生成一个uuid并把这个uuid在前端缓存起来下一次还是从前端的缓存获取在这里使用的是localStorage。Controller生成验证码之后把前端传过来的uuid通过redis缓存起来这里分两次缓存 缓存uuid以uuid为key缓存验证码这样当点击登录按钮将数据提交到后台登录接口时会从redis中获取uuid然后通过从redis中拿到的uuid去获取验证码和前端用户输入的验证码进行比较。
由于博主也是第一次做这个功能就随便在网上找了一个生成验证码的工具easy-captcha
!--生成验证码工具--
dependencygroupIdcom.github.whvcse/groupIdartifactIdeasy-captcha/artifactIdversion1.6.2/version
/dependency 生成验证码的控制器类
CaptchaController.java
package cn.edu.sgu.www.mhxysy.controller;import cn.edu.sgu.www.mhxysy.annotation.AnonymityAccess;
import cn.edu.sgu.www.mhxysy.config.CaptchaConfig;
import cn.edu.sgu.www.mhxysy.exception.GlobalException;
import cn.edu.sgu.www.mhxysy.restful.ResponseCode;
import cn.edu.sgu.www.mhxysy.util.UserUtils;
import com.wf.captcha.GifCaptcha;
import com.wf.captcha.SpecCaptcha;
import com.wf.captcha.base.Captcha;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;import javax.servlet.http.HttpServletResponse;
import java.io.IOException;/*** author heyunlin* version 1.0*/
Slf4j
RestController
Api(tags 验证码管理)
RequestMapping(value /captcha, produces application/json;charsetutf-8)
public class CaptchaController {private final CaptchaConfig captchaConfig;private final StringRedisTemplate stringRedisTemplate;Autowiredpublic CaptchaController(CaptchaConfig captchaConfig, StringRedisTemplate stringRedisTemplate) {this.captchaConfig captchaConfig;this.stringRedisTemplate stringRedisTemplate;}/*** 生成验证码* param type 验证码图片类型* param uuid 前端生成的uuid*/AnonymityAccessApiOperation(生成验证码)RequestMapping(value /generate, method RequestMethod.GET)public void generate(RequestParam String type, RequestParam String uuid) throws IOException {// 获取HttpServletResponse对象HttpServletResponse response UserUtils.getResponse();// 设置请求头response.setContentType(image/gif);response.setDateHeader(Expires, 0);response.setHeader(Pragma, No-cache);response.setHeader(Cache-Control, no-cache);Captcha captcha;Integer width captchaConfig.getWidth();Integer height captchaConfig.getHeight();switch (type) {case png:captcha new SpecCaptcha(width, height);break;case gif:captcha new GifCaptcha(width, height);break;default:throw new GlobalException(ResponseCode.BAD_REQUEST, 不合法的验证码类型 type);}captcha.setLen(4);captcha.setCharType(Captcha.TYPE_DEFAULT);String code captcha.text();log.debug(生成的验证码{}, code);// 保存uuidstringRedisTemplate.opsForValue().set(uuid, uuid);stringRedisTemplate.opsForValue().expire(uuid, 5, TimeUnit.MINUTES);// 缓存验证码stringRedisTemplate.opsForValue().set(uuid, code);stringRedisTemplate.opsForValue().expire(uuid, 5, TimeUnit.MINUTES);// 输出图片流captcha.out(response.getOutputStream());}} 前端页面代码
localStorage.js
/*** 保存数据到localStorage* param name 数据的名称* param value 数据的值*/
function storage(name, value) {localStorage.setItem(name, value);
}/*** localStorage根据name获取value* param name 数据的名称*/
function getStorage(name) {return localStorage.getItem(name);
} login.html
!DOCTYPE html
html xmlnshttp://www.w3.org/1999/xhtmlheadmeta http-equivContent-Type contenttext/html; charsetUTF-8title梦幻西游手游管理登录/titlelink relstylesheet href/css/login.cssscript src/js/public/jquery.min.js/scriptscript src/js/public/util.js/scriptscript src/js/public/localStorage.js/scriptscript src/js/login.js/script/headbody styleoverflow:hiddendiv classpagewrapdiv classmaindiv classheader/divdiv classcontentdiv classcon_left/divdiv classcon_rightdiv classcon_r_topa hrefjavascript: classleft下载游戏/aa hrefjavascript: classright登录管理/a/divulli classcon_r_left styledisplay:none;div classerweimadiv classqrcodediv idoutputimg src/images/login/mhxysy.png //div/div/divdiv styleheight:70px;p扫码下载梦幻西游手游/p/div/lili classcon_r_right styledisplay:block;divdiv classuserdivspan classuser-icon/spaninput typetext idlogin_username //divdivspan classmima-icon/spaninput typepassword idlogin_password //divdivspan classyzmz-icon/spaninput typetext idcode / img idcaptcha alt看不清点击更换 //div/divbrbutton idbtn_Login typebutton登 录/button/div/li/ul/div/div/div/div/body
/html login.js
/*** 禁止输入空格*/
function preventSpace() {let event window.event;if(event.keyCode 32) {event.returnValue false;}
}// 登录
function login() {let username $(#login_username).val();let password $(#login_password).val();let code $(#code).val();if (!username) {alert(请输入用户名);$(#login_username).focus();} else if (!password) {alert(请输入密码);$(#login_password).focus();} else if (!code) {alert(请输入验证码);} else {post(/user/login, {username: username,password: password,code: code}, function() {location.href /index.html;}, function (res) {if (res res.responseJSON) {let response res.responseJSON;if (res.status res.status 404) {let message;if(response.path) {message 路径 response.path 不存在。;} else {message response.message;}alert(message);} else {alert(response.message);}}});}
}$(function() {$(#login_username).keydown(function() {preventSpace();}).attr(placeholder, 请输入用户名);/*** 给密码输入框绑定回车登录事件*/$(#login_password).keydown(function(event) {if(event.keyCode 13) {login();}preventSpace();}).attr(placeholder, 请输入密码);$(#code).keydown(function() {preventSpace();}).attr(placeholder, 验证码);// 获取uuidlet uuid getStorage(uuid);if (!uuid) {uuid new Date().getTime();storage(uuid, uuid);}$(#captcha).attr(src, /captcha/generate?typepnguuid uuid);// 点击登录按钮$(#btn_Login).on(click, function () {login();});$(.content .con_right .left).on(click, function () {$(this).css({color: #333333,border-bottom: 2px solid #2e558e});$(.content .con_right .right).css({color: #999999,border-bottom: 2px solid #dedede});$(.content .con_right ul .con_r_left).css(display, block);$(.content .con_right ul .con_r_right).css(display, none);});$(.content .con_right .right).on(click, function () {$(this).css({color: #333333,border-bottom: 2px solid #2e558e});$(.content .con_right .left).css({color: #999999,border-bottom: 2px solid #dedede});$(.content .con_right ul .con_r_right).css(display, block);$(.content .con_right ul .con_r_left).css(display, none);});}); 后端登录代码
UserLoginDTO.java
package cn.edu.sgu.www.mhxysy.dto.system;import lombok.Data;import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.io.Serializable;/*** author heyunlin* version 1.0*/
Data
public class UserLoginDTO implements Serializable {private static final long serialVersionUID 18L;/*** 验证码*/NotNull(message 验证码不允许为空)NotEmpty(message 验证码不允许为空)private String code;/*** 用户名*/NotNull(message 用户名不允许为空)NotEmpty(message 用户名不允许为空)private String username;/*** 密码*/NotNull(message 密码不允许为空)NotEmpty(message 密码不允许为空)private String password;
}UserController.java
package cn.edu.sgu.www.mhxysy.controller.system;import cn.edu.sgu.www.mhxysy.annotation.AnonymityAccess;
import cn.edu.sgu.www.mhxysy.annotation.Exclusion;
import cn.edu.sgu.www.mhxysy.dto.system.UserLoginDTO;
import cn.edu.sgu.www.mhxysy.restful.JsonResult;
import cn.edu.sgu.www.mhxysy.service.system.UserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;/*** author heyunlin* version 1.0*/
Exclusion
RestController
Api(tags 用户管理)
RequestMapping(path /user, producesapplication/json;charsetutf-8)
public class UserController {private final UserService userService;Autowiredpublic UserController(UserService userService) {this.userService userService;}AnonymityAccessApiOperation(登录认证)RequestMapping(value /login, method RequestMethod.POST)public JsonResultVoid login(Validated UserLoginDTO loginDTO) {userService.login(loginDTO);return JsonResult.success();}/*省略的其他代码*/} UserServiceImpl.java
package cn.edu.sgu.www.mhxysy.service.system.impl;import cn.edu.sgu.www.mhxysy.dto.system.UserLoginDTO;
import cn.edu.sgu.www.mhxysy.entity.system.User;
import cn.edu.sgu.www.mhxysy.entity.system.UserLoginLog;
import cn.edu.sgu.www.mhxysy.exception.GlobalException;
import cn.edu.sgu.www.mhxysy.feign.FeignService;
import cn.edu.sgu.www.mhxysy.redis.RedisRepository;
import cn.edu.sgu.www.mhxysy.restful.ResponseCode;
import cn.edu.sgu.www.mhxysy.service.system.UserService;
import cn.edu.sgu.www.mhxysy.util.IpUtils;
import cn.edu.sgu.www.mhxysy.util.StringUtils;
import cn.edu.sgu.www.mhxysy.util.UserUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;import java.time.LocalDateTime;/*** author heyunlin* version 1.0*/
Slf4j
Service
public class UserServiceImpl implements UserService {private final FeignService feignService;private final RedisRepository redisRepository;private final StringRedisTemplate stringRedisTemplate;Value(${syslog.enable})private boolean enable;Autowiredpublic UserServiceImpl(FeignService feignService,RedisRepository redisRepository,StringRedisTemplate stringRedisTemplate) {this.feignService feignService;this.redisRepository redisRepository;this.stringRedisTemplate stringRedisTemplate;}Overridepublic void login(UserLoginDTO loginDTO) {String code loginDTO.getCode();String uuid stringRedisTemplate.opsForValue().get(uuid);// 得到的uuid为空则获取验证码到登录之间的时间已经过了5分钟uuid已经过期if (uuid null) {throw new GlobalException(ResponseCode.BAD_REQUEST, 验证码已失效请刷新页面重新获取~);}if (!code.equalsIgnoreCase(stringRedisTemplate.opsForValue().get(uuid))) {throw new GlobalException(ResponseCode.BAD_REQUEST, 验证码错误~);}// 得到用户名String username loginDTO.getUsername();log.debug(用户{}正在登录..., username);// 查询用户信息如果用户被锁定提前退出User user feignService.selectByUsername(username);if (user ! null) {if (user.getEnable()) {// shiro登录认证UsernamePasswordToken token new UsernamePasswordToken(username, loginDTO.getPassword());Subject subject UserUtils.getSubject();subject.login(token);// 设置session失效时间永不超时subject.getSession().setTimeout(-1001);// 修改管理员上一次登录时间User usr new User();usr.setId(user.getId());usr.setLastLoginTime(LocalDateTime.now());feignService.updateById(usr);// 如果开启了系统日志if (enable) {// 添加管理员登录历史UserLoginLog loginLog new UserLoginLog();loginLog.setId(StringUtils.uuid());loginLog.setUserId(user.getId());loginLog.setLoginTime(LocalDateTime.now());loginLog.setLoginIp(IpUtils.getLocalHostAddress());loginLog.setLoginHostName(IpUtils.getLocalHostName());feignService.saveLoginLog(loginLog);}// 从redis中删除用户权限redisRepository.remove(username);// 查询用户的权限信息并保存到redisredisRepository.save(username);} else {throw new GlobalException(ResponseCode.FORBIDDEN, 账号已被锁定禁止登录);}} else {throw new GlobalException(ResponseCode.NOT_FOUND, 用户名不存在~);}}} 潜在问题
这样的设计会有一个问题uuid这个key有可能会被其他用户修改但是验证码并不会被修改。 改进方案
目前正在找解决方案~ 好了文章就分享到这里了看完要是觉得对你有所帮助不要忘了点赞收藏哦~