验证码的编写
1.js的编写
//更换图片 function changeImg(){ var imgSrc = $("#imgObj"); var src = imgSrc.attr("src"); imgSrc.attr("src",chgUrl(src)); } //时间戳 //为了使每次生成图片不一致,即不让浏览器读缓存,所以需要加上时间戳 function chgUrl(url){ var timestamp = (new Date()).valueOf(); if((url.indexOf("&")>=0)){ url = url + "×tamp=" + timestamp; }else{ url = url + "?timestamp=" + timestamp; } return url; }
2.生成验证码图片的代码
import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.imageio.ImageIO; import javax.servlet.ServletOutputStream; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.awt.*; import java.awt.image.BufferedImage; import java.io.IOException; import java.util.Random; /** * Created by funian on 2018/7/12. */ @WebServlet(urlPatterns="/verifyCodeServlet", description="生成验证码") public class VerifyCodeServlet extends HttpServlet { private Logger LOGGER = LoggerFactory.getLogger(VerifyCodeServlet.class); private static final int WIDTH = 70;// 验证码图片的宽度。 private static final int HEIGHT = 34;// 验证码图片的高度。 private static final int CODE_COUNT = 4;// 验证码字符个数 private Random r = new Random(); // 定义有那些字体 { "宋体", "华文楷体", "黑体", "微软雅黑", "楷体_GB2312" }; private String[] fontNames = { "华文楷体"}; // 定义有那些验证码的随机字符 private String codes = "123456789abcdefghjkmnopqrstuvwxyzABCDEFGHJKLMNOPQRSTUVWXYZ"; // 用于gettext 方法 获得生成的验证码文本 private String text = ""; @Override protected void service(HttpServletRequest req, HttpServletResponse resp){ //生成图片 BufferedImage buffImg = createImage(); // 将四位的验证码保存到Session中 String validateCode = getText(); HttpSession session = req.getSession(); session.setAttribute("validateCode", validateCode); // 禁止图像缓存。 resp.setHeader("Pragma", "no-cache"); resp.setHeader("Cache-Control", "no-cache"); resp.setDateHeader("Expires", 0); resp.setContentType("image/jpeg"); // 将图像输出到Servlet输出流中。 try (ServletOutputStream sos = resp.getOutputStream()){ ImageIO.write(buffImg, "jpeg", sos); } catch (IOException e) { LOGGER.error("生成验证码异常", e); } } // 得到验证码的文本 后面是用来和用户输入的验证码 检测用 public String getText() { return text; } // 生成随机颜色 private Color randomColor() { // int red = r.nextInt(150); // int green = r.nextInt(150); // int blue = r.nextInt(150); return new Color(44, 47, 250); } // 生成随机字体 private Font randomFont() { int index = r.nextInt(fontNames.length); String fontName = fontNames[index];//生成随机的字体名称 int style = r.nextInt(3);//生成随机的样式, 0(无样式), 1(粗体), 2(斜体), 3(粗体+斜体) int size = r.nextInt(5) + 24;//生成随机字号, 24 ~ 28 return new Font(fontName, style, size); } // 画干扰线 private void drawLine(BufferedImage image) { int num = 3;//一共画3条 Graphics2D g2 = (Graphics2D) image.getGraphics(); for (int i = 0; i < num; i++) { int x1 = r.nextInt(WIDTH); int y1 = r.nextInt(HEIGHT); int x2 = r.nextInt(WIDTH); int y2 = r.nextInt(HEIGHT); g2.setStroke(new BasicStroke(1.5F)); g2.setColor(Color.blue);//干扰线是蓝色 g2.drawLine(x1, y1, x2, y2); } } // 得到codes的长度内的随机数 并使用charAt 取得随机数位置上的codes中的字符 private char randomChar() { int index = r.nextInt(codes.length()); return codes.charAt(index); } // 创建BufferedImage private BufferedImage getImage () { BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB); Graphics2D g2 = (Graphics2D)image.getGraphics(); g2.setColor(Color.WHITE); g2.fillRect(0, 0, WIDTH, HEIGHT); return image; } // 创建一张验证码的图片 public BufferedImage createImage() { BufferedImage image = getImage (); Graphics2D g2 = (Graphics2D)image.getGraphics(); StringBuilder sb = new StringBuilder(); // 向图中画字符 for (int i = 0; i < CODE_COUNT; i++) { String s = randomChar() + ""; sb.append(s); float x = i * 1.0F * WIDTH / 4;//设置当前字符的x轴坐标 g2.setFont(randomFont());//设置随机字体 g2.setColor(randomColor());//设置随机颜色 g2.drawString(s, x, HEIGHT - 5);//画图 } this.text = sb.toString(); //添加干扰线 drawLine(image); // 返回图片 return image; } }
3.拦截器的验证码代码
import im.lsn.framework.jpa.JpaRepositoryImpl; import im.lsn.framework.shrio.CustomSecurityException; import im.lsn.oss.exhibition.entity.QTbUser; import im.lsn.oss.exhibition.entity.TbUser; import im.lsn.oss.exhibition.entity.enumerate.LoginType; import im.lsn.oss.exhibition.service.ClickedCountService; import im.lsn.oss.exhibition.service.CustomizedToken; import im.lsn.oss.exhibition.service.SecurityService; import im.lsn.oss.exhibition.service.UserLoginToken; import org.apache.commons.lang3.StringUtils; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.IncorrectCredentialsException; import org.apache.shiro.authc.UnknownAccountException; import org.apache.shiro.subject.Subject; import org.apache.shiro.web.filter.authc.FormAuthenticationFilter; import org.apache.shiro.web.servlet.ShiroHttpServletRequest; import org.apache.shiro.web.util.WebUtils; import org.springframework.beans.factory.annotation.Autowired; import javax.annotation.PostConstruct; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; public class AdminFilter extends FormAuthenticationFilter { protected String getCaptcha(ServletRequest request) { return WebUtils.getCleanParam(request, "captcha"); } public AdminFilter() { } @Autowired private SecurityService securityService; @Autowired private ClickedCountService clickedCountService; @PersistenceContext protected EntityManager entityManager; private JpaRepositoryImpluserRepository; @PostConstruct public void initSecurityService() { this.userRepository = new JpaRepositoryImpl (TbUser.class, entityManager); } @Override protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception { Subject subject = SecurityUtils.getSubject(); if (subject != null) { UserLoginToken loginToken = (UserLoginToken) subject.getPrincipal(); if (loginToken != null && loginToken.getType().equalsIgnoreCase(LoginType.FRONT.toString())) { securityService.logout(); } } return super.preHandle(request, response); } protected CustomizedToken createToken(ServletRequest request, ServletResponse response) { String username = this.getUsername(request); String password = this.getPassword(request); String captcha = this.getCaptcha(request); boolean rememberMe = this.isRememberMe(request); String host = this.getHost(request); String type = LoginType.ADMIN.toString(); return new CustomizedToken(username, password.toCharArray(), rememberMe, host, captcha, type); } protected void setFailureAttribute(ServletRequest request, AuthenticationException ae) { String failureMessage = this.getFailureKeyAttribute() + ".message"; if (ae instanceof CustomSecurityException) { if(StringUtils.equals("用户名或密码错误",ae.getMessage())){ QTbUser qUser = QTbUser.tbUser; TbUser user = userRepository.findOne(qUser.username.eq(((ShiroHttpServletRequest) request).getCookies()[1].getValue())); Integer count = user.getClickedCount(); count++; clickedCountService.updateClickedCount(user.getId(),count); } request.setAttribute(failureMessage, ae.getMessage()); } if (ae instanceof UnknownAccountException) { request.setAttribute(failureMessage, "用户不存在 "); } if (ae instanceof IncorrectCredentialsException) { request.setAttribute(failureMessage, "密码错误"); } super.setFailureAttribute(request, ae); } @Override protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception { String failureMessage = this.getFailureKeyAttribute() + ".message"; // 在这里进行验证码的校验 HttpServletRequest httpServletRequest = (HttpServletRequest) request; HttpSession session = httpServletRequest.getSession(); // 取出验证码 String validateCode = (String) session.getAttribute("validateCode"); // 取出页面的验证码 // 输入的验证和session中的验证进行对比 String randomcode = httpServletRequest.getParameter("veryCode"); if (StringUtils.isNotBlank(randomcode)&& StringUtils.isNotBlank(validateCode)&&!randomcode.equalsIgnoreCase(validateCode)){ request.setAttribute(failureMessage,"验证码错误"); QTbUser qUser = QTbUser.tbUser; TbUser user = userRepository.findOne(qUser.username.eq(((ShiroHttpServletRequest) request).getCookies()[1].getValue())); if(null!=user){ Integer count = user.getClickedCount(); count++; clickedCountService.updateClickedCount(user.getId(),count); } return true; } return super.onAccessDenied(request, response); } @Override protected void issueSuccessRedirect(ServletRequest request, ServletResponse response) throws Exception { QTbUser qUser = QTbUser.tbUser; TbUser user = userRepository.findOne(qUser.username.eq(((ShiroHttpServletRequest) request).getCookies()[1].getValue())); clickedCountService.updateClickedCount(user.getId(),0); WebUtils.issueRedirect(request, response, this.getSuccessUrl(), null, true); } }
4.前端的验证码
<div class="row"> <div class="col-xs-9"> <div class="form-group has-feedback"> <input id="veryCode" name="veryCode" maxlength="4" class="form-control codeVal" type="text" placeholder="验证码" required /> <span class="glyphicon glyphicon-option-horizontal form-control-feedback">span> div> div> <div class="col-xs-3"> <img id="imgObj" align="right" alt="" onclick="changeImg()" src="${ctxRoot}/verifyCodeServlet"/> div> div>
5.前端的验证码的js格式验证
// 验证码校验
jQuery.validator.addMethod("codeVal", function(value, element) {
var reg = /^[a-zA-Z0-9]{4}$/;
return this.optional(element) || (reg.test(value));
}, "请输入正确的验证码!");