**采用了抽象的公共配置的方法
ValidateCodeController:前后跳转控制器
ValidateCodeProcessor: 验证码的具体逻辑接口
AbstractValidateCodeProcessor: 抽象类实现验证码的具体逻辑
ImageCodeProcessor: 图片验证码具体逻辑实现
SmsCodeProcessor: 短信验证码具体逻辑实现
ValidateCodeGenerator: 验证码生成接口
SmsCodeGenerator: 短信验证码生成类
ImageCodeGenerator:图片验证码生成类
**
- 项目目录
- ImageCode.java 图片属性类
package com.imooc.security.core.validate.code.image;
import java.awt.image.BufferedImage;
import java.time.LocalDateTime;
import com.imooc.security.core.validate.code.ValidateCode;
/**
* 图片验证码
* @author cjj
* @date 2018年10月8日
* @email 729165621@qq.com
* @blog blog.csdn.net/qq_29451823
*/
public class ImageCode extends ValidateCode{
//图片
private BufferedImage image;
public ImageCode(BufferedImage image, String code, int expireTime) {
super(code,expireTime);
this.image = image;
}
public ImageCode(BufferedImage image, String code, LocalDateTime expireTime) {
super(code,expireTime);
this.image = image;
}
public BufferedImage getImage() {
return image;
}
public void setImage(BufferedImage image) {
this.image = image;
}
}
- ValidateCode.java 短信属性类(因为短信类的部分属性与图片类一样,并且比图片类多)
package com.imooc.security.core.validate.code;
import java.time.LocalDateTime;
/**
* 短信验证码
* @author cjj
* @date 2018年10月8日
* @email 729165621@qq.com
* @blog blog.csdn.net/qq_29451823
*/
public class ValidateCode {
//随机数
private String code;
//过期时间
private LocalDateTime expireTime;
public ValidateCode() {
super();
}
public ValidateCode( String code, int expireTime) {
super();
this.code = code;
this.expireTime = LocalDateTime.now().plusSeconds(expireTime);//生成一个过期的时间点
}
public ValidateCode(String code, LocalDateTime expireTime) {
super();
this.code = code;
this.expireTime = expireTime;//生成一个过期的时间点
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public LocalDateTime getExpireTime() {
return expireTime;
}
public void setExpireTime(LocalDateTime expireTime) {
this.expireTime = expireTime;
}
public boolean isExpried() {
return LocalDateTime.now().isAfter(expireTime);//判断当前时间是否在过期时间之后
}
}
- ValidateCodeGenerator.java验证码自动生成接口
package com.imooc.security.core.validate.code;
import org.springframework.web.context.request.ServletWebRequest;
public interface ValidateCodeGenerator {
ValidateCode createImage(ServletWebRequest request);
}
- 、ImageCodeGenertor.java 图片验证码生成实现类
package com.imooc.security.core.validate.code.image;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.util.Random;
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;
import com.imooc.security.core.properties.SecurityProperties;
import com.imooc.security.core.validate.code.ValidateCodeGenerator;
@Component("imageCodeGenertor")
public class ImageCodeGenertor implements ValidateCodeGenerator{
@Autowired
private SecurityProperties securityProperties;
public ImageCode createImage(ServletWebRequest request) {
//获取用户设置的宽度
int width = ServletRequestUtils.getIntParameter(request.getRequest(), "width",
securityProperties.getValidate().getImage().getWidth());
//获取用户设置的高度
int height = ServletRequestUtils.getIntParameter(request.getRequest(), "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 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.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().getExprieIn());
}
/**
* 生成的随机条纹码
* @param fc
* @param bc
* @return
*/
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;
}
}
- SmsCodeGenertor.java 短信验证码生成代码
package com.imooc.security.core.validate.code.sms;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.util.Random;
import org.apache.commons.lang.RandomStringUtils;
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;
import com.imooc.security.core.properties.SecurityProperties;
import com.imooc.security.core.validate.code.ValidateCode;
import com.imooc.security.core.validate.code.ValidateCodeGenerator;
@Component("smsCodeGenertor")
public class SmsCodeGenertor implements ValidateCodeGenerator{
@Autowired
private SecurityProperties securityProperties;
public ValidateCode createImage(ServletWebRequest request) {
String code = RandomStringUtils.randomNumeric(securityProperties.getValidate().getSms().getLength());
return new ValidateCode(code, securityProperties.getValidate().getSms().getExprieIn());
}
public SecurityProperties getSecurityProperties() {
return securityProperties;
}
public void setSecurityProperties(SecurityProperties securityProperties) {
this.securityProperties = securityProperties;
}
}
- ValidateCodeProcessor.java验证码业务逻辑接口,验证码处理逻辑,都是分3步,第一步创建生成,第二步骤保存,第三步骤发送。所有所有不同的验证码只有发送的实现是不一样的,可以由具体的验证码子类实现。
package com.imooc.security.core.validate.code;
import org.springframework.web.context.request.ServletWebRequest;
/**
* 效验码处理器,封装不同效验码的处理逻辑
* @author cjj
* @date 2018年11月5日
* @email 729165621@qq.com
* @blog blog.csdn.net/qq_29451823
*/
public interface ValidateCodeProcessor {
/**
* 验证码放入session时的前缀
*/
String SESSION_KEY_PREFIX = "SESSION_KEY_FOR_CODE_";
/**
* 创建效验码
* ServletWebRequest servlet的封装类,request何response都可以放在这里面
* @param request
* @throws Exception
*/
void create(ServletWebRequest request) throws Exception;
}
- AbstractValidateCodeProcesser.java 验证码处理逻辑抽象实现类
package com.imooc.security.core.validate.code.impl;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
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;
import com.imooc.security.core.validate.code.ValidateCode;
import com.imooc.security.core.validate.code.ValidateCodeGenerator;
import com.imooc.security.core.validate.code.ValidateCodeProcessor;
public abstract class AbstractValidateCodeProcesser<C extends ValidateCode> implements ValidateCodeProcessor{
/**
* 操作session的工具类
*/
private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();
/**
* 收集系统中所有的{@link ValidateCodeGenerator} 接口的实现
*
*/
@Autowired
private Map<String,ValidateCodeGenerator> validateCodeGenerators;
public void create(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).trim();
//spring常见的依赖搜索,定向的去搜索某一个接口具体的实现
ValidateCodeGenerator validateCodeGenerator = validateCodeGenerators.get(type+"CodeGenertor");
C c = (C)validateCodeGenerator.createImage(request);
return c;
}
/**
* 发送效验码,由子类实现
* @param request
* @param validateCode
* @throws Exception
*/
protected abstract void send(ServletWebRequest request, C validateCode) throws Exception;
/**
* 保存效验码
* @param request
* @param validateCode
*/
private void save(ServletWebRequest request, C validateCode) {
sessionStrategy.setAttribute(request, SESSION_KEY_PREFIX + getProcessOrType(request).toUpperCase(), validateCode);
}
/**
* 根据请求的URL获取校验的类型
* @param request
* @return
*/
private String getProcessOrType(ServletWebRequest request) {
//从第一次出现该字符的位置往后面截取
return StringUtils.substringAfter(request.getRequest().getRequestURI(), "/code/");
}
}
- ImageCodeProcessor.java 图片验证码具体的发送逻辑
package com.imooc.security.core.validate.code.image;
import java.io.IOException;
import javax.imageio.ImageIO;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.ServletWebRequest;
import com.imooc.security.core.validate.code.impl.AbstractValidateCodeProcesser;
@Component("imageCodeProcessor")
public class ImageCodeProcessor extends AbstractValidateCodeProcesser<ImageCode>{
protected void send(ServletWebRequest request, ImageCode imageCode) throws IOException {
//图片验证码IO输出流就可以了
ImageIO.write(imageCode.getImage(), "JPEG", request.getResponse().getOutputStream());
}
}
- SmsCodeProcessor.java 短信验证码是由所使用的短信平台接口发送的,所有这里就模拟发送即可。
package com.imooc.security.core.validate.code.sms;
import javax.servlet.ServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.ServletRequestUtils;
import org.springframework.web.context.request.ServletWebRequest;
import com.imooc.security.core.validate.code.ValidateCode;
import com.imooc.security.core.validate.code.impl.AbstractValidateCodeProcesser;
@Component("smsCodeProcessor")
public class SmsCodeProcessor extends AbstractValidateCodeProcesser<ValidateCode>{
/**
* 短信发送器
*/
@Autowired
private SmsCodeSender smsCodeSender;
protected void send(ServletWebRequest request, ValidateCode validateCode) throws ServletRequestBindingException {
String mobile = ServletRequestUtils.getRequiredStringParameter(request.getRequest(), "mobile");
smsCodeSender.send(mobile, validateCode.getCode());
};
}
/*具体的发送,手动模拟*/
package com.imooc.security.core.validate.code.sms;
public interface SmsCodeSender {
void send (String mobile, String code);
}
package com.imooc.security.core.validate.code.sms;
public class DefaultSmsCodeSender implements SmsCodeSender {
@Override
public void send(String mobile, String code) {
System.out.println("像手机"+mobile+"发送 选项验证码"+code);
}
}
- ValidateCodeBeanConfig.java 使用@Configuration配置生成Bean,并且可以验证其他注解的Bean
package com.imooc.security.core.validate.code;
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;
import com.imooc.security.core.properties.SecurityProperties;
import com.imooc.security.core.validate.code.image.ImageCodeGenertor;
import com.imooc.security.core.validate.code.sms.DefaultSmsCodeSender;
import com.imooc.security.core.validate.code.sms.SmsCodeSender;
@Configuration
public class ValidateCodeBeanConfig {
@Autowired
private SecurityProperties securityProperties;
@Bean
@ConditionalOnMissingBean(name="imageCodeGenertor")//如果初始化spring容器的时候,里面有imageCodeGenerator则就不用加载下面的内容
public ValidateCodeGenerator imageCodeGenertor() {
ImageCodeGenertor codeGenertor = new ImageCodeGenertor();
codeGenertor.setSecurityProperties(securityProperties);
return codeGenertor;
}
@Bean
@ConditionalOnMissingBean(SmsCodeSender.class)
public SmsCodeSender smsCodeSender() {
return new DefaultSmsCodeSender();
}
}
- BrowserSecurityConfig.java 安全框架配置,只需要添加验证码访问路径就行了
.and()
.authorizeRequests()//对请求做一个授权
.antMatchers("/authentication/require"
,securityProperties.getBrowser().getLoginPage()
,"/code/*").permitAll()//访问这个页面的时候不需要授权