一、添加依赖包
<!--验证码依赖包-->
<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;
}