美文网首页
spring security 使用篇 图形验证码的实现

spring security 使用篇 图形验证码的实现

作者: 怪诞140819 | 来源:发表于2018-10-13 11:12 被阅读143次

    在很多登录功能中,需要用到图形验证码,所以简单的实现下面的图形验证码的功能


    图形验证码

    1.获取图形验证码

    图形验证码的获取实现分为三步

    • 1.生成随机的验证码以及验证码的图片信息
    • 2.将生成的图形验证码存入Session中
    • 3.将生成的验证码图片写入接口的响应中
    @GetMapping("/code/image")
        public void createCode(HttpServletRequest request, HttpServletResponse response) throws IOException {
            //生成随机验证码
            ImageCode  imageCode = imageCodeGenerator.generateImageCode(request);
    
            //将随机数存入session
            sessionStrategy.setAttribute(new ServletWebRequest(request),IMAGE_CODE_KEY,imageCode);
    
            //将生成的图片写到接口的响应中
            ImageIO.write(imageCode.getImage(),"JPEG",response.getOutputStream());
        }
    

    1.1 生成随机验证码

    • 定义生成验证码的接口
    public interface ImageCodeGenerator {
    
        /**
         * 生成图形验证码
         * @param request
         * @return
         */
        ImageCode generateImageCode(HttpServletRequest request);
    }
    
    • 实现图形验证码的生成
    public class DefaultImageGenerator implements ImageCodeGenerator {
    
        private SecurityProperties securityProperties;
    
        @Override
        public ImageCode generateImageCode(HttpServletRequest request) {
            int width = ServletRequestUtils.getIntParameter(request,"width",securityProperties.getValidate().getImage().getWidth());
            int height = ServletRequestUtils.getIntParameter(request,"height",securityProperties.getValidate().getImage().getHeight());
            BufferedImage image = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
            Graphics g = image.getGraphics();
            Random random = new Random();
            g.setColor(getRandColor(200,250));
            g.fillRect(0,0,width,height);
            g.setFont(new Font("Times New Roman",Font.ITALIC,20));
            g.setColor(getRandColor(160,200));
            for(int i=0; i<155;i++){
                int x = random.nextInt(width);
                int y = random.nextInt(height);
                int x1 = random.nextInt(12);
                int y1 = random.nextInt(12);
                g.drawLine(x,y,x+x1,y+y1);
            }
    
            String sRand = "";
            for(int i=0; i<securityProperties.getValidate().getImage().getLength();i++){
                String rand = String.valueOf(random.nextInt(10));
                sRand += rand;
                g.setColor(new Color(20+random.nextInt(110),20+random.nextInt(110),20+random.nextInt(110)));
                g.drawString(rand,13*i+6,16);
            }
            g.dispose();
            return new ImageCode(image,sRand,securityProperties.getValidate().getImage().getExpiredIn());
        }
    
        private Color getRandColor(int fc, int bc) {
            Random random = new Random();
            if(fc>255){
                fc = 255;
            }
            if(bc>255){
                bc = 255;
            }
            int r = fc+random.nextInt(bc-fc);
            int g = fc+random.nextInt(bc-fc);
            int b = fc+random.nextInt(bc-fc);
            return new Color(r,g,b);
        }
    
        public SecurityProperties getSecurityProperties() {
            return securityProperties;
        }
    
        public void setSecurityProperties(SecurityProperties securityProperties) {
            this.securityProperties = securityProperties;
        }
    }
    
    • 其中用到了securityProperties是对相关的属性的定义
     private int width = 130;
    
        private int height = 20;
    
        private int length = 4;
    
        private int expiredIn = 60;
    
        private String urls;
    

    主要是图片的宽度,高度,验证码过期时间,验证码长度,以及过滤的urls,这样我们就生成了图形验证码。
    其实图形验证码和短信验证码的生成逻辑非常的相似,再后面的短信验证码生成再做一个抽象

    2.验证图形验证码

    2.1 创建验证图形验证码的过滤器

    /**
     * @Auther: guaidan
     * @Date: 2018/10/12 13:41
     * @Description:
     */
    public class ValidateFilter extends OncePerRequestFilter implements InitializingBean {
    
        private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();
    
        private AuthenticationFailureHandler authenticationFailureHandler;
    
        private SecurityProperties securityProperties;
    
        private Set<String> urls = new HashSet<>();
    
        private AntPathMatcher pathMatcher = new AntPathMatcher();
    
        public ValidateFilter(AuthenticationFailureHandler authenticationFailureHandler,SecurityProperties securityProperties) throws ServletException {
            this.authenticationFailureHandler = authenticationFailureHandler;
            this.securityProperties = securityProperties;
        }
    
    
        @Override
        public void afterPropertiesSet() throws ServletException{
            super.afterPropertiesSet();
            String[] configUrls = StringUtils.splitByWholeSeparatorPreserveAllTokens(securityProperties.getValidate().getImage().getUrls(),",");
            for (String configUrl:configUrls){
                urls.add(configUrl);
            }
            urls.add("/authentication/form");
    
        }
    
        @Override
        protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
    
            boolean action = false;
            for(String url:urls){
                if(pathMatcher.match(url,request.getRequestURI())){
                    action = true;
                }
            }
    
            if(action){
                    //对图形验证码进行验证
                    try{
                        validate(request);
                    }catch (ValidateImageCodeException e){
                        authenticationFailureHandler.onAuthenticationFailure(request,response,e);
                        return;
                    }
            }
            filterChain.doFilter(request,response);
        }
    
        private void validate(HttpServletRequest request) {
            String imageCode = request.getParameter("imageCode");
            ServletWebRequest servletWebRequest = new ServletWebRequest(request);
            ImageCode sessionImageCode = (ImageCode) sessionStrategy.getAttribute(servletWebRequest, ValidateCodeController.IMAGE_CODE_KEY);
    
            if(StringUtils.isBlank(imageCode)){
                throw new ValidateImageCodeException("验证码为空");
            }
    
            if(null==sessionImageCode){
                throw new ValidateImageCodeException("验证码不存在");
            }
    
            if(LocalDateTime.now().isAfter(sessionImageCode.getExpiredTime())){
                sessionStrategy.removeAttribute(servletWebRequest, ValidateCodeController.IMAGE_CODE_KEY);
                throw new ValidateImageCodeException("验证码已过期");
            }
    
            if(!StringUtils.equalsIgnoreCase(imageCode,sessionImageCode.getCode())){
                sessionStrategy.removeAttribute(servletWebRequest, ValidateCodeController.IMAGE_CODE_KEY);
                throw new ValidateImageCodeException("验证码不匹配");
            }
            sessionStrategy.removeAttribute(servletWebRequest, ValidateCodeController.IMAGE_CODE_KEY);
        }
    }
    
    
    • 第一步,初始化的时候获取到所有需要过滤的url
    • 第二部,对于需要过滤的url做正则匹配,因为我们配置可能是用正则表达式的形式
    • 第三部, 获取到session中存储的图片验证码,然后和用户输入的图形验证码进行比较,比对失败,对相应的错误利用AuthenticationFailureHandler 返回回去。

    相关文章

      网友评论

          本文标题:spring security 使用篇 图形验证码的实现

          本文链接:https://www.haomeiwen.com/subject/ulcoaftx.html