使用Captcha做验证码,存到数据库中,登录时候后端做判空,过期,错误提示,样式配置
实现思路:调用验证码接口传入uuid获取到验证码,把uuid和验证码传入登录接口,根据数据库中的验证码匹配,验证码登录后删除已使用的验证码,调用验证码接口时会删除小于当前时间的数据(因为存储时间的时候存的时间比系统多一分钟)
pom依赖
<!--验证码-->
<dependency>
<groupId>com.github.axet</groupId>
<artifactId>kaptcha</artifactId>
<version>0.0.9</version>
</dependency>
<!--时间工具-->
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.9.9</version>
</dependency>
建表sql
/*
Navicat Premium Data Transfer
Source Server : 本地人防
Source Server Type : PostgreSQL
Source Server Version : 90507
Source Host : localhost:5432
Source Catalog : postgis_23_sample
Source Schema : public
Target Server Type : PostgreSQL
Target Server Version : 90507
File Encoding : 65001
Date: 24/03/2021 09:12:10
*/
-- ----------------------------
-- Table structure for xj_captcha
-- ----------------------------
DROP TABLE IF EXISTS "public"."xj_captcha";
CREATE TABLE "public"."xj_captcha" (
"uuid" varchar COLLATE "pg_catalog"."default" NOT NULL,
"code" varchar(255) COLLATE "pg_catalog"."default",
"expire_time" timestamp(6)
)
;
-- ----------------------------
-- Primary Key structure for table xj_captcha
-- ----------------------------
ALTER TABLE "public"."xj_captcha" ADD CONSTRAINT "xj_captcha_pkey" PRIMARY KEY ("uuid");
config 配置
/**
* Copyright (c) 2016-2019 人人开源 All rights reserved.
*
* https://www.renren.io
*
* 版权所有,侵权必究!
*/
package com.ph.rfwg.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 Mark sunlightcs@gmail.com
*/
@Configuration
public class KaptchaConfig {
@Bean
public DefaultKaptcha producer() {
Properties properties = new Properties();
//图片边框,合法值yes,no,默认值yes
properties.put("kaptcha.border", "no");
// 字体颜色
properties.put("kaptcha.textproducer.font.color", "black");
//文字间隔
properties.put("kaptcha.textproducer.char.space", "5");
// 图片样式
properties.put("kaptcha.obscurificator.impl", "com.google.code.kaptcha.impl.WaterRipple");
properties.put("kaptcha.textproducer.font.names", "Arial,Courier,cmr10,宋体,楷体,微软雅黑");
// 验证码长度默认是5,下面是设为4,备用
// properties.put("kaptcha.textproducer.char.length", "4");
// 验证码内容,从0123456789数字里随机生成,不写的话默认是数字加字母
properties.put("kaptcha.textproducer.char.string", "0123456789");
Config config = new Config(properties);
DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
defaultKaptcha.setConfig(config);
return defaultKaptcha;
}
}
controller
package com.ph.rfwg.controller;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.ph.rfwg.entity.*;
import com.ph.rfwg.mapper.CaptchaMapper;
import com.ph.rfwg.service.CaptchaService;
import com.ph.rfwg.service.UserService;
import com.ph.rfwg.util.CommUtils;
import com.ph.rfwg.util.HttpCilentUtil;
import com.ph.rfwg.value.AjaxObj;
import com.ph.rfwg.value.DataProperty;
import com.ph.rfwg.value.ReturnValCode;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import com.alibaba.fastjson.JSON;
@CrossOrigin(allowCredentials = "true", origins = "*", maxAge = 3600)
@RestController
@Api(tags = "登录")
public class UserController {
@Value("${urlLogin}")
private String urlLogin;
@Autowired
private CaptchaService captchaService;
@Value("${urlListUserdomsNolayers}")
private String urlListUserdomsNolayers;
@Autowired
private CaptchaMapper captchaMapper;
private final static Logger logger = LoggerFactory.getLogger(UserController.class);
/**
* 登录
* @param userName
* @param password
* @param sysId
* @param request
* @return
* @throws Exception
*/
@ApiOperation(value = "登录")
@RequestMapping(value={"", "/", "/login", "/login.do"}, method={RequestMethod.POST, RequestMethod.GET})
public AjaxObj login(@ApiParam(name = "userName",value = "用户名")String userName,
@ApiParam(name = "password",value = "密码")String password,
@ApiParam(name = "sysId",value = "系统id")String sysId,
@ApiParam(name = "uuid",value = "验证码id")@RequestParam(value = "uuid",required=true) String uuid,
@ApiParam(name = "yzmCode",value = "验证码字符串")@RequestParam(value = "yzmCode",required=true) String yzmCode,
// @ApiParam(name = "type",value = "区分web和app")@RequestParam(value = "type",required=false) String type,
HttpServletRequest request) throws Exception {
HttpSession session = request.getSession();
// if(!CommUtils.isEmpty(type)){
// if(type.equals("1") && CommUtils.isEmpty(uuid) || CommUtils.isEmpty(yzmCode)){
// return new AjaxObj(ReturnValCode.RETURN_VALUE_CODES_FAIL, "请输入验证码","error");
// }
// }
if(CommUtils.isEmpty(uuid) || CommUtils.isEmpty(yzmCode)){
return new AjaxObj(ReturnValCode.RETURN_VALUE_CODES_FAIL, "请输入验证码","error");
}
boolean validateOverTime = captchaService.validateOverTime(uuid, yzmCode);
if(validateOverTime && !CommUtils.isEmpty(uuid) && !CommUtils.isEmpty(yzmCode)){
return new AjaxObj(ReturnValCode.RETURN_VALUE_CODES_FAIL, "验证码已过期","error");
}
boolean validate = captchaService.validate(uuid, yzmCode);
if(!validate && !CommUtils.isEmpty(uuid) && !CommUtils.isEmpty(yzmCode)){
return new AjaxObj(ReturnValCode.RETURN_VALUE_CODES_FAIL, "验证码错误","error");
}
// 根据输入的验证码和数据库中的验证码相比较,如果输入正确则把数据库中的删掉
if(!CommUtils.isEmpty(uuid)){
CaptchaEntity captchaEntity = captchaMapper.selectOne(new QueryWrapper<CaptchaEntity>().eq("uuid", uuid));
String pgCode = captchaEntity.getCode();
if(!CommUtils.isEmpty(pgCode) && yzmCode.equals(pgCode)){
captchaMapper.deleteById(uuid);
}
}
String result = HttpCilentUtil.getRemoteDatas(urlLogin, "userName", userName, "password", password, "sysId", sysId);
AjaxObj ajaxObj = JSON.parseObject(result, AjaxObj.class);
int code = ajaxObj.getCode();
logger.info("result = " + result);
if(code>=0) {
// 调用方法存储session
User user = result2Session(result);
session.setAttribute(DataProperty.LOGIN_USER, user);
User attribute = (User) session.getAttribute(DataProperty.LOGIN_USER);
/*System.err.println(session.getAttribute(DataProperty.LOGIN_USER)+"111111111111111111111");
System.err.println(attribute+"22222222222222222");*/
session.setAttribute(DataProperty.LOGIN_USER_ROLE, user.getRoles().get(0).getName());
return new AjaxObj(ReturnValCode.RTN_VAL_CODE_SUCCESS, "登录成功", user);
} else {
return new AjaxObj(ReturnValCode.RETURN_VALUE_CODES_FAIL, "用户名或密码错误");
}
}
@ApiOperation(value = "登陆成功")
@RequestMapping(value="/loginSuccess", method={RequestMethod.POST, RequestMethod.GET})
public AjaxObj loginSuccess(@ApiParam(name = "token",value = "使用login.do返回的token值调用该接口")String token,
@ApiParam(name = "sysId",value = "验证码")String sysId, HttpServletRequest request, HttpSession session) throws IOException, Exception {
@GetMapping("captcha.jpg")
public void captcha(HttpServletResponse response, @RequestParam(value = "uuid",required = true) String uuid)throws IOException {
response.setHeader("Cache-Control", "no-store, no-cache");
response.setContentType("image/jpeg");
//获取图片验证码
BufferedImage image = captchaService.getCaptcha(uuid);
ServletOutputStream out = response.getOutputStream();
ImageIO.write(image, "jpg", out);
IOUtils.closeQuietly(out);
}
}
service
package com.ph.rfwg.service;
import java.awt.image.BufferedImage;
public interface CaptchaService {
BufferedImage getCaptcha(String uuid);
// 验证验证码是否正确
boolean validate(String uuid, String code);
// 验证验证码是否过期
boolean validateOverTime(String uuid, String code);
}
impl
package com.ph.rfwg.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.google.code.kaptcha.Producer;
import com.ph.rfwg.entity.CaptchaEntity;
import com.ph.rfwg.mapper.CaptchaMapper;
import com.ph.rfwg.service.CaptchaService;
import com.ph.rfwg.util.CommUtils;
import com.ph.rfwg.util.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.awt.image.BufferedImage;
import java.util.Date;
import java.util.List;
@Service
public class CaptchaServiceImpl implements CaptchaService {
@Autowired
private Producer producer;
@Autowired
private CaptchaMapper captchaMapper;
@Override
public BufferedImage getCaptcha(String uuid) {
List<CaptchaEntity> captchaEntities = captchaMapper.selectList(new QueryWrapper<CaptchaEntity>());
if (captchaEntities.size()>0) {
captchaEntities.forEach(captchaEntity1 -> {
if (captchaEntity1.getExpireTime().getTime() < System.currentTimeMillis()) {
captchaMapper.deleteById(captchaEntity1.getUuid());
}
});
}
//生成文字验证码
String code = producer.createText();
CaptchaEntity captchaEntity = new CaptchaEntity();
captchaEntity.setUuid(uuid);
captchaEntity.setCode(code);
// 1分钟后过期
captchaEntity.setExpireTime(DateUtils.addDateMinutes(new Date(), 1));
captchaMapper.insert(captchaEntity);
return producer.createImage(code);
}
//验证码校验 在登录的时候判断
@Override
public boolean validate(String uuid, String code) {
// 验证码错误
CaptchaEntity captchaEntity = captchaMapper.selectOne(new QueryWrapper<CaptchaEntity>().eq("uuid", uuid));
if(captchaEntity == null){
return false;
}
//删除验证码
// captchaMapper.deleteById(uuid);
// List<CaptchaEntity> captchaEntities = captchaMapper.selectList(new QueryWrapper<CaptchaEntity>());
// if (captchaEntities.size()>0) {
// captchaEntities.forEach(captchaEntity1 -> {
// if (captchaEntity.getExpireTime().getTime() < System.currentTimeMillis()) {
// captchaMapper.deleteById(captchaEntity1.getUuid());
// }
// });
// }
if(captchaEntity.getCode().equalsIgnoreCase(code) && captchaEntity.getExpireTime().getTime() >= System.currentTimeMillis()){
return true;
}
return false;
}
//验证码校验 在登录的时候判断
@Override
public boolean validateOverTime(String uuid, String code) {
// 验证码过期
CaptchaEntity captchaEntity = captchaMapper.selectOne(new QueryWrapper<CaptchaEntity>().eq("uuid", uuid));
if(!CommUtils.isEmpty(captchaEntity)){
if(!CommUtils.isEmpty(captchaEntity.getCode()) && captchaEntity.getExpireTime().getTime() < System.currentTimeMillis()){
return true;
}
}else{
return true;
}
return false;
}
}
util
package com.ph.rfwg.value;
import java.io.Serializable;
/**
* 消息对象
*
* @author fiver
*
*/
public class AjaxObj implements Serializable {
/**
*
*/
private static final long serialVersionUID = 4806448810229890854L;
private int code;
private String msg;
private String token;
private Object data;
private Boolean flag;
public AjaxObj() {
}
public Boolean getFlag() {
return flag;
}
public void setFlag(Boolean flag) {
this.flag = flag;
}
public AjaxObj(int code, String msg) {
this.code = code;
this.msg = msg;
}
public AjaxObj(int code, String msg, Object data) {
this.code = code;
this.msg = msg;
this.data = data;
}
public AjaxObj(int code, String msg, Object data, Boolean flag) {
this.code = code;
this.msg = msg;
this.data = data;
this.flag = flag;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
}
/**
* Copyright (c) 2016-2019 人人开源 All rights reserved.
*
* https://www.renren.io
*
* 版权所有,侵权必究!
*/
package com.ph.rfwg.util;
import org.apache.commons.lang.StringUtils;
import org.joda.time.DateTime;
import org.joda.time.LocalDate;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* 日期处理
*
* @author Mark sunlightcs@gmail.com
*/
public class DateUtils {
/** 时间格式(yyyy-MM-dd) */
public final static String DATE_PATTERN = "yyyy-MM-dd";
/** 时间格式(yyyy-MM-dd HH:mm:ss) */
public final static String DATE_TIME_PATTERN = "yyyy-MM-dd HH:mm:ss";
/**
* 日期格式化 日期格式为:yyyy-MM-dd
* @param date 日期
* @return 返回yyyy-MM-dd格式日期
*/
public static String format(Date date) {
return format(date, DATE_PATTERN);
}
/**
* 日期格式化 日期格式为:yyyy-MM-dd
* @param date 日期
* @param pattern 格式,如:DateUtils.DATE_TIME_PATTERN
* @return 返回yyyy-MM-dd格式日期
*/
public static String format(Date date, String pattern) {
if(date != null){
SimpleDateFormat df = new SimpleDateFormat(pattern);
return df.format(date);
}
return null;
}
/**
* 字符串转换成日期
* @param strDate 日期字符串
* @param pattern 日期的格式,如:DateUtils.DATE_TIME_PATTERN
*/
public static Date stringToDate(String strDate, String pattern) {
if (StringUtils.isBlank(strDate)){
return null;
}
DateTimeFormatter fmt = DateTimeFormat.forPattern(pattern);
return fmt.parseLocalDateTime(strDate).toDate();
}
/**
* 根据周数,获取开始日期、结束日期
* @param week 周期 0本周,-1上周,-2上上周,1下周,2下下周
* @return 返回date[0]开始日期、date[1]结束日期
*/
public static Date[] getWeekStartAndEnd(int week) {
DateTime dateTime = new DateTime();
LocalDate date = new LocalDate(dateTime.plusWeeks(week));
date = date.dayOfWeek().withMinimumValue();
Date beginDate = date.toDate();
Date endDate = date.plusDays(6).toDate();
return new Date[]{beginDate, endDate};
}
/**
* 对日期的【秒】进行加/减
*
* @param date 日期
* @param seconds 秒数,负数为减
* @return 加/减几秒后的日期
*/
public static Date addDateSeconds(Date date, int seconds) {
DateTime dateTime = new DateTime(date);
return dateTime.plusSeconds(seconds).toDate();
}
/**
* 对日期的【分钟】进行加/减
*
* @param date 日期
* @param minutes 分钟数,负数为减
* @return 加/减几分钟后的日期
*/
public static Date addDateMinutes(Date date, int minutes) {
DateTime dateTime = new DateTime(date);
return dateTime.plusMinutes(minutes).toDate();
}
/**
* 对日期的【小时】进行加/减
*
* @param date 日期
* @param hours 小时数,负数为减
* @return 加/减几小时后的日期
*/
public static Date addDateHours(Date date, int hours) {
DateTime dateTime = new DateTime(date);
return dateTime.plusHours(hours).toDate();
}
/**
* 对日期的【天】进行加/减
*
* @param date 日期
* @param days 天数,负数为减
* @return 加/减几天后的日期
*/
public static Date addDateDays(Date date, int days) {
DateTime dateTime = new DateTime(date);
return dateTime.plusDays(days).toDate();
}
/**
* 对日期的【周】进行加/减
*
* @param date 日期
* @param weeks 周数,负数为减
* @return 加/减几周后的日期
*/
public static Date addDateWeeks(Date date, int weeks) {
DateTime dateTime = new DateTime(date);
return dateTime.plusWeeks(weeks).toDate();
}
/**
* 对日期的【月】进行加/减
*
* @param date 日期
* @param months 月数,负数为减
* @return 加/减几月后的日期
*/
public static Date addDateMonths(Date date, int months) {
DateTime dateTime = new DateTime(date);
return dateTime.plusMonths(months).toDate();
}
/**
* 对日期的【年】进行加/减
*
* @param date 日期
* @param years 年数,负数为减
* @return 加/减几年后的日期
*/
public static Date addDateYears(Date date, int years) {
DateTime dateTime = new DateTime(date);
return dateTime.plusYears(years).toDate();
}
}