赞
踩
1.图片验证与缓存依赖:
- <dependencies>
- <!-- 验证码-->
- <dependency>
- <groupId>com.github.whvcse</groupId>
- <artifactId>easy-captcha</artifactId>
- <version>1.6.2</version>
- </dependency>
- <!-- redis-->
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-data-redis</artifactId>
- </dependency>
- <dependency>
- <groupId>org.apache.commons</groupId>
- <artifactId>commons-pool2</artifactId>
- </dependency>
- </dependencies>

2.redis配置:在application.properties中加入配置
- spring.redis.host=localhost
- spring.redis.port=6379
- spring.redis.database= 0
- spring.redis.timeout=1800000
- spring.redis.lettuce.pool.max-active=20
- spring.redis.lettuce.pool.max-wait=-1
- #最大阻塞等待时间(负数表示没限制)
- spring.redis.lettuce.pool.max-idle=5
- spring.redis.lettuce.pool.min-idle=0
- #最小空闲
3.redis工具类:
注意之所以每加static是因为咱们是注入形式调用的,而不是通过类名调用,这里是通过spring对象帮我们调用。注意!!!同时@component注解不要忘记加!
- package com.laoyang.educms.utils;
-
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.data.redis.core.RedisTemplate;
- import org.springframework.stereotype.Component;
- import org.springframework.util.CollectionUtils;
-
- import javax.annotation.Resource;
- import java.util.List;
- import java.util.Map;
- import java.util.Set;
- import java.util.concurrent.TimeUnit;
-
- /**
- * @author:Kevin
- * @create: 2022-10-05 14:30
- * @Description:
- */
- @Component
- public class redisutils {
- @Autowired
- private RedisTemplate<String, Object> redisTemplate;
-
- // =============================common============================
-
- /**
- * 指定缓存失效时间
- *
- * @param key 键
- * @param time 时间(秒)
- */
- public boolean expire(String key, long time) {
- try {
- if (time > 0) {
- redisTemplate.expire(key, time, TimeUnit.SECONDS);
- }
- return true;
- } catch (Exception e) {
- e.printStackTrace();
- return false;
- }
- }
-
- /**
- * 根据key 获取过期时间
- *
- * @param key 键 不能为null
- * @return 时间(秒) 返回0代表为永久有效
- */
- public long getExpire(String key) {
- return redisTemplate.getExpire(key, TimeUnit.SECONDS);
- }
-
-
- /**
- * 判断key是否存在
- *
- * @param key 键
- * @return true 存在 false不存在
- */
- public boolean hasKey(String key) {
- try {
- return redisTemplate.hasKey(key);
- } catch (Exception e) {
- e.printStackTrace();
- return false;
- }
- }
-
-
- /**
- * 删除缓存
- *
- * @param key 可以传一个值 或多个
- */
- @SuppressWarnings("unchecked")
- public void del(String... key) {
- if (key != null && key.length > 0) {
- if (key.length == 1) {
- redisTemplate.delete(key[0]);
- } else {
- redisTemplate.delete(CollectionUtils.arrayToList(key));
- }
- }
- }
-
-
- // ============================String=============================
-
- /**
- * 普通缓存获取
- *
- * @param key 键
- * @return 值
- */
- public Object get(String key) {
- return key == null ? null : redisTemplate.opsForValue().get(key);
- }
-
- /**
- * 普通缓存放入
- *
- * @param key 键
- * @param value 值
- * @return true成功 false失败
- */
-
- public boolean set(String key, Object value) {
- try {
- redisTemplate.opsForValue().set(key, value);
- return true;
- } catch (Exception e) {
- e.printStackTrace();
- return false;
- }
- }
-
-
- /**
- * 普通缓存放入并设置时间
- *
- * @param key 键
- * @param value 值
- * @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
- * @return true成功 false 失败
- */
-
- public boolean set(String key, Object value, long time) {
- try {
- if (time > 0) {
- redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
- } else {
- set(key, value);
- }
- return true;
- } catch (Exception e) {
- e.printStackTrace();
- return false;
- }
- }
-
-
- /**
- * 递增
- *
- * @param key 键
- * @param delta 要增加几(大于0)
- */
- public long incr(String key, long delta) {
- if (delta < 0) {
- throw new RuntimeException("递增因子必须大于0");
- }
- return redisTemplate.opsForValue().increment(key, delta);
- }
-
-
- /**
- * 递减
- *
- * @param key 键
- * @param delta 要减少几(小于0)
- */
- public long decr(String key, long delta) {
- if (delta < 0) {
- throw new RuntimeException("递减因子必须大于0");
- }
- return redisTemplate.opsForValue().increment(key, -delta);
- }
-
-
- // ================================Map=================================
-
- /**
- * HashGet
- *
- * @param key 键 不能为null
- * @param item 项 不能为null
- */
- public Object hget(String key, String item) {
- return redisTemplate.opsForHash().get(key, item);
- }
-
- /**
- * 获取hashKey对应的所有键值
- *
- * @param key 键
- * @return 对应的多个键值
- */
- public Map<Object, Object> hmget(String key) {
- return redisTemplate.opsForHash().entries(key);
- }
-
- /**
- * HashSet
- *
- * @param key 键
- * @param map 对应多个键值
- */
- public boolean hmset(String key, Map<String, Object> map) {
- try {
- redisTemplate.opsForHash().putAll(key, map);
- return true;
- } catch (Exception e) {
- e.printStackTrace();
- return false;
- }
- }
-
-
- /**
- * HashSet 并设置时间
- *
- * @param key 键
- * @param map 对应多个键值
- * @param time 时间(秒)
- * @return true成功 false失败
- */
- public boolean hmset(String key, Map<String, Object> map, long time) {
- try {
- redisTemplate.opsForHash().putAll(key, map);
- if (time > 0) {
- expire(key, time);
- }
- return true;
- } catch (Exception e) {
- e.printStackTrace();
- return false;
- }
- }
-
-
- /**
- * 向一张hash表中放入数据,如果不存在将创建
- *
- * @param key 键
- * @param item 项
- * @param value 值
- * @return true 成功 false失败
- */
- public boolean hset(String key, String item, Object value) {
- try {
- redisTemplate.opsForHash().put(key, item, value);
- return true;
- } catch (Exception e) {
- e.printStackTrace();
- return false;
- }
- }
-
- /**
- * 向一张hash表中放入数据,如果不存在将创建
- *
- * @param key 键
- * @param item 项
- * @param value 值
- * @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
- * @return true 成功 false失败
- */
- public boolean hset(String key, String item, Object value, long time) {
- try {
- redisTemplate.opsForHash().put(key, item, value);
- if (time > 0) {
- expire(key, time);
- }
- return true;
- } catch (Exception e) {
- e.printStackTrace();
- return false;
- }
- }
-
-
- /**
- * 删除hash表中的值
- *
- * @param key 键 不能为null
- * @param item 项 可以使多个 不能为null
- */
- public void hdel(String key, Object... item) {
- redisTemplate.opsForHash().delete(key, item);
- }
-
-
- /**
- * 判断hash表中是否有该项的值
- *
- * @param key 键 不能为null
- * @param item 项 不能为null
- * @return true 存在 false不存在
- */
- public boolean hHasKey(String key, String item) {
- return redisTemplate.opsForHash().hasKey(key, item);
- }
-
-
- /**
- * hash递增 如果不存在,就会创建一个 并把新增后的值返回
- *
- * @param key 键
- * @param item 项
- * @param by 要增加几(大于0)
- */
- public double hincr(String key, String item, double by) {
- return redisTemplate.opsForHash().increment(key, item, by);
- }
-
-
- /**
- * hash递减
- *
- * @param key 键
- * @param item 项
- * @param by 要减少记(小于0)
- */
- public double hdecr(String key, String item, double by) {
- return redisTemplate.opsForHash().increment(key, item, -by);
- }
-
-
- // ============================set=============================
-
- /**
- * 根据key获取Set中的所有值
- *
- * @param key 键
- */
- public Set<Object> sGet(String key) {
- try {
- return redisTemplate.opsForSet().members(key);
- } catch (Exception e) {
- e.printStackTrace();
- return null;
- }
- }
-
-
- /**
- * 根据value从一个set中查询,是否存在
- *
- * @param key 键
- * @param value 值
- * @return true 存在 false不存在
- */
- public boolean sHasKey(String key, Object value) {
- try {
- return redisTemplate.opsForSet().isMember(key, value);
- } catch (Exception e) {
- e.printStackTrace();
- return false;
- }
- }
-
-
- /**
- * 将数据放入set缓存
- *
- * @param key 键
- * @param values 值 可以是多个
- * @return 成功个数
- */
- public long sSet(String key, Object... values) {
- try {
- return redisTemplate.opsForSet().add(key, values);
- } catch (Exception e) {
- e.printStackTrace();
- return 0;
- }
- }
-
-
- /**
- * 将set数据放入缓存
- *
- * @param key 键
- * @param time 时间(秒)
- * @param values 值 可以是多个
- * @return 成功个数
- */
- public long sSetAndTime(String key, long time, Object... values) {
- try {
- Long count = redisTemplate.opsForSet().add(key, values);
- if (time > 0)
- expire(key, time);
- return count;
- } catch (Exception e) {
- e.printStackTrace();
- return 0;
- }
- }
-
-
- /**
- * 获取set缓存的长度
- *
- * @param key 键
- */
- public long sGetSetSize(String key) {
- try {
- return redisTemplate.opsForSet().size(key);
- } catch (Exception e) {
- e.printStackTrace();
- return 0;
- }
- }
-
-
- /**
- * 移除值为value的
- *
- * @param key 键
- * @param values 值 可以是多个
- * @return 移除的个数
- */
-
- public long setRemove(String key, Object... values) {
- try {
- Long count = redisTemplate.opsForSet().remove(key, values);
- return count;
- } catch (Exception e) {
- e.printStackTrace();
- return 0;
- }
- }
-
- // ===============================list=================================
-
- /**
- * 获取list缓存的内容
- *
- * @param key 键
- * @param start 开始
- * @param end 结束 0 到 -1代表所有值
- */
- public List<Object> lGet(String key, long start, long end) {
- try {
- return redisTemplate.opsForList().range(key, start, end);
- } catch (Exception e) {
- e.printStackTrace();
- return null;
- }
- }
-
-
- /**
- * 获取list缓存的长度
- *
- * @param key 键
- */
- public long lGetListSize(String key) {
- try {
- return redisTemplate.opsForList().size(key);
- } catch (Exception e) {
- e.printStackTrace();
- return 0;
- }
- }
-
-
- /**
- * 通过索引 获取list中的值
- *
- * @param key 键
- * @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推
- */
- public Object lGetIndex(String key, long index) {
- try {
- return redisTemplate.opsForList().index(key, index);
- } catch (Exception e) {
- e.printStackTrace();
- return null;
- }
- }
-
-
- /**
- * 将list放入缓存
- *
- * @param key 键
- * @param value 值
- */
- public boolean lSet(String key, Object value) {
- try {
- redisTemplate.opsForList().rightPush(key, value);
- return true;
- } catch (Exception e) {
- e.printStackTrace();
- return false;
- }
- }
-
-
- /**
- * 将list放入缓存
- *
- * @param key 键
- * @param value 值
- * @param time 时间(秒)
- */
- public boolean lSet(String key, Object value, long time) {
- try {
- redisTemplate.opsForList().rightPush(key, value);
- if (time > 0)
- expire(key, time);
- return true;
- } catch (Exception e) {
- e.printStackTrace();
- return false;
- }
-
- }
-
-
- /**
- * 将list放入缓存
- *
- * @param key 键
- * @param value 值
- * @return
- */
- public boolean lSet(String key, List<Object> value) {
- try {
- redisTemplate.opsForList().rightPushAll(key, value);
- return true;
- } catch (Exception e) {
- e.printStackTrace();
- return false;
- }
-
- }
-
-
- /**
- * 将list放入缓存
- *
- * @param key 键
- * @param value 值
- * @param time 时间(秒)
- * @return
- */
- public boolean lSet(String key, List<Object> value, long time) {
- try {
- redisTemplate.opsForList().rightPushAll(key, value);
- if (time > 0)
- expire(key, time);
- return true;
- } catch (Exception e) {
- e.printStackTrace();
- return false;
- }
- }
-
-
- /**
- * 根据索引修改list中的某条数据
- *
- * @param key 键
- * @param index 索引
- * @param value 值
- * @return
- */
-
- public boolean lUpdateIndex(String key, long index, Object value) {
- try {
- redisTemplate.opsForList().set(key, index, value);
- return true;
- } catch (Exception e) {
- e.printStackTrace();
- return false;
- }
- }
-
-
- /**
- * 移除N个值为value
- *
- * @param key 键
- * @param count 移除多少个
- * @param value 值
- * @return 移除的个数
- */
-
- public long lRemove(String key, long count, Object value) {
- try {
- Long remove = redisTemplate.opsForList().remove(key, count, value);
- return remove;
- } catch (Exception e) {
- e.printStackTrace();
- return 0;
- }
-
- }
- }

