目录
实现图片验证码功能
开发生成图片验证码接口
用来封装image图片
public class ImageCode extends ValidateCode {
private BufferedImage image;
public ImageCode(BufferedImage image, String code, int expireIn){
super(code, expireIn);
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;
}
}
将生成的图片放入到session里面
生成图形验证码
/**
* 系统配置
*/
@Autowired
private SecurityProperties securityProperties;
/*
* (non-Javadoc)
*
* @see
* com.imooc.security.core.validate.code.ValidateCodeGenerator#generate(org.
* springframework.web.context.request.ServletWebRequest)
*/
@Override
public ImageCode generate(ServletWebRequest request) {
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++) { //securityProperties.getCode().getImage().getLength() 生成条形码的长度
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()); //securityProperties.getCode().getImage().getExpireIn()过期时间
}
/**
* 生成随机背景条纹
*
* @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;
}
将该请求加入到安全配置里面
启动之后
当图片验证码发送给security框架之后,我们需要手动写security过滤器来处理图形验证码
创建过滤类ValidateCodeFilter继承OncePerRequestFilter(使得只过滤一次)
将自己写的过滤器加入的security过滤器链中
运行
代码重构
验证码基本参数可配置
首先配置core项目中的属性ImageCodeProperties
创建ValidateCodeProperties来封装各个属性类型(后期还会有短信验证)
进入到demo项目中写应用级的配置
请求级的配置
core项目中的ValidateCodeController对象
启动项目指挥,请求里面的width会覆盖配置里面的width,配置里面的width会覆盖默认的width
验证码拦截的接口可配置
将url进行统一处理
这样的话,直接访问user user/*的路径都会被拦截下来,并且返回报错信息
验证码的生成逻辑可配置
其实ImageCodeGenerator类也可以 @component注解直接注入,但是通过配置类注解来来创建实现,这样的话可以通过@ConditionalOnMissingBean进行判断是否bean里面已经存在一个相同的bean
如果在其他地方又注入了它的话,这个时候上面的imageCodeGenerator就会失效
记住我
记住我基本原理
浏览器发送认证请求给后台,当UsernamePasswordAuthenticationFilter认证成功之后通过RemenberMeService将token信息保存到TokenRepositoty以及数据库中,然后将token写入到浏览器Cookie中;
当浏览器过了几天再次发送服务请求的时候,直接被RemenberMeAuthenticationFilter拦截下来,读取Cookie中的token,在调用RemeberMeService去数据库查找Token,调用UserDetailsService完成认证。
RememberMeAuthenticationFilter过滤器一般都在自定义过滤器的末尾,当前面的认证都不可行的时候,就尝试的调用它
代码实现
创建persistentTokenRepository的bean,用来调用数据库验证使用的。tokenRepository.setCreateTableOnStartup(true)方法的目的是第一次初始化的时候,在数据库创建对象的表来存放token。
配置过期时间
调用userDetailsService
总体逻辑就是调用persistenTokenRepository()方法,去数据库去或者存放对应的token,设置过期时间,然后调用userDetailsService验证加载用户信息。
记住我源码分析
登陆
进入到UsernamePasswordAuthenticationFilter类中
认证成功之后,进入到AbstractAuthenticationProcessingFilter方法中
调用rememberMeServices
调用tokenRepository向数据库添加新的token
addCookie()的方法就是将token写到浏览器cookie里面
服务重新启动
判断前面的认证是否认证成功,如果没有认证成功就进入if语句
通过tokenSeries去数据库拿token的值 ,然后就是做token的相关检查
然后就是调用userDetailsService,去获取用户信息
认证成功之后,将认证信息保存到session里面