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);將提示信息放入域中,然后在頁面取出即可展示給用戶;
最后,如果生成的圖片驗證碼不是很清晰的話可以設置圖片的樣式、文字顏色和文字間隔,使其更清晰
如何去掉干擾線:
