1. 程式人生 > >結合springMVC,shiro,redis,kaptcha進行驗證碼登錄完整隨筆

結合springMVC,shiro,redis,kaptcha進行驗證碼登錄完整隨筆

賬號 custom tid get success catch nor rod case

自己在做項目的時候總結的配置全部流程,用作記錄也希望能幫助大家。

一、進行kaptha的依賴配置

1  <!--驗證碼生成工具-->
2         <dependency>
3             <groupId>com.github.penggle</groupId>
4             <artifactId>kaptcha</artifactId>
5             <version>2.3.2</version>
6         </dependency>

二、web.xml配置,我們只需要簡單配置一個 Servlet,頁面通過 IMG 標簽就可以展現圖形驗證碼。

<servlet>
        <servlet-name>Kaptcha</servlet-name>
        <servlet-class>com.google.code.kaptcha.servlet.KaptchaServlet</servlet-class>
        <init-param>
        <param-name>kaptcha.image.width</param-name>
        <param-value>200</param-value>                
        </init-param>
        <init-param>
        <param-name>kaptcha.image.height</param-name>
        <param-value>50</param-value>                
        </init-param>
        <init-param>
        <param-name>kaptcha.textproducer.char
.length</param-name> <param-value>4</param-value> </init-param> <init-param> <param-name>kaptcha.noise.impl</param-name> <param-value>com.google.code.kaptcha.impl.NoNoise</param-value> </init-param> <init-param> <param-name>kaptcha.session.key</param-name> <param-value>rand</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>Kaptcha</servlet-name> <url-pattern>/kaptcha.jpg</url-pattern> </servlet-mapping>

三、擴展 UsernamePasswordTokenShiro 表單認證,頁面提交的用戶名密碼等信息,用 UsernamePasswordToken 類來接收,很容易想到,要接收頁面驗證碼的輸入,我們需要擴展此類:

package com.caa.shiro.filter;

import javax.servlet.ServletRequest;

import javax.servlet.ServletResponse;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpSession;

import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;

public class CaptchaUsernamePasswordToken extends UsernamePasswordToken {
    //驗證碼字符串
    private String captcha;
 
    public CaptchaUsernamePasswordToken(String username, String password,
            boolean rememberMe, String captcha) {
        super(username, password, rememberMe);
        this.captcha = captcha;
    }
 
    public String getCaptcha() {
        return captcha;
    }
 
    public void setCaptcha(String captcha) {
        this.captcha = captcha;
    }
    
}

四、

擴展 FormAuthenticationFilter

接下來我們擴展 FormAuthenticationFilter 類

