1、简介kaptcha
Kaptcha是一个基于SimpleCaptcha的验证码开源项目。
2、实现原理
首先,使用Kaptcha生成一个验证码captcha;
然后,为这个验证码生成一个token,以token为key,captcha为value存在redis中,然后将该token和生成的验证图片传给前端;
最后,在用户登录的时候,前端将这个token,以及用户所输入的验证码传到后台,后台根据token到redis中取值,对比用户所输入的验证码和redis中读取到的验证码是否一致,如果一致,验证通过,不一致,验证失败。
3、示例
1)引入kaptcha依赖(百度搜索 maven kaptcha)
com.github.penggle
kaptcha
2.3.2
2)写一个Kaptcha配置类(网上也有很多使用xml配置文件,可以自行搜索)
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 sjm
* @date 2018-10-26
* 生成验证码的配置
*/
@Configuration
public class KaptchaConfig {
@Bean
public DefaultKaptcha producer(){
Properties properties = new Properties();
properties.put("kaptcha.border", "no");
properties.put("kaptcha.textproducer.font.color", "black");
properties.put("kaptcha.textproducer.char.space", "10");
properties.put("kaptcha.textproducer.char.length","4");
properties.put("kaptcha.image.height","34");
properties.put("kaptcha.textproducer.font.size","25");
properties.put("kaptcha.noise.impl","com.google.code.kaptcha.impl.NoNoise");
Config config = new Config(properties);
DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
defaultKaptcha.setConfig(config);
return defaultKaptcha;
}
}
3)创建一个实体类(目前没有用到,如果是不存在redis中,选择存在数据库里面就需要)
import lombok.Data;
@Data
public class Captcha {
private String token;
private String captcha;
}
4)CaptchaService
import com.geepush.openapi.common.constant.RedisKeys;
import com.geepush.openapiclient.utils.StringRedisUtil;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
@Service
public class CaptchaService {
@Value("${server.session.timeout}")
private Integer timeout;
public Map createToken(String captcha){
//生成一个token
String cToken = UUID.randomUUID().toString();
//生成验证码对应的token 以token为key 验证码为value存在redis中
StringRedisTemplate template = StringRedisUtil.getStringRedisTemplate();
ValueOperations valueOperations = template.opsForValue();
String key = String.format(RedisKeys.CLIENT_TOKEN, cToken);
valueOperations.set(key, captcha);
template.expire(key, timeout, TimeUnit.MINUTES);
Map map = new HashMap<>();
map.put("cToken", cToken);
map.put("expire", timeout);
return map;
}
}
4)controller中
@Autowired
private DefaultKaptcha producer;
@Autowired
private CaptchaService captchaService;
a 获取验证码接口
/**
* 获取验证码
* @param response
* @return
* @throws ServletException
* @throws IOException
*/
@ResponseBody
@POStMapping("/captcha")
public Map captcha(HttpServletResponse response) throws ServletException, IOException {
// 生成文字验证码
String text = producer.createText();
// 生成图片验证码
ByteArrayOutputStream outputStream = null;
BufferedImage image = producer.createImage(text);
outputStream = new ByteArrayOutputStream();
ImageIO.write(image, "jpg", outputStream);
// 对字节数组Base64编码
BASE64Encoder encoder = new BASE64Encoder();
// 生成captcha的token
Map map =captchaService.createToken(text);
map.put("img", encoder.encode(outputStream.toByteArray()));
return map;
}
b 登录接口 (验证验证码)
/**
* 登录返回令牌
* @param username
* @param password
* @param cToken 验证码对应的ctoken
* @param captcha 用户输入的验证码
* @return
*/
@PostMapping("/login")
public ResponseBean login(@RequestParam("username") String username,
@RequestParam("password") String password,
@RequestParam("cToken") String cToken,
@RequestParam("captcha") String captcha) {
User userBean = userService.find(username);
if (userBean == null) {
// throw new UnauthorizedException("用户不存在");
return new ResponseBean(1, "用户不存在");
} else {
//根据传过来的ctoken验证验证码
StringRedisTemplate redisTemplate = StringRedisUtil.getStringRedisTemplate();
ValueOperations operations = redisTemplate.opsForValue();
String cKey = String.format(RedisKeys.CLIENT_TOKEN, cToken);
//判断验证码是否还存在
if(redisTemplate.hasKey(cKey)){
if(operations.get(cKey).equals(captcha)){
//验证通过之后删除对应的key
redisTemplate.delete(cKey);
if (userBean.getPassword().equals(password)) {
String token = JWTUtil.sign(username, password);
//登陆成功后将token存于redis
StringRedisTemplate template = StringRedisUtil.getStringRedisTemplate();
ValueOperations valueOperations = template.opsForValue();
String key = String.format(RedisKeys.CLIENT_TOKEN, username);
valueOperations.set(key, token);
template.expire(key, timeout, TimeUnit.MINUTES);
return new ResponseBean(0, "Login success", token);
} else {
// throw new UnauthorizedException("密码错误");
return new ResponseBean(1, "密码错误");
}
}else {
return new ResponseBean(1, "验证码错误");
}
}else {
return new ResponseBean(1, "验证码不存在");
}
}
}