springboot--14 security(图形验证码的开发,代码的重构,记住我功能实现)

1.图形验证码

   

 2.

   

      


package com.wx.core.validate.code;

import java.awt.image.BufferedImage;
import java.time.LocalDateTime;

public class ImageCode {

    private String code;

    private LocalDateTime expireTime;

    private BufferedImage image;

    public ImageCode(BufferedImage image, String code, int expireIn) {
        this.code = code;
        this.expireTime = LocalDateTime.now().plusSeconds(expireIn);
        this.image = image;
    }


    public ImageCode(BufferedImage image, String code, LocalDateTime expireTime) {
        this.code = code;
        this.expireTime = expireTime;
        this.image = image;
    }

    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 BufferedImage getImage() {
        return image;
    }

    public void setImage(BufferedImage image) {
        this.image = image;
    }
}
/**
 *
 */
package com.wx.core.validate.code;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

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.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 sun.security.util.SecurityConstants;

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



@RestController
public class ValidateCodeController {

    private static final String SESSION_KEY = "SESSION_KEY_IMAGE_CODE";

    private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();


    @GetMapping("/code/image")
    public void createCode(HttpServletRequest request, HttpServletResponse response)
            throws Exception {
        ImageCode imageCode = createImageCode(request);
        sessionStrategy.setAttribute(new ServletWebRequest(request), SESSION_KEY, imageCode);
        ImageIO.write(imageCode.getImage(), "JPEG", response.getOutputStream());
    }

    private ImageCode createImageCode(HttpServletRequest request) {
        int height = 22;
        int width = 68;
        // 1.创建图片缓存区
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);

        Random r = new Random();
        // 2.创建绘制环境
        Graphics paint = image.getGraphics();
        Color c = new Color(200, 150, 255);
        // 设置画笔
        paint.setColor(c);
        // 画背景
        paint.fillRect(0, 0, width, height);

        // 绘制数字和字母
        StringBuffer codes = new StringBuffer();
        char[] ch = "QWERTYUIOPASDFGHJKLZXCVBNM1234567890".toCharArray();

        int index;

        for (int i = 0; i < 4; i++) {
            index = r.nextInt(ch.length);
            // 设置文本颜色
            paint.setColor(new Color(r.nextInt(88), r.nextInt(150), r.nextInt(255)));
            paint.drawString(ch[index] + "", (i * 16) + 3, 18);
            codes.append(ch[index]);
        }
        paint.dispose();
        return new ImageCode(image, codes.toString(), 60);
    }

}

  

  ok,把验证码的权限放开,然后访问登陆页面

  

  现在要写校验逻辑,思路就是写一个过滤器,放到Security过滤器的链上,位置就放在UsernamePasswordAuthenticationFilter

  前面,如果成功就调UsernamePasswordAuthenticationFilter,不成功抛出异常。

   

//OncePerRequestFilter保证过滤器只被掉一次
public class ValidateCodeFilter extends OncePerRequestFilter {

    private AuthenticationFailureHandler authenticationFailureHandler;

    private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();

    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,
                                    FilterChain filterChain) throws ServletException, IOException {
        if (StringUtils.equals("/authentication/form", httpServletRequest.getRequestURI())
                && StringUtils.equalsIgnoreCase(httpServletRequest.getMethod(), "post")) {
            try {
                validate(new ServletWebRequest(httpServletRequest));
            } catch (ValidateCodeException e) {
                authenticationFailureHandler.onAuthenticationFailure(httpServletRequest, httpServletResponse, e);
                return;
            }

        }
        filterChain.doFilter(httpServletRequest, httpServletResponse);
        
    }

    private void validate(ServletWebRequest servletWebRequest) throws ServletRequestBindingException {
        //从session中拿出验证码
        ImageCode imageCode = (ImageCode) sessionStrategy.getAttribute(servletWebRequest, ValidateCodeController.SESSION_KEY);
        //从请求中拿到imageCode这个参数,页面name为imageCode
        String codeInRequest = ServletRequestUtils.getStringParameter(servletWebRequest.getRequest(), "imageCode");
        if (StringUtils.isBlank(codeInRequest)) {
            throw new ValidateCodeException("验证码的值不能为空");
        }
        if (imageCode == null) {
            throw new ValidateCodeException("验证码不存在");
        }
        if (imageCode.isExpried()) {
            throw new ValidateCodeException("验证码过期");
        }
        if (!StringUtils.equals(imageCode.getCode(), codeInRequest)) {
            throw new ValidateCodeException("验证码不匹配");
        }
        sessionStrategy.removeAttribute(servletWebRequest, ValidateCodeController.SESSION_KEY);
    }

    public AuthenticationFailureHandler getAuthenticationFailureHandler() {
        return authenticationFailureHandler;
    }

    public void setAuthenticationFailureHandler(AuthenticationFailureHandler authenticationFailureHandler) {
        this.authenticationFailureHandler = authenticationFailureHandler;
    }
}
 @Override
    protected void configure(HttpSecurity http) throws Exception {
        ValidateCodeFilter validateCodeFilter = new ValidateCodeFilter();
        validateCodeFilter.setAuthenticationFailureHandler(wxAuthenctiationFailureHandler);

        http
                .addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class)
                .formLogin()  //基于security默认表单登陆的方式
                .loginPage("/authentication/require") //自定义登陆页面
                .loginProcessingUrl("/authentication/form") //表单登陆提交的登陆请求地址
                .successHandler(wxAuthenticationSuccessHandler) //自定义登陆成功后的处理
                .failureHandler(wxAuthenctiationFailureHandler) //自定义登陆失败后的处理
                .permitAll()
                .and()
                .authorizeRequests() //下面这些配置
                .antMatchers("/authentication/require").permitAll() //登陆页面不需要权限认证就可以访问
                .antMatchers(securityProperties.getBrowser().getLoginPage()).permitAll() //用户自定义的登陆页面有也要授权访问
                .antMatchers("/error").permitAll() // 访问错误页面
                .antMatchers("/code/image").permitAll() //生成验证码的请求
                .anyRequest()        //任何请求,都需要身份认证才能访问
                .authenticated() 都需要认证
                .and()
                .csrf().disable();
    }

 没有配置跳转,登陆完场后就返回Authentication信息

 

重构代码

   

    验证码配置类:

public class ValidateCodeProperties {
	
	private ImageCodeProperties image = new ImageCodeProperties();
	

	public ImageCodeProperties getImage() {
		return image; 
	}

	public void setImage(ImageCodeProperties image) {
		this.image = image;
	}


	
}

  图片验证码配置类:

 

public class ImageCodeProperties {
    //如果应用中用户没有配置验证码的参数,使用默认值如下
    private int width = 67;
    private int height = 23;
    private int length = 4;
    private int expireIn = 60;

    public int getExpireIn() {
        return expireIn;
    }

    public void setExpireIn(int expireIn) {
        this.expireIn = expireIn;
    }

    public int getLength() {
        return length;
    }

    public void setLength(int length) {
        this.length = length;
    }

    public int getWidth() {
        return width;
    }

    public void setWidth(int width) {
        this.width = width;
    }

    public int getHeight() {
        return height;
    }

    public void setHeight(int height) {
        this.height = height;
    }

}

 

@RestController
public class ValidateCodeController {

    public static final String SESSION_KEY = "SESSION_KEY_IMAGE_CODE";

    private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();

    @Autowired
    private SecurityProperties securityProperties;


    @GetMapping("/code/image")
    public void createCode(HttpServletRequest request, HttpServletResponse response)
            throws Exception {
        ImageCode imageCode = createImageCode(new ServletWebRequest(request));
        //讲生成的验证码放进session
        sessionStrategy.setAttribute(new ServletWebRequest(request), SESSION_KEY, imageCode);
        ImageIO.write(imageCode.getImage(), "JPEG", response.getOutputStream());
    }

    private ImageCode createImageCode(ServletWebRequest request) {
        //getIntParameter(ServletRequest request, String name, int defaultVal)
        int height = ServletRequestUtils.getIntParameter(request.getRequest(),
                "height", securityProperties.getCode().getImage().getHeight());
        int width = ServletRequestUtils.getIntParameter(request.getRequest(),
                "width", securityProperties.getCode().getImage().getWidth());
        // 1.创建图片缓存区
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);

        Random r = new Random();
        // 2.创建绘制环境
        Graphics paint = image.getGraphics();
        Color c = new Color(200, 150, 255);
        // 设置画笔
        paint.setColor(c);
        // 画背景
        paint.fillRect(0, 0, width, height);

        // 绘制数字和字母
        StringBuffer codes = new StringBuffer();
        char[] ch = "QWERTYUIOPASDFGHJKLZXCVBNM1234567890".toCharArray();

        int index;

        for (int i = 0; i < securityProperties.getCode().getImage().getLength(); i++) {
            index = r.nextInt(ch.length);
            // 设置文本颜色
            paint.setColor(new Color(r.nextInt(88), r.nextInt(150), r.nextInt(255)));
            paint.drawString(ch[index] + "", (i * 16) + 3, 18);
            codes.append(ch[index]);
        }
        paint.dispose();
        return new ImageCode(image, codes.toString(), securityProperties.getCode().getImage().getExpireIn());
    }

}

 ok 现在启动工程

 

验证码的参数可配置

 ok,现在如果有多个请求需要需验证码该怎么配置?就是验证码拦截的接口可以配置

验证码生成逻辑可配置

把生成验证码的逻辑移到一个接口的实现里面去,然后做成可配置的

抽出一个验证码生成的接口来:

实现这个接口,作为系统的一个默认的配置

public class ImageCodeGenerator implements ValidateCodeGenerator {

	/**
	 * 系统配置
	 */
	@Autowired
	private SecurityProperties securityProperties;

	@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++) {
			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());
	}
	
	/**
	 * 生成随机背景条纹
	 * 
	 * @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;
	}
}

再加一个配置类:

@Configuration
public class ValidateCodeBeanConfig {
	
	@Autowired
	private SecurityProperties securityProperties;
	
	@Bean
	@ConditionalOnMissingBean(name = "imageCodeGenerator")  //如果用户没用自定义实现imageCodeGenerator。就用这个默认的
	public ValidateCodeGenerator imageCodeGenerator() {
		ImageCodeGenerator codeGenerator = new ImageCodeGenerator();
		codeGenerator.setSecurityProperties(securityProperties);
		return codeGenerator;
	}
}

在生成的验证码里面去调用它:

用户可以自定义生成验证码的逻辑:

 

2.实现记住我的功能:

  

  

  

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

时空恋旅人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值