4.还需要加入配置类,否则启动不起来,一样@component注解不要忘记加!
- package com.laoyang.educms.config;
-
- /**
- * @author:Kevin
- * @create: 2022-10-05 15:38
- * @Description:
- */
-
- import com.fasterxml.jackson.annotation.JsonAutoDetect;
- import com.fasterxml.jackson.annotation.JsonTypeInfo;
- import com.fasterxml.jackson.annotation.PropertyAccessor;
- import com.fasterxml.jackson.databind.ObjectMapper;
- import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
- import org.springframework.context.annotation.Bean;
- import org.springframework.data.redis.connection.RedisConnectionFactory;
- import org.springframework.data.redis.core.RedisTemplate;
- import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
- import org.springframework.data.redis.serializer.StringRedisSerializer;
- import org.springframework.stereotype.Component;
-
- @Component
- public class Config {
-
- @Bean(name = "template")
- public RedisTemplate<String, Object> template(RedisConnectionFactory factory) {
- // 创建RedisTemplate<String, Object>对象
- RedisTemplate<String, Object> template = new RedisTemplate<>();
- // 配置连接工厂
- template.setConnectionFactory(factory);
- // 定义Jackson2JsonRedisSerializer序列化对象
- Jackson2JsonRedisSerializer<Object> jacksonSeial = new Jackson2JsonRedisSerializer<>(Object.class);
- ObjectMapper om = new ObjectMapper();
- // 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和public
- om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
- // 指定序列化输入的类型,类必须是非final修饰的,final修饰的类,比如String,Integer等会报异常
- om.activateDefaultTyping(
-
- LaissezFaireSubTypeValidator.instance ,
- ObjectMapper.DefaultTyping.NON_FINAL,
-
- JsonTypeInfo.As.WRAPPER_ARRAY);
- jacksonSeial.setObjectMapper(om);
- StringRedisSerializer stringSerial = new StringRedisSerializer();
- // redis key 序列化方式使用stringSerial
- template.setKeySerializer(stringSerial);
- // redis value 序列化方式使用jackson
- template.setValueSerializer(jacksonSeial);
- // redis hash key 序列化方式使用stringSerial
- template.setHashKeySerializer(stringSerial);
- // redis hash value 序列化方式使用jackson
- template.setHashValueSerializer(jacksonSeial);
- template.afterPropertiesSet();
- return template;
- }
- }

