springboot使用kaptcha設置圖形驗證碼


kaptcha參數說明:
 
Constant 描述 默認值
kaptcha.border  圖片邊框,合法值:yes , no yes 
kaptcha.border.color  邊框顏色,合法值: r,g,b (and optional alpha) 或者 white,black,blue. black
kaptcha.border.thickness  邊框厚度,合法值:>0  1
kaptcha.image.width 圖片寬 200
kaptcha.image.height 圖片高 50
kaptcha.producer.impl 圖片實現類 com.google.code.kaptcha.impl.DefaultKaptcha
kaptcha.textproducer.impl 文本實現類 com.google.code.kaptcha.text.impl.DefaultTextCreator
kaptcha.textproducer.char.string 文本集合,驗證碼值從此集合中獲取 abcde2345678gfynmnpwx
kaptcha.textproducer.char.length 驗證碼長度 5
kaptcha.textproducer.font.names 字體 Arial, Courier
kaptcha.textproducer.font.size 字體大小 40px.
kaptcha.textproducer.font.color 字體顏色,合法值: r,g,b  或者 white,black,blue. black
kaptcha.textproducer.char.space 文字間隔 2
kaptcha.noise.impl 干擾實現類 com.google.code.kaptcha.impl.DefaultNoise
kaptcha.noise.color 干擾線顏色,合法值: r,g,b 或者 white,black,blue. black
kaptcha.obscurificator.impl 圖片樣式: 
水紋com.google.code.kaptcha.impl.WaterRipple 
魚眼com.google.code.kaptcha.impl.FishEyeGimpy
陰影com.google.code.kaptcha.impl.ShadowGimpy
com.google.code.kaptcha.impl.WaterRipple
kaptcha.background.impl 背景實現類 com.google.code.kaptcha.impl.DefaultBackground
kaptcha.background.clear.from 背景顏色漸變,開始顏色 light grey
kaptcha.background.clear.to 背景顏色漸變, 結束顏色 white
kaptcha.word.impl 文字渲染器 com.google.code.kaptcha.text.impl.DefaultWordRenderer
kaptcha.session.key session key KAPTCHA_SESSION_KEY
kaptcha.session.date session date KAPTCHA_SESSION_DATE
一、springboot+shiro+kaptcha進行圖片驗證碼
1、jar包配置:
1.1、maven中配置如下jar包
<dependency>
    <groupId>com.github.penggle</groupId>
    <artifactId>kaptcha</artifactId>
    <version>2.3.2</version>
</dependency>

 

1.2、非maven配置去官網下載jar包導入到lib下: https://code.google.com/p/kaptcha/w/list
2、kaptcha配置:
package com.dymy.saas.common.config.verification;

import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.google.code.kaptcha.util.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

import java.util.Properties;

/**
 * @Title: KaptchaUtils
 * @Author: 兵子
 * @Date: 2018/11/24 17:17:56
 * @Description: 驗證碼設置工具類
 */
@Component
public class KaptchaConfig {

    private final static String CODE_LENGTH = "4";
    private final static String SESSION_KEY = "verification_session_key";

    @Bean
public DefaultKaptcha defaultKaptcha() {
        DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
        Properties properties = new Properties();
        // 設置邊框
        properties.setProperty("kaptcha.border", "yes");
        // 設置邊框顏色
        properties.setProperty("kaptcha.border.color", "105,179,90");
        // 設置字體顏色
        properties.setProperty("kaptcha.textproducer.font.color", "blue");
        // 設置圖片寬度
        properties.setProperty("kaptcha.image.width", "118");
        // 設置圖片高度
        properties.setProperty("kaptcha.image.height", "36");
        // 設置字體尺寸
        properties.setProperty("kaptcha.textproducer.font.size", "30");
        // 設置session key
        properties.setProperty("kaptcha.session.key", SESSION_KEY);
        // 設置驗證碼長度
        properties.setProperty("kaptcha.textproducer.char.length", CODE_LENGTH);
        // 設置字體
        properties.setProperty("kaptcha.textproducer.font.names", "宋體,楷體,黑體");
        Config config = new Config(properties);
        defaultKaptcha.setConfig(config);
        return defaultKaptcha;
    }
}        

 

