验证码的编写


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 JpaRepositoryImpl userRepository;
    @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));
    }, "请输入正确的验证码!");