5.创建service:验证码service:两个方法,一个是生成验证码图片,另一个方法是验证输入的验证码是否正确。
- package com.laoyang.educms.service;
-
- import javax.servlet.http.HttpServletResponse;
- import java.io.IOException;
-
- /**
- * @author:Kevin
- * @create: 2022-10-05 13:50
- * @Description: 验证码
- */
-
- public interface ValidateCodeService {
- /**
- * 生成验证码
- */
- void create(String key, HttpServletResponse response) throws IOException;
-
- /**
- * 校验验证码
- * @param key 前端上送 key
- * @param value 前端上送待校验值
- */
- boolean check(String key, String value);
- }

6.编写验证码service的实现类:注意这里会用到缓存,因为咱们这个redis工具类不是static的,所以这里用的话需要对象注入调用
- package com.laoyang.educms.service.impl;
-
- import com.laoyang.CommonUtils.Contst.ResultCode;
- import com.laoyang.MyException;
- import com.laoyang.educms.entity.CacheObject;
- import com.laoyang.educms.service.ValidateCodeService;
- import com.laoyang.educms.utils.redisutils;
- import com.wf.captcha.ArithmeticCaptcha;
- import com.wf.captcha.base.Captcha;
- import org.apache.commons.lang3.StringUtils;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.http.HttpHeaders;
- import org.springframework.http.MediaType;
- import org.springframework.stereotype.Service;
-
- import javax.servlet.http.HttpServletResponse;
- import java.io.IOException;
-
- /**
- * @author:Kevin
- * @create: 2022-10-05 13:51
- * @Description:
- */
- @Service
- public class ValidateCodeServiceImpl implements ValidateCodeService {
-
- @Autowired
- private redisutils redisutils;
-
- /**
- * 生成验证码图片
- * @param key
- * @param response
- * @throws IOException
- */
- @Override
- public void create(String key, HttpServletResponse response) throws IOException {
- if (StringUtils.isBlank(key)) {
- throw new MyException(ResultCode.ERROR,"验证码不能为空");
- }
-
- response.setContentType(MediaType.IMAGE_PNG_VALUE);
- response.setHeader(HttpHeaders.PRAGMA, "No-cache");
- response.setHeader(HttpHeaders.CACHE_CONTROL, "No-cache");
- response.setDateHeader(HttpHeaders.EXPIRES, 0L);
-
- Captcha captcha = new ArithmeticCaptcha(115, 42);
- captcha.setCharType(2);
-
- String text = captcha.text();
- System.out.println(text);
-
- //设置过期时间为5分种
- redisutils.set(key,text,300);
-
- captcha.out(response.getOutputStream());
-
- }
-
- /**
- * 校验输入的验证码
- * @param key 前端上送 key
- * @param value 前端上送待校验值
- * @return
- */
- @Override
- public boolean check(String key, String value) {
- if (StringUtils.isBlank(value)) {
- throw new MyException(ResultCode.ERROR,"验证码不能为空");
- }
- //根据key从缓存中获取验证码
- String code = (String) redisutils.get(key);
- if (code == null) {
- throw new MyException(ResultCode.ERROR,"验证码已经过期");
- }
- //比对验证码
- if (!StringUtils.equalsIgnoreCase(value,
- code)) {
- throw new MyException(ResultCode.ERROR,"验证码不正确");
- }
- //验证通过,立即从缓存中删除验证码
- redisutils.del(key);
- return true;
-
- }
- }

7.编写一个前端后端数据交互的登录DTO
- package com.laoyang.educms.entity;
-
- import io.swagger.annotations.ApiModel;
- import io.swagger.annotations.ApiModelProperty;
- import lombok.*;
- import lombok.experimental.Accessors;
-
- import javax.validation.constraints.NotEmpty;
-
- /**
- * @author:Kevin
- * @create: 2022-10-05 15:00
- * @Description:
- */
- @Data
- @NoArgsConstructor
- @AllArgsConstructor
- @Accessors(chain = true)
- @ToString(callSuper = true)
- @EqualsAndHashCode(callSuper = false)
- @Builder
- @ApiModel(value = "LoginParamDTO", description = "登录参数")
- public class LoginDto {
-
- @ApiModelProperty(value = "验证码KEY")
- @NotEmpty(message = "验证码KEY不能为空")
- private String key;
- @ApiModelProperty(value = "验证码")
- @NotEmpty(message = "验证码不能为空")
- private String code;
- @ApiModelProperty(value = "账号")
- @NotEmpty(message = "账号不能为空")
- private String account;
- @ApiModelProperty(value = "密码")
- @NotEmpty(message = "密码不能为空")
- private String password;
- @ApiModelProperty(value = "电话号码")
- @NotEmpty(message = "电话号码不能为空")
- private String Mobile;
- }

