kaptcha验证码实现

       前言

        Kaptcha是一个非常实用的验证码生成、是一种开源的验证码实现工具,可以通过配置生成多样化的验证码。以图片的形式显示,从而无法进行复制粘贴。


kaptcha验证码实现
  • 添加kaptcha依赖

    <dependency>
      <groupId>com.github.axet</groupId>
      <artifactId>kaptcha</artifactId>
      <version>0.0.9</version>
    </dependency>
  • SpringBoot注册Bean使框架知道验证码的存在,用户第一次打开登录页面发送请求时拦截进行验证码图片和参数的传递

    //配置kaptcha图片验证码框架提供的Servlet,必须注册
    @Bean
    public ServletRegistrationBean kaptchaServlet(){
        ServletRegistrationBean servlet = new ServletRegistrationBean(
              new KaptchaServlet(),"/kaptcha.jpg");
        servlet.addInitParameter(Constants.KAPTCHA_SESSION_CONFIG_KEY,
                Constants.KAPTCHA_SESSION_KEY); //session key
        servlet.addInitParameter(Constants.KAPTCHA_TEXTPRODUCER_FONT_SIZE,"50");           servlet.addInitParameter(Constants.KAPTCHA_BORDER,"no");        servlet.addInitParameter(Constants.KAPTCHA_BORDER_COLOR,"105,179,90");        servlet.addInitParameter(Constants.KAPTCHA_TEXTPRODUCER_FONT_SIZE,"45");        servlet.addInitParameter(Constants.KAPTCHA_TEXTPRODUCER_CHAR_LENGTH,"4");     servlet.addInitParameter(Constants.KAPTCHA_TEXTPRODUCER_FONT_NAMES,"宋体");     servlet.addInitParameter(Constants.KAPTCHA_TEXTPRODUCER_FONT_COLOR,"blue");    servlet.addInitParameter(Constants.KAPTCHA_IMAGE_WIDTH,"125");     servlet.addInitParameter(Constants.KAPTCHA_IMAGE_HEIGHT,"60");
      return servlet;
    }
  • 创建过滤器继承框架过滤器在,在用户登录时进行拦截比对用户账号密码和验证码,错误则抛出异常,FormAuthenticationFilter过滤器是Shiro中的authc的实例。

    public class KaptchaFilter extends FormAuthenticationFilter {
    private static final String DEFAULT_CAPTCHA_PARAM = "captcha";
    
    private String captchaParam = DEFAULT_CAPTCHA_PARAM;
    
    private static final Logger _logger = LoggerFactory.getLogger(KaptchaFilter.class);
    
    @Override
    protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
        CapchaUsernamePasswordToken token = createToken(request,response);
        try {
            _logger.info("KaptchaFilter.executeLogin");
            //对比验证码,判断是否正确
            doCaptchaValidate((HttpServletRequest)request,token);
            Subject subject = getSubject(request,response);
            subject.login(token);
            ManagerInfo user = ShiroKit.getUser();
            ((HttpServletRequest)request).getSession().setAttribute("user",user);
            return onLoginSuccess(token,subject,request,response);
        }catch (AuthenticationException e){
            return onLoginFailure(token,e,request,response);
        }
    }
    
    @Override
    protected boolean onLoginSuccess(AuthenticationToken token,
                                     Subject subject,
                                     ServletRequest request,
                                     ServletResponse response) throws Exception {
        issueSuccessRedirect(request,response);
        return false;
    }
    
    protected void issueSuccessRedirect(ServletRequest request,ServletResponse response) throws IOException {
        WebUtils.issueRedirect(request,response,"/",null,true);
    }
    
    protected void doCaptchaValidate(HttpServletRequest request,CapchaUsernamePasswordToken token){
        _logger.info("KaptchaFilter.doCaptchaValidate");
    
        String captcha =(String)request.getSession().getAttribute(Constants.KAPTCHA_SESSION_KEY);
    
        _logger.info("session中的图形码字符串:"+captcha);
        //对比
        if (captcha == null || !captcha.equalsIgnoreCase(token.getCaptcha())){
            throw new IncorrectCaptchaException();
        }
    }
    
    @Override
    protected CapchaUsernamePasswordToken createToken(ServletRequest request, ServletResponse response) {
        String username = getUsername(request);
        String password = getPassword(request);
        String captcha = getCaptcha(request);
        boolean rememberMe = isRememberMe(request);
        String host = getHost(request);
        return new CapchaUsernamePasswordToken(username,password.toCharArray(),rememberMe,host,captcha);
    }
    
    @Override
    protected void setFailureAttribute(ServletRequest request, AuthenticationException ae) {
        request.setAttribute(getFailureKeyAttribute(),ae);
    }
    
    protected String getCaptcha(ServletRequest request){
        return WebUtils.getCleanParam(request,getCaptchaParam());
    }
    
    public String getCaptchaParam() {
        return captchaParam;
    }
    
    public void setCaptchaParam(String captchaParam) {
        this.captchaParam = captchaParam;
    }

    }

  • 如果需要用到Shiro框架验证,可以重写用户口令添加一个验证码字段

      @Data
    public class CapchaUsernamePasswordToken extends UsernamePasswordToken {
        //在用户名的基础上,扩展验证码的字符串属性
      private String captcha;
    
      public CapchaUsernamePasswordToken(
              String username,char [] password,boolean rememberMe,
              String host,String captcha){
          super(username, password, rememberMe, host);
          this.captcha = captcha;
      }

    }

  • 通过Shiro框架指定登录地址和验证码过滤器

    @Bean
    public Realm myRealm(){
        return new MyRealm();
    }
    
    @Bean
    public SecurityManager securityManager(){
        DefaultSecurityManager manager = new DefaultWebSecurityManager();
        manager.setRealm(myRealm());
        manager.setRememberMeManager(cookieRememberMeManager());
        return manager;
    }
    
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager){
        ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
        factoryBean.setSecurityManager(securityManager);
        KaptchaFilter kaptchaFilter = new KaptchaFilter();
        Map<String , Filter> filterMap = factoryBean.getFilters();
        filterMap.put("kaptchaFilter",kaptchaFilter);
        factoryBean.setFilters(filterMap);
        Map<String ,String> map = new LinkedHashMap<>();
        map.put("/logout","logout");
        map.put("/main","user");
        map.put("/toLogin","kaptchaFilter");    //登录请求由自定义的验证码过滤器来处理
        //静态资源
        map.put("/css","anon");
        map.put("/fonts","anon");
        map.put("/images","anon");
        map.put("/js","anon");
        map.put("/localcss","anon");
        map.put("/localjs","anon");
        map.put("/kaptcha.jpg","anon");
        factoryBean.setLoginUrl("/toLogin");    //登录地址-必须和验证码处理的登录地址一致,否则无法跳转
        factoryBean.setSuccessUrl("/main");
        factoryBean.setUnauthorizedUrl("/error/404.jsp");
        factoryBean.setFilterChainDefinitionMap(map);
        return factoryBean;
    }