首页 > 编程学习 > spring-security入门10---图形验证码(二)----重构【验证码生成逻辑可配置 验证码基本参数可配置】

文章目录

  • 1.重构内容和逻辑介绍
    • 2. 代码开发
    • 2.1 将图形验证码的生成逻辑从原来请求获取图形验证码的逻辑中抽离出来
      • 2.1.1 抽离出图形验证码生成逻辑后的获取图形验证码接口
      • 2.1.2 抽离出的图形验证码生成逻辑
    • 2.2 验证码基本参数可配置
    • 2.3 验证码基本参数配置(yml和请求url上)
    • 2.4 ★★★让开发者可以选择使用自己的图形验证码生成逻辑★★★
  • 3 测试
    • 3.1 验证码基本参数可配置
    • 3.2 验证码生成逻辑可配置

项目源码地址 https://github.com/nieandsun/security

1.重构内容和逻辑介绍

本篇文章是对上篇文章所写代码的重构,重构内容包括以下两点

  • 验证码生成逻辑可配置
  • 验证码基本参数可配置

验证码基本参数可配置分为三级

  • 第一级为默认配置
  • 第二级应用级配置 — 在yml或者properties配置文件里进行配置
  • 第三级请求级配置 — 即通过请求传入不同的参数,生成不同的图形验证码

需要做的具体工作

  • 将图形验证码的生成逻辑从原来请求获取图形验证码的逻辑中抽离出来
  • 将图形验证码的基本参数高度、长度、位数、过期时间从验证码的生成逻辑中抽离出来使其可配置
  • 让开发者可以选择使用自己的图形验证码生成逻辑

2. 代码开发

2.1 将图形验证码的生成逻辑从原来请求获取图形验证码的逻辑中抽离出来

2.1.1 抽离出图形验证码生成逻辑后的获取图形验证码接口

@RestController
public class ValidateCodeController {

    public static final String SESSION_KEY = "SESSION_KEY_IMAGE_CODE";
    //session操作工具类
    private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();

    //验证码生成逻辑封装类
    @Autowired
    private ValidateCodeGenerator imageCodeGenerator;
    
    @GetMapping("/code/image")
    public void createImageCode(HttpServletRequest request, HttpServletResponse response) throws IOException {
        //1.生成ImageCode对象
        ImageCode imageCode = imageCodeGenerator.generate(new ServletWebRequest(request));
        //2.将ImageCode对象写入到session
        sessionStrategy.setAttribute(new ServletWebRequest(request), SESSION_KEY, imageCode);
        //3.将验证码写回到浏览器
        ImageIO.write(imageCode.getImage(), "JPEG", response.getOutputStream());
    }
}

2.1.2 抽离出的图形验证码生成逻辑

  • 验证码生成接口
package com.nrsc.security.core.validate.code;
import org.springframework.web.context.request.ServletWebRequest;
/**
 * Created By: Sun Chuan
 * Created Date: 2019/7/6 17:24
 * Description: 以后还会有别的验证码生成逻辑,故将其统一定义出来
 */
public interface ValidateCodeGenerator {
    ImageCode generate(ServletWebRequest request);
}
  • 验证码生成具体实现
package com.nrsc.security.core.validate.code;

import com.nrsc.security.core.properties.SecurityProperties;
import lombok.Data;
import org.springframework.web.bind.ServletRequestUtils;
import org.springframework.web.context.request.ServletWebRequest;

import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.Random;

/**
 * Created By: Sun Chuan
 * Created Date: 2019/7/6 17:25
 * Description:
 *          (1)请求中可以传入高度和宽度
 *          (2)配置文件中可以配置高度、宽度、验证码的位数和过期时间
 */
@Data
public class ImageCodeGenerator implements ValidateCodeGenerator {

    public SecurityProperties securityProperties;

    @Override
    public ImageCode generate(ServletWebRequest request) {
        //ServletRequestUtils工具的作用是
        //如果可以获取到请求中name为"width"的变量,将其转为int类型并作为返回值
        //如果获取不到请求中name为"width"的变量,则将第三个参数进行返回即下面的securityProperties.getCode().getImage().getWidth()
        int width = ServletRequestUtils.getIntParameter(request.getRequest(), "width",
                securityProperties.getCode().getImage().getWidth());

        int height = ServletRequestUtils.getIntParameter(request.getRequest(), "height",
                securityProperties.getCode().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 xl = random.nextInt(12);
            int yl = random.nextInt(12);
            g.drawLine(x, y, x + xl, y + yl);
        }

        String sRand = "";
        for (int i = 0; i < securityProperties.getCode().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.getCode().getImage().getExpireIn());
    }

    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);
    }
}

2.2 验证码基本参数可配置

使用的是三级结构封装代码如下

  • 最外层 — 封装了所有的springsecurity相关的参数,包括之前的浏览器参数
package com.nrsc.security.core.properties;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

/**
 * "大盒子"类-----------------用于统一管理项目中所有由yml或properties文件传入的变量值
 * Created By: Sun Chuan
 * Created Date: 2019/6/20 22:13
 */
@Component //将此类注入到spring容器中
@Data //不用写get set方法了
@ConfigurationProperties(prefix = "nrsc.security") //指定以nrsc.security开头的配置会射入到该类中
public class SecurityProperties {
    //封装浏览器相关的属性
    private BrowserProperties browser = new BrowserProperties();
    //验证码相关的属性---可能包含图形验证码,短信验证码等,所以对其进行了又一次封装
    private ValidateCodeProperties code = new ValidateCodeProperties();
}
  • 中间层 — 是对所有验证码的封装,现在只有图形验证码,以后可能会有短信验证码等