加@component注解,為了讓springboot掃描到此配置類
3、生成圖片驗證碼:
/**
 * @Author: 兵子
 * @Date: 2018/11/24 18:12
 * @Description: 獲取驗證碼
 * @param:
* @return:
*/
@RequestMapping(value = "verification")
public void getVerification(HttpServletRequest request, HttpServletResponse response) throws IOException {
    byte[] verByte = null;
    ByteArrayOutputStream jpegOutputStream = new ByteArrayOutputStream();
    try {
        //生產驗證碼字符串並保存到session中
     String createText = defaultKaptcha.createText();
        request.getSession().setAttribute("verify_session_Code", createText);
        //使用生產的驗證碼字符串返回一個BufferedImage對象並轉為byte寫入到byte數組中
     BufferedImage challenge = defaultKaptcha.createImage(createText);
        ImageIO.write(challenge, "jpg", jpegOutputStream);
    } catch (IllegalArgumentException e) {
        response.sendError(HttpServletResponse.SC_NOT_FOUND);
        return;
    } catch (IOException e) {
        e.printStackTrace();
    }
    //定義response輸出類型為image/jpeg類型,使用response輸出流輸出圖片的byte數組
   verByte = jpegOutputStream.toByteArray();
    response.setHeader("Cache-Control", "no-store");
    response.setHeader("Pragma", "no-cache");
    response.setDateHeader("Expires", 0);
    response.setContentType("image/jpeg");
    ServletOutputStream responseOutputStream = response.getOutputStream();
    responseOutputStream.write(verByte);
    responseOutputStream.flush();
    responseOutputStream.close();
}

 

將生成的驗證碼存入session中,然后將圖片驗證碼以流的形式輸出write;
然后設置header(響應頭),然后將此圖片流響應回調用方
4、html、jsp等前端頁面寫法
<div class="form-ctrls clearfix">
         <label for="verify" class=""></label>
         <input type="text" id="verify" name="verifyCode" placeholder="請輸入驗證碼" class="verify-input">
         <span class="v-code">
   <img src="/verification" alt="" onclick="this.src='/verification?d='+new Date()*1">
</span>
    <i class="del"></i>
</div>

 

前端頁面要想生成顯示圖片驗證碼,只需在img標簽內src的屬性寫上生成圖片驗證碼的請求地址即可,然后在添加點擊事件,每次的點擊請求一次生成圖片驗證碼方法,替換原有的圖片驗證即可,此處寫法可直接在onclick中給當前src替換即可,也可以單獨寫js點擊事件,此處參數 d 是為了讓頁面不形成緩存進行實時刷新(參數可為隨機數,推薦用時間毫秒不會重復)
5、配合shiro進行驗證碼驗證
注意:如果后端用到了shiro則一定要對shiro進行配置,否則會出現無法生成圖片驗證碼甚至連請求的方法都進不去
5.1、shiroConfig配置:
@Bean
public ShiroFilterFactoryBean shiroFilter(org.apache.shiro.mgt.SecurityManager securityManager,
                                              KickoutSessionControlFilter kickoutSessionControlFilter) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);

        // 沒有登陸的用戶只能訪問登陸頁面
     shiroFilterFactoryBean.setLoginUrl("/login");
        // 登錄成功后要跳轉的鏈接
     shiroFilterFactoryBean.setSuccessUrl("/index");
        // 未授權界面; ----這個配置了沒卵用,具體原因想深入了解的可以自行百度
     shiroFilterFactoryBean.setUnauthorizedUrl("/404");

        //自定義攔截器
     Map<String, Filter> filtersMap = new LinkedHashMap<String, Filter>();
        //限制同一帳號同時在線的個數。
      // filtersMap.put("kickout", kickoutSessionControlFilter);
        // 配置驗證碼過濾器
     filtersMap.put("kaptcha", shiroKaptchaFilter());
        shiroFilterFactoryBean.setFilters(filtersMap);


        // 權限控制map.
     Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
        filterChainDefinitionMap.put("/actuator", "anon");
        filterChainDefinitionMap.put("/actuator/**", "anon");
        filterChainDefinitionMap.put("/403", "anon");
        // 新的過濾
     filterChainDefinitionMap.put("/common/**", "anon");

        filterChainDefinitionMap.put("/error/**", "anon");
        // 添加驗證碼訪問路徑
     filterChainDefinitionMap.put("/verification", "anon");
        filterChainDefinitionMap.put("/login", "kaptcha,anon");
        filterChainDefinitionMap.put("/logout", "logout");
        filterChainDefinitionMap.put("/kickout", "anon");
        //filterChainDefinitionMap.put("/index2", "authc,kickout,perms[admin]");
        //filterChainDefinitionMap.put("/**", "authc,kickout");
     filterChainDefinitionMap.put("/index2", "authc,perms[admin]");
        filterChainDefinitionMap.put("/**", "authc");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }

 

要想生成圖片驗證碼一定要配置此訪問地址:
anon:匿名訪問,即不需要權限訪問;
authc:需要登錄權限
 
5.2、驗證碼過濾器(驗證碼校驗)
 
 
 
此處配置自定義過濾器,過濾器實際為一個map集合,將其key(kaptcha)再配置到訪問路徑中,則shiro在加在時會自動加載自定義過濾器,這里因為是驗證碼,只需要在登錄時驗證,所以只配置在了登錄(login)路徑中
 
6、驗證碼校驗
package com.dymy.saas.common.filter;

import com.dymy.saas.common.util.StringUtils;
import com.google.common.collect.Maps;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.AccessControlFilter;
import org.apache.shiro.web.util.WebUtils;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;

/**
 * @Title: ShiroKaptchaFilter
 * @Author: 兵子
 * @Date: 2018/11/26 14:14:41
 * @Description: 驗證碼驗證過濾器
 */
public class ShiroKaptchaFilter extends AccessControlFilter {

    // 頁面提交的驗證碼參數
private String LOGIN_KAPTCHA = "verifyCode";
    // 錯誤提示
private String ERROR_KAPTCHA = "msg";
    // session中的驗證碼
private String SHIRO_VERIFY_SESSION = "verify_session_Code";
    // 錯誤后的跳轉地址
private String ERROR_CODE_URL = "/login";


    @Override
protected boolean isAccessAllowed(ServletRequest servletRequest, ServletResponse servletResponse, Object o) throws Exception {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        // 清除此提示,防止表單重復提示
request.removeAttribute(ERROR_KAPTCHA);
        // 獲取session中的驗證碼
Subject subject = SecurityUtils.getSubject();
        String verCode = (String) subject.getSession().getAttribute(SHIRO_VERIFY_SESSION);
        // 獲取提交的驗證碼
String paramCode = request.getParameter(LOGIN_KAPTCHA);
        // 因為登錄為表單提交登錄,此處判斷是否為表單提交
if ("post".equalsIgnoreCase(request.getMethod())) {
            // 判斷session中的驗證碼是否為空,為空則說明可能為第一次進入登錄頁面
if (verCode != null) {
                // 判斷提交的驗證碼是否為空
if (StringUtils.isNotBlank(paramCode)) {
                    // 驗證碼不區分大小寫
verCode = verCode.toLowerCase();
                    paramCode = paramCode.toLowerCase();
                    // 判斷驗證碼是否一致
if (paramCode.equals(verCode)) {
                        return true;
                    }
                }
                return false;
            }
        }
        return true;
    }

    @Override
protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
        // 重定向到登錄頁 ,並給出提示
Map<String, String> map = Maps.newHashMap();
        map.put(ERROR_KAPTCHA, "驗證碼錯誤");
        WebUtils.issueRedirect(servletRequest, servletResponse, ERROR_CODE_URL, map);
        return false;
    }
}

 

驗證碼的校驗過濾器需繼承AccessControlFilter類,然后重寫父類的isAccessAllowed和onAccessDenied方法;
isAccessAllowed:
    此方法是表示是否允許訪問,return true時表示允許訪問,return false時表示不允許訪問,可在此方法中操作判斷是否允許訪問;
onAccessDenied:
    此方法表示在 isAccessAllowed方法中如果不允許訪問即return false,調用此方法然后在此方法中 進行處理,當此方法也返回false時,則 直接返回, 后面的filter則都不執行(但是此處有可能會返回一個空頁面,所以需要提前處理,進行重定向到登錄頁面),此時構建map集合,給出驗證碼錯誤消息提示然后重定向返回到登錄頁;
 
7、登錄方法處理
    注意:如果是shiro一般都會有兩個登錄方法,get和post,get登錄方法是一個頁面跳轉方法,如訪問www.baidu.com網址會跳轉到百度首頁,如果不是用的shiro則此處無用
當在 onAccessDenied方法進行重定向到login方法后,此時實際是要再走一遍登錄login方法的,所以在login方法中獲取之前重定向時傳入的map集合參數,然后將其響應到登錄頁面,告訴用戶驗證碼錯誤;
@RequestMapping(value = "/login", method = RequestMethod.GET)
public String loginPage(Model model, HttpServletRequest request) {
    String msg = request.getParameter("msg");
    request.setAttribute("msg", msg);
    Operator currentLoginUser = RequestUtils.currentLoginUser();
    if (currentLoginUser != null && StringUtils.isNotEmpty(currentLoginUser.getLoginName())) {
        Integer operatorId = null;
        if (!Constants.ADMIN.equals(currentLoginUser.getLoginName())) {
            operatorId = currentLoginUser.getOperatorId();
        }
        List<Menu> menus = menuService.queryMenuByPidAndOperatorId(0, operatorId);
        model.addAttribute("menus", menus);
        model.addAttribute("user", currentLoginUser);
        return "redirect:/index";
    } else {
        return "login";
    }
}

 

通過request.getparamer("msg");取出之前重定向到登錄時的map中的提示信息,然后可以request.setattribute("msg",msg);也可以model.addattribute("msg",msg);將提示信息放入域中,然后在頁面取出即可展示給用戶;
 
最后,如果生成的圖片驗證碼不是很清晰的話可以設置圖片的樣式、文字顏色和文字間隔,使其更清晰
 
 
如何去掉干擾線:
 
 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM