跳到主要内容

15、Spring Security 实战 - 添加验证码

前言

为什么需要添加验证码呢,如果没有验证码,不是也可以登录吗?验证码的主要作用是为了区分是人操作还是机器操作,防止机器自动注册和登录;如果没有验证码环节,机器可以无限制的注册用户,压垮数据库,当然登录也是一样,可以通过机器登录,然后肆无忌惮地朝网络上倾倒大量的、无意义的僵尸信息,垃圾邮件、垃圾广告、垃圾评论。

实现

添加依赖

<dependency>
	<groupId>com.github.penggle</groupId>
	<artifactId>kaptcha</artifactId>
	<version>2.3.2</version>
</dependency>

设置验证码属性

@Configuration
public class ImageCodeProperties {
   
     
    @Bean
    public DefaultKaptcha getImageCode() {
   
     
        DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
        Properties properties = new Properties();
        properties.setProperty(Constants.KAPTCHA_BORDER, "yes");
        properties.setProperty(Constants.KAPTCHA_BORDER_COLOR, "192,192,192");
        properties.setProperty(Constants.KAPTCHA_IMAGE_WIDTH, "110");
        properties.setProperty(Constants.KAPTCHA_IMAGE_HEIGHT, "40");
        properties.setProperty(Constants.KAPTCHA_TEXTPRODUCER_FONT_COLOR, "blue");
        properties.setProperty(Constants.KAPTCHA_TEXTPRODUCER_FONT_SIZE, "28");
        properties.setProperty(Constants.KAPTCHA_TEXTPRODUCER_FONT_NAMES, "宋体");
        properties.setProperty(Constants.KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "4");
        properties.setProperty(Constants.KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.ShadowGimpy");
        Config config = new Config(properties);
        defaultKaptcha.setConfig(config);
        return defaultKaptcha;
    }
}

添加图形验证码过滤器

@Component
public class ImageCodeFilter extends OncePerRequestFilter {
   
     
    @Autowired
    private SignInFailureHandler signInFailureHandler;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain
            filterChain) throws IOException, ServletException {
   
     
        if (StringUtils.equals(request.getRequestURI(), "/login") &&
                StringUtils.equalsIgnoreCase(request.getMethod(), "POST")) {
   
     
            try {
   
     
                validateCode(request);
            } catch (AuthenticationException exception) {
   
     
                signInFailureHandler.onAuthenticationFailure(request, response, exception);
                return;
            }
        }
        filterChain.doFilter(request, response);
    }

    private void validateCode(HttpServletRequest request) {
   
     
        String sessionCode = (String) request.getSession().getAttribute(Constant.IMAGE_CODE);
        String inputCode = request.getParameter("code");
        if (StringUtils.isBlank(inputCode)) {
   
     
            throw new ValidateCodeException(ResultCode.CODE_NOT_NULL.getMessage());
        }
        if (!StringUtils.equalsIgnoreCase(sessionCode, inputCode)) {
   
     
            throw new ValidateCodeException(ResultCode.CODE_ERROR.getMessage());
        }
    }
}

创建常量类

public interface Constant {
   
     
    /**
     * 图片验证码
     */
    String IMAGE_CODE = "IMAGE_CODE";
}

创建异常类

public class ValidateCodeException extends AuthenticationException {
   
     
    public ValidateCodeException(String msg, Throwable cause) {
   
     
        super(msg, cause);
    }

    public ValidateCodeException(String msg) {
   
     
        super(msg);
    }
}

将图形验证码过滤器添加到SpringSecurityConfig中,放在UsernamePasswordAuthenticationFilter前面

http.addFilterBefore(imageCodeFilter, UsernamePasswordAuthenticationFilter.class);

添加请求验证码的接口

@Autowired
private DefaultKaptcha defaultKaptcha;

/**
 * 生成图片验证码
 *
 * @param request  request
 * @param response response
 * @throws IOException IOException
 */
@GetMapping("code/image")
public void imageCode(HttpServletRequest request, HttpServletResponse response) throws IOException {

 
    String code = defaultKaptcha.createText();
    request.getSession().setAttribute(Constant.IMAGE_CODE, code);
    BufferedImage image = defaultKaptcha.createImage(code);
    ServletOutputStream outputStream = response.getOutputStream();
    ImageIO.write(image, "jpg", outputStream);
}

验证

不带验证登录,提示验证码不能为空
 
接下来获取验证
 
带验证登录,先输入错误的验证码
 
输入正确的验证码
 
登录成功