這裏貼一個全部的擴展方法,不過實際上我使用的是shiro自帶的登錄驗證 後添加的驗證碼的校驗方法(如果也是只需要添加驗證碼功能那只需要在你的控制層調用doCaptchaValidate方法即可

package com.caa.shiro.filter;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.apache.shiro.web.util.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
 
public class CaptchaFormAuthenticationFilter extends FormAuthenticationFilter {
    private static final Logger LOG = LoggerFactory.getLogger(CaptchaFormAuthenticationFilter.class);
    
    public CaptchaFormAuthenticationFilter() {
    }
    @Override
    /**
     * 登錄驗證
     */
    protected boolean executeLogin(ServletRequest request,
            ServletResponse response) throws Exception {
        CaptchaUsernamePasswordToken token = createToken(request, response);
        try {
            /*圖形驗證碼驗證*/
            doCaptchaValidate((HttpServletRequest) request, token);
            Subject subject = getSubject(request, response);
            subject.login(token);//正常驗證
            LOG.info(token.getUsername()+"登錄成功");
            return onLoginSuccess(token, subject, request, response);
        }catch (AuthenticationException e) {
            LOG.info(token.getUsername()+"登錄失敗--"+e);
            return onLoginFailure(token, e, request, response);
        }
    }
 
    // 驗證碼校驗
    public void doCaptchaValidate(HttpServletRequest request,
            CaptchaUsernamePasswordToken token) {
//session中的圖形碼字符串
        HttpSession session = request.getSession();    
        String captcha = (String)session.getAttribute("rand");
//比對
        if (captcha != null && !captcha.equalsIgnoreCase(token.getCaptcha())) {
            throw new IncorrectCaptchaException("驗證碼錯誤!");
        }
    }
 
    @Override
    protected CaptchaUsernamePasswordToken createToken(ServletRequest request,
            ServletResponse response) {
        String username = getUsername(request);
        String password = getPassword(request);
        String captcha = getCaptcha(request);
        boolean rememberMe = isRememberMe(request);
        String host = getHost(request);
 
        return new CaptchaUsernamePasswordToken(username,
                password, rememberMe, captcha);
    }
 
    public static final String DEFAULT_CAPTCHA_PARAM = "captcha";
 
    private String captchaParam = DEFAULT_CAPTCHA_PARAM;
 
    public String getCaptchaParam() {
        return captchaParam;
    }
 
    public void setCaptchaParam(String captchaParam) {
        this.captchaParam = captchaParam;
    }
 
    protected String getCaptcha(ServletRequest request) {
        return WebUtils.getCleanParam(request, getCaptchaParam());
    }
    
//保存異常對象到request
    @Override
    protected void setFailureAttribute(ServletRequest request,
            AuthenticationException ae) {
        request.setAttribute(getFailureKeyAttribute(), ae);
    }
}

六、前面驗證碼校驗不通過,我們拋出一個異常 IncorrectCaptchaException,此類繼承 AuthenticationException,之所以需要擴展一個新的異常類,為的是在頁面能更精準顯示錯誤提示信息。

package com.caa.shiro.filter;

 
import org.apache.shiro.authc.AuthenticationException;
 
public class IncorrectCaptchaException extends AuthenticationException {
 
    /**
     * 
     */
    private static final long serialVersionUID = 1L;

    public IncorrectCaptchaException() {
        super();
    }
 
    public IncorrectCaptchaException(String message, Throwable cause) {
        super(message, cause);
    }
 
    public IncorrectCaptchaException(String message) {
        super(message);
    }
 
    public IncorrectCaptchaException(Throwable cause) {
        super(cause);
    }
}

七、shiro配置(Filter的配置及使用)這段因為我的配置文件東西較多 在網上貼了一段 核心東西都有

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
        http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.2.xsd">
 
    <!-- Shiro Filter 攔截器相關配置 -->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <!-- securityManager -->
        <property name="securityManager" ref="securityManager" />
        <!-- 登錄路徑 -->
        <property name="loginUrl" value="/login.jsp" />
        <!-- 登錄成功後跳轉路徑 -->
        <property name="successUrl" value="/pages/index.jsp" />
        <!-- 授權失敗跳轉路徑 -->
        <property name="unauthorizedUrl" value="/login.jsp" />
        <property name="filters">
            <util:map>
                <entry key="authc" value-ref="myAuthenFilter" />
            </util:map>
        </property>
        <!-- 過濾鏈定義 -->
        <property name="filterChainDefinitions">
            <value>
                /login.jsp = authc
                /pages/* = authc
                /index.jsp* = authc
                /logout.do = logout
                <!-- 訪問這些路徑必須擁有某種權限 /role/edit/* = perms[role:edit] /role/save = perms[role:edit] 
                    /role/list = perms[role:view] -->
            </value>
        </property>
    </bean>
 
    <!-- 自定義驗證攔截器 -->
    <bean id="myAuthenFilter" class="javacommon.shiro.CaptchaFormAuthenticationFilter" />
 
    <!-- securityManager -->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="realm" ref="myRealm" />
    </bean>
 
    <!-- <bean id="shiroCacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager"> 
        <property name="cacheManager" ref="cacheManager" /> </bean> -->
 
    <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />
 
    <!-- 自定義Realm實現 -->
    <bean id="myRealm" class="javacommon.shiro.CustomRealm">
        <!-- <property name="cacheManager" ref="shiroCacheManager" /> -->
    </bean>
</beans>

八、前臺頁面(本人用的是html加vue的,不過前天東西不多可以根據自己的頁面來修改)

<body class="hold-transition login-page" style="background: black url(statics/images/login-bg.jpg) no-repeat fixed top;">
    <div class="login-box" id="rrapp" style="margin-top: 12%" v-cloak>
      <div class="login-box-body">
          <p class="login-box-msg" style="font-size: 25px;font-weight:bold">中拍協後臺管理系統</p>
          <div v-if="error" class="alert alert-danger alert-dismissible">
            <h4 style="margin-bottom: 0px;"><i class="fa fa-exclamation-triangle"></i> {{errorMsg}}</h4>
          </div>
          <div class="form-group has-feedback">
            <input type="text" class="form-control" v-model="username" @keyup.enter="login" placeholder="賬號" autofocus>
            <span class="fa fa-user form-control-feedback"></span>
          </div>
          <div class="form-group has-feedback">
            <input type="password" class="form-control" v-model="password" @keyup.enter="login" placeholder="密碼">
            <span class="fa fa-lock form-control-feedback"></span>
          </div>
          <div >
            <input type="text" id="code" name="captcha" v-model="captcha" @keyup.enter="login" class="form-control" placeholder="驗證碼">
        </div>
        <div >
            <img id="codeImg" src="kaptcha.jpg" class="img-responsive" style="display:inline" @click="refreshCaptcha">(看不清<a href="javascript:void(0)" @click="refreshCaptcha" style="display:inline">換一張</a>)
        </div>
          <div class="checkbox">
              <label>
                <input type="checkbox" name="isRememberMe" v-model="isRememberMe">記住我,下次免登陸
              </label>
          </div>
          <div class="row">
            <div class="col-xs-12">
              <button type="button" class="btn btn-block btn-success btn-lg" @click="login">登錄</button>
            </div>
          </div>
      </div>
    </div>

    <script type="text/javascript">
        var vm = new Vue({
            el:‘#rrapp‘,
            data:{
                username: ‘‘,
                password: ‘‘,
                captcha: ‘‘,
                error: false,
                errorMsg: ‘‘,
                isRememberMe:false
            },
            beforeCreate: function(){
                if(self != top){
                    top.location.href = self.location.href;
                }
            },
            methods: {
                login: function (event) {
                    var data = "username="+vm.username+"&password="+vm.password+"&isRememberMe="+vm.isRememberMe+"&captcha="+vm.captcha;
                    $.ajax({
                        type: "POST",
                        url: "login",
                        data: data,
                        dataType: "json",
                        success: function(result){
                            if(result.code == 0){//登錄成功
                                parent.location.href =‘index.html‘;
                            }else{
                                vm.error = true;
                                vm.errorMsg = result.msg;
                                document.getElementById("codeImg").src="kaptcha.jpg?t=" + Math.random();
                            }
                        }
                    });
                },
                refreshCaptcha: function () {
                    document.getElementById("codeImg").src="kaptcha.jpg?t=" + Math.random();
                }
            }
        });
    </script>
</body>

結合springMVC,shiro,redis,kaptcha進行驗證碼登錄完整隨筆