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);將提示信息放入域中,然后在頁面取出即可展示給用戶;

最后,如果生成的圖片驗證碼不是很清晰的話可以設置圖片的樣式、文字顏色和文字間隔,使其更清晰

如何去掉干擾線:
