【Spring Security + OAuth2 + JWT入门到实战】10. 改造优化获取图形验证码和短信验证码

项目文件结构

验证码处理器代码结构

虚线是调用

  • ValidateCodeController # 服务入口
  • ValidateCodeProcessor # 验证码处理接口
  • AbstractValidateCodeProcessor # 验证码处理 公共功能,使用模版方法模式聚合公共逻辑
  • ImageCodeProcessor # 图片验证码处理:响应
  • SmsCodeProcessor # 短信验证码处理:发送
  • ValidateCodeGenerator # 生成接口
  • ImageCodeGenerate # 图形验证码生成
  • SmsCodeGenerator # 短信验证码生成

ValidateCodeProcessor

com.spring.security.validate.code路径创建ValidateCodeProcessor类,生成验证码入口

package com.spring.security.validate.code;

import org.springframework.web.context.request.ServletWebRequest;

/**
 * 校验码处理器,封装不同校验码的处理逻辑
 *
 */
public interface ValidateCodeProcessor {

	/**
	 * 验证码放入session时的前缀
	 */
	String SESSION_KEY_PREFIX = "SESSION_KEY_FOR_CODE_";

	/**
	 * 创建校验码
	 * 
	 * @param request
	 * @throws Exception
	 */
	void createCode(ServletWebRequest request) throws Exception;

}

 

AbstractValidateCodeProcessor

验证码处理 公共功能,使用模版方法模式聚合公共逻辑

com.spring.security.validate.code.impl路径创建AbstractValidateCodeProcessor类

package com.spring.security.validate.code.impl;

import java.util.Map;

import com.spring.security.validate.code.ValidateCodeException;
import org.apache.commons.lang.StringUtils;
import com.spring.security.validate.code.ValidateCode;
import com.spring.security.validate.code.ValidateCodeGenerator;
import com.spring.security.validate.code.ValidateCodeProcessor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.social.connect.web.HttpSessionSessionStrategy;
import org.springframework.social.connect.web.SessionStrategy;
import org.springframework.web.context.request.ServletWebRequest;

public abstract class AbstractValidateCodeProcessor<C extends ValidateCode> implements ValidateCodeProcessor {

    /**
     * 操作session的工具类
     */
    private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();
    /**
     * 收集系统中所有的 {@link ValidateCodeGenerator} 接口的实现。
     */
    @Autowired
    private Map<String, ValidateCodeGenerator> validateCodeGenerators;

    /**
     * ValidateCodeProcessor
     */
    @Override
    public void createCode(ServletWebRequest request) throws Exception {
        C validateCode = generate(request);
        save(request, validateCode);
        send(request, validateCode);
    }

    /**
     * 生成校验码
     *
     * @param request
     * @return
     */
    @SuppressWarnings("unchecked")
    private C generate(ServletWebRequest request) {
        String type = getProcessorType(request);
        ValidateCodeGenerator validateCodeGenerator = validateCodeGenerators.get(type + "CodeGenerator");
        if (validateCodeGenerator == null) {
            throw new ValidateCodeException("验证码生成器" + type + "CodeGenerator不存在");
        }
        return (C) validateCodeGenerator.createCode(request);
    }

    /**
     * 保存校验码
     *
     * @param request
     * @param validateCode
     */
    private void save(ServletWebRequest request, C validateCode) {
        sessionStrategy.setAttribute(request, SESSION_KEY_PREFIX + getProcessorType(request).toUpperCase(), validateCode);
    }

    /**
     * 发送校验码,由子类实现
     *
     * @param request
     * @param validateCode
     * @throws Exception
     */
    protected abstract void send(ServletWebRequest request, C validateCode) throws Exception;

    /**
     * 根据请求的url获取校验码的类型
     *
     * @param request
     * @return
     */
    private String getProcessorType(ServletWebRequest request) {
        return StringUtils.substringAfter(request.getRequest().getRequestURI(), "/code/");
    }


}

 

ImageCodeProcessor

com.spring.security.validate.code.image路径创建ImageCodeProcessor图片验证码处理:响应

/**
 * 
 */
package com.spring.security.validate.code.image;

import javax.imageio.ImageIO;

import com.spring.security.validate.code.impl.AbstractValidateCodeProcessor;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.ServletWebRequest;


/**
 * 图片验证码处理器
 *
 */
@Component("imageCodeProcessor")
public class ImageCodeProcessor extends AbstractValidateCodeProcessor<ImageCode> {

	/**
	 * 发送图形验证码,将其写到响应中
	 */
	@Override
	protected void send(ServletWebRequest request, ImageCode imageCode) throws Exception {
		ImageIO.write(imageCode.getImage(), "JPEG", request.getResponse().getOutputStream());
	}
}

 

SmsCodeProcessor

com.spring.security.validate.code.sms路径创建SmsCodeProcessor短信验证码处理:发送

/**
 *
 */
package com.spring.security.validate.code.sms;

import com.spring.security.validate.code.ValidateCode;
import com.spring.security.validate.code.impl.AbstractValidateCodeProcessor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.ServletRequestUtils;
import org.springframework.web.context.request.ServletWebRequest;

/**
 * 短信验证码处理器
 *
 */
@Component("smsCodeProcessor")
public class SmsCodeProcessor extends AbstractValidateCodeProcessor<ValidateCode> {

    /**
     * 短信验证码发送器
     */
    @Autowired
    private SmsCodeSender smsCodeSender;

    @Override
    protected void send(ServletWebRequest request, ValidateCode validateCode) throws Exception {
        //获取手机号
        String mobile = ServletRequestUtils.getRequiredStringParameter(request.getRequest(), "mobile");
        //发送短信
        smsCodeSender.send(mobile, validateCode.getCode());
    }

}

 

修改控制器

改造ValidateController类统一访问路径

package com.spring.security.validate.code;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.ServletWebRequest;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;

/**
 * 获取验证码控制器
 */
@RestController
public class ValidateController {

    @Autowired
    private Map<String, ValidateCodeProcessor> validateCodeProcessor;

    /**
     * 创建验证码,根据验证码类型不同,调用不同的 {@link ValidateCodeProcessor}接口实现
     *
     * @param request
     * @param response
     * @param type
     * @throws Exception
     */
    @GetMapping("/code/{type}")
    public void createCode(HttpServletRequest request, HttpServletResponse response, @PathVariable String type)
            throws Exception {
        validateCodeProcessor.get(type + "CodeProcessor").createCode(new ServletWebRequest(request, response));
    }
}

 

修改图形验证码拦截器

修改ValidateCodeFilter类的validateCode方法:

ImageCode codeInSession = (ImageCode) sessionStrategy.getAttribute(servletWebRequest, "SESSION_KEY_FOR_CODE_IMAGE");

session值我们先写死,等做到短信验证码拦截器一起优化掉。

 

测试

启动项目访问:http://127.0.0.1:8080/signIn.html

获取图形验证码没问题,输入正确验证码账号登录:

 

重启项目测试获取短信验证: