Springboot集成Kaptcha实现前后端分离的验证码功能

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, "验证码不存在");
            }
        }
    }

 

你可能感兴趣的:(Springboot集成Kaptcha实现前后端分离的验证码功能)