shiro 整合kaptcha驗證碼
阿新 • • 發佈:2018-11-23
一、新增依賴包
<!--驗證碼依賴包-->
<dependency>
<groupId>com.github.penggle</groupId>
<artifactId>kaptcha</artifactId>
<version>2.3.2</version>
</dependency>
二、新建CaptchaValidateFilter.java 過濾器 繼承AccessControlFilter
/** * create_by krystal on 2018/11/15/015. */ public class CaptchaValidateFilter extends AccessControlFilter { private String captchaParam = "captchaCode"; //前臺提交的驗證碼引數名 private String failureKeyAttribute = "shiroLoginFailure"; //驗證失敗後儲存到的屬性名 public String getCaptchaCode(ServletRequest request) { return WebUtils.getCleanParam(request, getCaptchaParam()); } public String getName(ServletRequest request) { return WebUtils.getCleanParam(request, "name"); } @Override protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception { // 從session獲取正確的驗證碼 Session session = SecurityUtils.getSubject().getSession(); //頁面輸入的驗證碼 String name=getName(request); String validateCode = (String)session.getAttribute(Constants.KAPTCHA_SESSION_KEY); String captchaCode = getCaptchaCode(request); HttpServletRequest httpServletRequest = WebUtils.toHttp(request); //判斷驗證碼是否表單提交(允許訪問) if ( !"post".equalsIgnoreCase(httpServletRequest.getMethod())) { return true; } // 若驗證碼為空或匹配失敗則返回false if(captchaCode == null) { return false; } else if (validateCode != null) { captchaCode = captchaCode.toLowerCase(); validateCode = validateCode.toLowerCase(); if(!captchaCode.equals(validateCode)) { return false; } } return true; } @Override protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception { //如果驗證碼失敗了,儲存失敗key屬性 request.setAttribute(failureKeyAttribute, "驗證碼錯誤"); return true; } public String getCaptchaParam() { return captchaParam; } public void setCaptchaParam(String captchaParam) { this.captchaParam = captchaParam; } }
三、新建KaptchaConfig.java 驗證碼工具類
public class KaptchaConfig { @Bean public ServletRegistrationBean<KaptchaServlet> kaptchaServlet() { ServletRegistrationBean<KaptchaServlet> registrationBean = new ServletRegistrationBean<KaptchaServlet>(new KaptchaServlet(), "/captcha/kaptcha.jpg"); registrationBean.addInitParameter(Constants.KAPTCHA_SESSION_CONFIG_KEY, Constants.KAPTCHA_SESSION_KEY); //寬度 registrationBean.addInitParameter(Constants.KAPTCHA_IMAGE_WIDTH,"140"); //高度 registrationBean.addInitParameter(Constants.KAPTCHA_IMAGE_HEIGHT,"60"); //字型大小 registrationBean.addInitParameter(Constants.KAPTCHA_TEXTPRODUCER_FONT_SIZE,"50"); // registrationBean.addInitParameter(Constants.KAPTCHA_BORDER_THICKNESS,"1"); //邊框 //無邊框 registrationBean.addInitParameter(Constants.KAPTCHA_BORDER,"no"); //文字顏色 registrationBean.addInitParameter(Constants.KAPTCHA_TEXTPRODUCER_FONT_COLOR, "blue"); //長度 registrationBean.addInitParameter(Constants.KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "4"); //字元間距 registrationBean.addInitParameter(Constants.KAPTCHA_TEXTPRODUCER_CHAR_SPACE, "6"); //可以設定很多屬性,具體看com.google.code.kaptcha.Constants // kaptcha.border 是否有邊框 預設為true 我們可以自己設定yes,no // kaptcha.border.color 邊框顏色 預設為Color.BLACK // kaptcha.border.thickness 邊框粗細度 預設為1 // kaptcha.producer.impl 驗證碼生成器 預設為DefaultKaptcha // kaptcha.textproducer.impl 驗證碼文字生成器 預設為DefaultTextCreator // kaptcha.textproducer.char.string 驗證碼文字字元內容範圍 預設為abcde2345678gfynmnpwx // kaptcha.textproducer.char.length 驗證碼文字字元長度 預設為5 // kaptcha.textproducer.font.names 驗證碼文字字型樣式 預設為new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize) // kaptcha.textproducer.font.size 驗證碼文字字元大小 預設為40 // kaptcha.textproducer.font.color 驗證碼文字字元顏色 預設為Color.BLACK // kaptcha.textproducer.char.space 驗證碼文字字元間距 預設為2 // kaptcha.noise.impl 驗證碼噪點生成物件 預設為DefaultNoise // kaptcha.noise.color 驗證碼噪點顏色 預設為Color.BLACK // kaptcha.obscurificator.impl 驗證碼樣式引擎 預設為WaterRipple // kaptcha.word.impl 驗證碼文字字元渲染 預設為DefaultWordRenderer // kaptcha.background.impl 驗證碼背景生成器 預設為DefaultBackground // kaptcha.background.clear.from 驗證碼背景顏色漸進 預設為Color.LIGHT_GRAY // kaptcha.background.clear.to 驗證碼背景顏色漸進 預設為Color.WHITE // kaptcha.image.width 驗證碼圖片寬度 預設為200 // kaptcha.image.height 驗證碼圖片高度 預設為50 return registrationBean; } }
四、新建MyFormAuthenticationFilter .java
用於驗證碼驗證的Shiro攔截器在用於身份認證的攔截器之前執行;但是如果驗證碼驗證攔截器失敗了,就不需要進行身份認證攔截器流程了;所以需要修改下如FormAuthenticationFilter身份認證攔截器,當驗證碼驗證失敗時不再走身份認證攔截器
public class MyFormAuthenticationFilter extends FormAuthenticationFilter { protected boolean onAccessDenied(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception { if (request.getAttribute(getFailureKeyAttribute()) != null) { return true; } return super.onAccessDenied(request, response, mappedValue); } @Override protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception { WebUtils.issueRedirect(request, response, getSuccessUrl()); return false; } }
五、在shiro 許可權類裡面加上我們自己寫的驗證碼過濾器
private void loadShiroFilterChain(ShiroFilterFactoryBean shiroFilterFactoryBean){/*, UserService userService*/
/////////////////////// 下面這些規則配置最好配置到配置檔案中 ///////////////////////
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
//自定義攔截器 驗證碼使用
Map<String, Filter> filters = shiroFilterFactoryBean.getFilters();
filters.put("captchaVaildate", new CaptchaValidateFilter());
filters.put("authc111", new MyFormAuthenticationFilter());
filterChainDefinitionMap.put("/sysUser/**", "authc");
filterChainDefinitionMap.put("/**", "anon");//anon 可以理解為不攔截
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
}
六、在jsp使用:
<c:set var="ctx" value="${pageContext.request.contextPath}"></c:set>
<div class="layui-form-item form_code">
<input class="layui-input" name="captchaCode" id="captchaCode" placeholder="驗證碼" lay-verify="required" type="text" autocomplete="off"/>
<div>
<img type="image" src="${ctx}/captcha/kaptcha.jpg" id="codeImage" onclick="chageCode()" title="圖片看不清?點選重新得到驗證碼" style="cursor:pointer;" width="116" height="36"/>
</div>
</div>
七、登入時候進行校驗,判斷是否驗證碼錯誤(方式很多我只寫了這個)
在登入的方法裡面加上:
if("驗證碼錯誤".equals(request.getAttribute("shiroLoginFailure"))){//該返回值在過濾器中有set進去
//return "codeError";
json.put("message","驗證碼錯誤");
json.put("flag","errorCode");
return json;
}