package com.nrsc.security.core.properties;
import lombok.Data;

/**
 * Created By: Sun Chuan
 * Created Date: 2019/7/6 16:51
 * Description: 验证码相关的封装类,之后还会有其他的验证码,所以将图片验证码,短信验证码等统一封装到该类里
 */
@Data
public class ValidateCodeProperties {
    private ImageCodeProperties image = new ImageCodeProperties();
}
  • 最里层 — 真正与图形验证码相关内容的封装,包括高度、宽度、位数和超时时间
package com.nrsc.security.core.properties;

import lombok.Data;

/**
 * Created By: Sun Chuan
 * Created Date: 2019/7/6 16:50
 */
@Data
public class ImageCodeProperties {
    /**验证码的宽度*/
    private int width = 67;
    /**验证码的高度*/
    private int height = 23;
    /**验证码的位数*/
    private int length = 4;
    /**验证码过期时间*/
    private int expireIn = 60;
}

2.3 验证码基本参数配置(yml和请求url上)

应用级配置 — 在yml或者properties配置文件里进行配置

nrsc:
  security:
    code:
      image:
        length: 6 #图形验证码的位数为6,将覆盖默认配置中的4
        width: 100 #图形验证码的宽度为100,将覆盖默认配置中的67,但由于请求中设置的宽度为200,
        		   #所以真正的宽度将为200

请求级配置 — 即通过请求传入不同的参数,生成不同的图形验证码

        <tr>
            <td>图形验证码:</td>
            <td>
                <input type="text" name="imageCode">
                <!--指定图形验证码的宽度为200,该配置为请求级配置,优先级最高-->
                <img src="/code/image?width=200">
            </td>
        </tr>

2.4 ★★★让开发者可以选择使用自己的图形验证码生成逻辑★★★

  • 通过配置文件指定当开发者实现了自己的图形验证码生成逻辑时,可以覆盖我们默认的生成逻辑
package com.nrsc.security.core.validate.code;

import com.nrsc.security.core.properties.SecurityProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * Created By: Sun Chuan
 * Created Date: 2019/7/6 17:29
 */
@Configuration
public class ValidateCodeBeanConfig {
    @Autowired
    private SecurityProperties securityProperties;

    //@ConditionalOnMissingBean注解后面可以是name也可以是Type(XXX.class)
    //如果为name表示当spring容器里没有该名字的类时,被@ConditionalOnMissingBean注解的bean才可以注册到spring容器
    //如果为type表示当spring容器里没有该类型的类时,被@ConditionalOnMissingBean注解的bean才可以注册到spring容器

    //假如业务觉得我们提供的代码生成器太low,想换一个高大上的,比较好的做法不是随意改我们提供的默认代码
    //而是自己实现一套逻辑覆盖掉我们的逻辑------》实际开发中这叫“增量开发”

    //记录一个问题---------------------------------------之后会慢慢去研究----------------------------------------
    /***
     * 其实我还一直有一个疑惑----》
     *   比如说进行认证成功的代码开发时,只要我们实现AuthenticationSuccessHandler接口,当认证成功后就会走我们定义的
     *   逻辑----》这是为什么???
     *   肯定不是使用了@ConditionalOnMissingBean,因为使用该注解的话,spring-security默认实现了该接口的bean就不会
     *   注入到spring容器中,但我们的逻辑却是  如果配置文件里传的为REDIRECT的话,就会调用spring-security默认的认证
     *   成功逻辑---------》
     */
    //记录一个问题---------------------------------------之后会慢慢去研究------------------------------------------
    @Bean
    @ConditionalOnMissingBean(name = "imageCodeGenerator")
    public ValidateCodeGenerator imageCodeGenerator() {
        ImageCodeGenerator codeGenerator = new ImageCodeGenerator();
        codeGenerator.setSecurityProperties(securityProperties);
        return codeGenerator;
    }
}
  • 其他开发者实现的图形验证码生成逻辑----(主要用来验证上面的配置可行,不要计较具体代码)
package com.nrsc.security.server.validate;
import com.nrsc.security.core.validate.code.ImageCode;
import com.nrsc.security.core.validate.code.ValidateCodeGenerator;
import org.springframework.web.context.request.ServletWebRequest;
/**
 * Created By: Sun Chuan
 * Created Date: 2019/7/6 19:03
 */
//@Component("imageCodeGenerator")
public class DemoImageCodeGenerator implements ValidateCodeGenerator {
    @Override
    public ImageCode generate(ServletWebRequest request) {
        System.out.println("更高级的图形验证码生成代码");
        return null;
    }
}

3 测试

3.1 验证码基本参数可配置

访问localhost:8080/demo.html,自动跳到下面的页面说明验证码 基本参数可配置已经生效
在这里插入图片描述

3.2 验证码生成逻辑可配置

将2.4中的第二段代码的注释放开,再访问localhost:8080/demo.html,前端应该看不到验证码,且后端打出“更高级的图形验证码生成代码”,并报空指针异常

可以发现 验证码生成逻辑可配置已经生效在这里插入图片描述

Copyright © 2010-2022 ngui.cc 版权所有 |关于我们| 联系方式| 豫B2-20100000