验证码登录流程,别人博客看的
实现过程
-
之前SSM其实就做过了,保存session中,只不过之前是将验证码的值放到session中,而这次是将验证码的值保存到redis中,并给它设置一个过期时间,120秒
-
引入redis和Kaptcha依赖
<!--kaptcha依赖--> <dependency> <groupId>com.github.axet</groupId> <artifactId>kaptcha</artifactId> <version>0.0.9</version> </dependency> <!--redis依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
-
application.properties中r配置redis
spring.redis.port=6379 spring.redis.host=127.0.0.1 spring.redis.database=0
-
kaptcha配置信息
package com.cy.store.config; 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; /** * @author ZhangHailong * @date 2022/5/18 - 12:23 * @project_name Kaptcha配置信息 */ @Configuration public class KaptchaConfig { @Bean public DefaultKaptcha getDDefaultKaptcha() { DefaultKaptcha dk = new DefaultKaptcha(); Properties properties = new Properties(); // 图片边框 properties.setProperty("kaptcha.border", "yes"); // 边框颜色 properties.setProperty("kaptcha.border.color", "105,179,90"); // 字体颜色 properties.setProperty("kaptcha.textproducer.font.color", "red"); // 图片宽 properties.setProperty("kaptcha.image.width", "110"); // 图片高 properties.setProperty("kaptcha.image.height", "40"); // 字体大小 properties.setProperty("kaptcha.textproducer.font.size", "30"); // session key properties.setProperty("kaptcha.session.key", "code"); // 验证码长度 properties.setProperty("kaptcha.textproducer.char.length", "5"); // 字体 properties.setProperty("kaptcha.textproducer.font.names", "宋体,楷体,微软雅黑"); Config config = new Config(properties); dk.setConfig(config); return dk; } }
-
在用户访问登录页面login.html的时候,页面加载完成后,发送请求,获取验证码图片,其中做的处理是,①返回的图片格式用base64编码,②redis中存放的验证码的key的格式是:CAPTCHA+随机数,其value就是验证码的实际值
package com.cy.store.controller; import com.cy.store.common.Const; import com.cy.store.util.JsonResult; import com.google.code.kaptcha.impl.DefaultKaptcha; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; 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.RestController; import sun.misc.BASE64Encoder; import javax.imageio.ImageIO; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.awt.image.BufferedImage; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.UUID; import java.util.concurrent.TimeUnit; /** * @author ZhangHailong * @date 2022/5/18 - 12:25 * @project_name */ @RestController @RequestMapping("kaptcha") public class KaptchaController extends BaseController{ @Autowired DefaultKaptcha defaultKaptcha; @Autowired StringRedisTemplate redisTemplate; @RequestMapping(value = "getKaptchaImage", method = RequestMethod.GET) public JsonResult<Object> getKaptchaImageController(HttpServletResponse httpServletResponse) throws IOException { ByteArrayOutputStream jpegOutputStream = new ByteArrayOutputStream(); String codeKey = ""; try { //生产验证码字符串 String codeValue = defaultKaptcha.createText(); // 随机生成一个codeKey,用于鉴别验证码身份 codeKey = Const.CAPTCHA_K + UUID.randomUUID().toString(); // 将codeKey和codeValue存放到redis中 redisTemplate.opsForValue().set(codeKey, codeValue, 120, TimeUnit.SECONDS); // System.out.println("存放到redis中的codeKey为: " + redisTemplate.opsForValue().get(codeKey)); //使用生产的验证码字符串返回一个BufferedImage对象并转为byte写入到byte数组中 BufferedImage bufferedImage = defaultKaptcha.createImage(codeValue); ImageIO.write(bufferedImage, "jpg", jpegOutputStream); } catch (IllegalArgumentException e) { httpServletResponse.sendError(HttpServletResponse.SC_NOT_FOUND); } // 将图片转为64编码 BASE64Encoder encoder = new BASE64Encoder(); String base64Img = "data:image/jpeg;base64," + encoder.encode(jpegOutputStream.toByteArray()); Map<String, String> map = new HashMap<>(); map.put("codeKey", codeKey); map.put("base64Img", base64Img); return new JsonResult<>(OK, map); } }
-
自定义全局常量
package com.cy.store.common; /** * @author ZhangHailong * @date 2022/5/19 - 10:33 * @project_name 自定义全局常量 */ public class Const { // 验证码标识前缀 public final static String CAPTCHA_K = "CAPTCHA"; // 搜索历史标识前缀 public final static String HISTORY_K = "HISTORY"; }
-
增加用户登录的判断
@RequestMapping(value = "login", method = RequestMethod.POST)
public JsonResult<User> loginUserController(LoginUserVO loginUser, HttpSession session) {
String realCodeValue = redisTemplate.opsForValue().get(Const.CAPTCHA_K + loginUser.getCodeKey());
if (realCodeValue == null) {
throw new KaptchaCodeFailureException("验证码失效");
}
if (!loginUser.getSubmitCodeValue().equals(realCodeValue)) {
throw new VertifyCodeNotMatchException("验证码不正确");
}
User user = new User();
user.setUsername(loginUser.getUsername());
user.setPassword(loginUser.getPassword());
User resuData = iUserService.loginUser(user);
// 将登陆成功的用户的uid,username存到session中
session.setAttribute("uid", resuData.getUid());
session.setAttribute("username", resuData.getUsername());
return new JsonResult<>(OK, resuData);
}
-
在增加验证码的模块后,用户登录需要提交的信息有:用户名、密码、自己输入的验证码、验证码图片对应的key,所以就不能用实体类User去接,而是使用值对象,定义一个值对象LoginUserVO
package com.cy.store.vo; import java.util.Objects; /** * @author ZhangHailong * @date 2022/5/18 - 13:46 * @project_name */ public class LoginUserVO { private String username; private String password; private String codeKey; private String submitCodeValue; // 省略get,set
-
测试验证码接口
-
访问data.base64Img,得到图片
-
在redis可视化工具中查看验证码的有效时间