shiro-登錄驗證

shiro實現登錄驗證,可以用它自身的方法來實現,也可以自定義方法來實現登錄驗證,瞭解了shiro的登錄邏輯,實現自定義的驗證邏輯就很簡單
1、用shiro方法實現
shiro配置:
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean" >
        <property name="securityManager" ref="securityManager" />
        <property name="loginUrl" value = "/login" />
        <property name="successUrl" value = "/"  />
        <property name="unauthorizedUrl" value = "/unauthorize"/>
<property name="filterChainDefinitions">
            <value>
                /static/**=anon
                /login=authc
                /logout=logout
                /unauthorize=authc
                /**=user,perms
            </value>
        </property>
</bean>
由於shiro默認註冊了FormAuthenticationFilter,所以配置中可以不需要爲此方法定義bean,但有個前提,登錄頁面中的登錄賬號和密碼,記住我的name必須和FormAuthenticationFilter默認的名稱一致,如下圖

如果登錄頁面的name和FormAuthenticationFilter不一致,則需要自己爲FormAuthenticationFilter進行配置
<bean id="formAuthenticationFilter" class="org.apache.shiro.web.filter.authc.FormAuthenticationFilter">
        <property name="usernameParam" value="name"/>
        <property name="passwordParam" value="password1"/>
        <property name="rememberMeParam" value="rememberMe1"/>
        <property name="loginUrl" value="/login"/>
        <property name="successUrl" value="/"/>
</bean>
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean" >
        <property name="securityManager" ref="securityManager" />
        <property name="loginUrl" value = "/login" />
        <property name="successUrl" value = "/"  />
        <property name="unauthorizedUrl" value = "/unauthorize"/>
<property name="filters">
            <map>
                <entry key="authc" value-ref="formAuthenticationFilter"/>
            </map>
        </property>
<property name="filterChainDefinitions">
            <value>
                /static/**=anon
                /login=authc
                /logout=logout
                /unauthorize=authc
                /**=user,perms <
            </value>
        </property>
</bean>
登錄頁面提交後,跳轉到 /login,進入登錄方法,由於此路徑權限設置爲authc,shiro對該路徑進行過濾,authc權限由FormAuthenticationFilter進行過濾。登錄請求進入onAccessDenied方法
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        if (isLoginRequest(request, response)) {  //判斷是否是登錄請求
            if (isLoginSubmission(request, response)) { // 是否是http post請求
                if (log.isTraceEnabled()) {
                    log.trace("Login submission detected.  Attempting to execute login.");
                }
                return executeLogin(request, response);
            } else {
                if (log.isTraceEnabled()) {
                    log.trace("Login page view.");
                }
                //allow them to see the login page ;)
                return true;
            }
        } else {
            if (log.isTraceEnabled()) {
                log.trace("Attempting to access a path which requires authentication.  Forwarding to the " +
                        "Authentication url [" + getLoginUrl() + "]");
            }
            saveRequestAndRedirectToLogin(request, response);
            return false;
        }
}
其中 executeLogin(request, response)方法的具體實現在繼承的AuthenticatingFilter裏
protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
        AuthenticationToken token = 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);
        }
        try {
            Subject subject = getSubject(request, response);
            subject.login(token);
            return onLoginSuccess(token, subject, request, response);
        } catch (AuthenticationException e) {
            return onLoginFailure(token, e, request, response);
        }
}

剖析:createToken(request, response); 具體實現在子類FormAuthenticationFilter中

protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) {
    String username = getUsername(request);
    String password = getPassword(request);
    return createToken(username, password, request, response);
}
從上可以看出,具體的登錄賬號和密碼從request中取出來,並創建了token對象,調用subject的login方法,login方法實現大致流程是用token去realm中取AuthenticationInfo對象,AuthenticationInfo對象存放的是正確的登錄賬號和密碼,並和token中數據進行匹配,然後根據匹配情況返回相應的結果。realm中方法需自己實現,大致流程:從token中取出用戶登錄填寫的賬號,去查找正確的登錄信息,若是查找不到,返回null,如果查找到對應的登錄賬號和密碼,則封裝到AuthenticationInfo對象中,並返回該對象。
java代碼:
@RequestMapping(value = "/login")
public String toLogin(HttpServletRequest request, Model model){
        String exceptionClassName = (String)request.getAttribute("shiroLoginFailure");
        String error = null;
        if(UnknownAccountException.class.getName().equals(exceptionClassName)) {
            error = "用戶名/密碼錯誤";
        } else if(IncorrectCredentialsException.class.getName().equals(exceptionClassName)) {
            error = "用戶名/密碼錯誤";
        } else if(exceptionClassName != null) {
            error = "其他錯誤:" + exceptionClassName;
        }
        model.addAttribute("error", error);
        return "login";  // 跳轉到登錄頁面
}

2、自定義登錄實現
最關鍵的是在shiro配置中,登錄提交的url需設置爲不過濾處理,如提交到/login,則/login=anon,
方法中可實現自己的登錄驗證邏輯。若是賬號密碼匹配工作仍要shiro來完成,則將用戶填寫的賬號和密碼封裝到token對象中,調用subject的login方法。
代碼示例:
UsernamePasswordToken token = new UsernamePasswordToken(userName, password);
       try {
            subject.login(token);
        } catch (UnknownAccountException ua){
            returnInfo.setMessage("用戶名錯誤");
        } catch (IncorrectCredentialsException ic){
            returnInfo.setMessage("密碼錯誤");
        }
   }

附:
1)shiro默認註冊的filters
public enum DefaultFilter {
    anon(AnonymousFilter.class),
    authc(FormAuthenticationFilter.class),
    authcBasic(BasicHttpAuthenticationFilter.class),
    logout(LogoutFilter.class),
    noSessionCreation(NoSessionCreationFilter.class),
    perms(PermissionsAuthorizationFilter.class),
    port(PortFilter.class),
    rest(HttpMethodPermissionFilter.class),
    roles(RolesAuthorizationFilter.class),
    ssl(SslFilter.class),
    user(UserFilter.class);
}
2)登錄異常
UnknownAccountException(賬號不存在)
IncorrectCredentialsException(密碼錯誤)
DisabledAccountException(帳號被禁用)
LockedAccountException(帳號被鎖定)
ExcessiveAttemptsException(登錄失敗次數過多)
ExpiredCredentialsException(憑證過期)等

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章