8.编写验证码视图类
- package com.laoyang.educms.controller;
-
-
- import com.laoyang.CommonUtils.R;
- import com.laoyang.educms.entity.LoginDto;
- import com.laoyang.educms.entity.UcenterMember;
- import com.laoyang.educms.service.UcenterMemberService;
- import com.laoyang.educms.service.ValidateCodeService;
- import io.swagger.annotations.Api;
- import io.swagger.annotations.ApiOperation;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.web.bind.annotation.*;
-
- import javax.servlet.http.HttpServletResponse;
- import java.io.IOException;
-
- /**
- * <p>
- * 会员表 前端控制器
- * </p>
- *
- * @author testjava
- * @since 2022-10-05
- */
- @RestController
- @CrossOrigin
- @Api(value = "UcenterMemberController", tags = "登录")
- @RequestMapping("/educms/member")
- public class UcenterMemberController {
-
-
- @Autowired
- private UcenterMemberService memberService;
-
- @Autowired
- private ValidateCodeService validateCodeService;
-
- /**
- * 登录
- * @param loginDto
- * @return
- */
- @PostMapping("/login")
- public R login(@RequestBody LoginDto loginDto){
- if (validateCodeService.check(loginDto.getKey(), loginDto.getCode())){
- String token = memberService.login(loginDto);
- return R.ok("登录成功").data("token",token);
- }
- return R.error("登录失败");
-
- }
-
- /**
- * 生成验证码
- */
- @ApiOperation(value = "验证码", notes = "验证码")
- @GetMapping(value = "/captcha", produces = "image/png")
- public void captcha(@RequestParam(value = "key") String key,
- HttpServletResponse response) throws IOException {
- this.validateCodeService.create(key, response);
-
- }
-
-
-
- }
-

最终测试实现:
第一个测试生成验证码的接口:

第二个是从redis缓存中通过传入的key获取数据再和前端用户输入的code值进行判断,如果不对,就从新刷新进行验证


10.前端接受时的写法,否则会乱码,需要加上响应类型为blob

11.前端的js代码获取

Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。