1. 程式人生 > >shiro學習筆記 過濾器 shiro 表單 驗證碼 登入

shiro學習筆記 過濾器 shiro 表單 驗證碼 登入

自己自定義實現了一個驗證碼錶單過濾器,基於FormAuthenticationFilter

程式碼如下:

package cn.ddsxy.ddlx.shiro;

import cn.ddsxy.ddlx.util.CaptchaUtil;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.apache.shiro.web.util.WebUtils;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

/**
 * 自定義表單過濾器
 * @author zhiguang
 */
public class CaptchaFormAuthentcationFileter extends FormAuthenticationFilter {

    /**
     * 是否開啟驗證碼支援
     */
    private boolean captchaEbabled = true;

    /**
     * 驗證碼引數名
     */
    private String captchaParam = "captcha";

    public boolean isCaptchaEbabled() {
        return captchaEbabled;
    }

    public void setCaptchaEbabled(boolean captchaEbabled) {
        this.captchaEbabled = captchaEbabled;
    }

    public String getCaptchaParam() {
        return captchaParam;
    }

    public void setCaptchaParam(String captchaParam) {
        this.captchaParam = captchaParam;
    }

    public String getCaptcha(ServletRequest request){
        return WebUtils.getCleanParam(request, this.getCaptchaParam());
    }

    @Override
    protected CaptchaUserNamePassWordToken createToken(ServletRequest request, ServletResponse response) {
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        String captcha = request.getParameter("captcha");
        return new CaptchaUserNamePassWordToken(username, password==null?"":password, captcha);
    }

    @Override
    protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
        CaptchaUserNamePassWordToken token = createToken(request, response);
        try {
            if(captchaEbabled) CaptchaUtil.verify(request);
            Subject subject = getSubject(request, response);
            subject.login(token);
            return onLoginSuccess(token, subject, request, response);
        } catch (IncorrectCaptchaException e) {
            return onLoginFailure(token, e, request, response);
        } catch (UnknownAccountException e1) { // 不存在使用者名稱對應的有效使用者記錄
            return onLoginFailure(token, e1, request, response);
        } catch (IncorrectCredentialsException e2) { // 使用者名稱或密碼不對
            return onLoginFailure(token, e2, request, response);
        } catch (AuthenticationException e3) { // 其它異常
            return onLoginFailure(token, e3, request, response);
        }
    }

    @Override
    protected void setFailureAttribute(ServletRequest request, AuthenticationException e) {
        if(e instanceof IncorrectCaptchaException){
            request.setAttribute(DEFAULT_ERROR_KEY_ATTRIBUTE_NAME,"驗證碼錯誤");
        }else if(e instanceof UnknownAccountException){
            request.setAttribute(DEFAULT_ERROR_KEY_ATTRIBUTE_NAME,"使用者不存在");
        }else if(e instanceof IncorrectCredentialsException){
            request.setAttribute(DEFAULT_ERROR_KEY_ATTRIBUTE_NAME,"密碼錯誤");
        }else if(e instanceof AuthenticationException){
            request.setAttribute(DEFAULT_ERROR_KEY_ATTRIBUTE_NAME,"登入失敗");
        }
    }

    /**
     * 表示訪問拒絕時是否自己處理,如果返回true表示自己不處理且繼續攔截器鏈執行,返回false表示自己已經處理了(比如重定向到另一個頁面)
     * @param request
     * @param response
     * @return
     * @throws Exception
     */
    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        return super.onAccessDenied(request, response);
    }

    /**
     * 是否允許訪問,返回true表示允許
     * @param request
     * @param response
     * @param mappedValue
     * @return
     */
    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        return super.isAccessAllowed(request, response, mappedValue);
    }
}

CaptchaUtil.verify該方法驗證驗證碼是否正確,可自行實現,重要的是executeLogin這個方法,我開始瞭解shiro的時候一直分不清這個FormAuthenticationFilter過濾器和Realm的doGetAuthenticationInfo(身份認證)有什麼關係,看原始碼,看到了AuthenticatingFilter這個父類executeLogin方法裡呼叫了
protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
        AuthenticationToken token = this.createToken(request, response);
        if (token == null) {
            String msg = "createToken method implementation returned null. A valid non-null AuthenticationToken must be created in order to execute a login attempt.";
            throw new IllegalStateException(msg);
        } else {
            try {
                Subject subject = this.getSubject(request, response);
                subject.login(token);
                return this.onLoginSuccess(token, subject, request, response);
            } catch (AuthenticationException var5) {
                return this.onLoginFailure(token, var5, request, response);
            }
        }
    }
我明白了,這個過濾器無非還是用subject進行登入,shiro實現登入就這個介面