1. 程式人生 > >shiro 整合kaptcha驗證碼

shiro 整合kaptcha驗證碼

一、新增依賴包

<!--驗證碼依賴包-->
<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;
}