springmvc集成shiro登錄失敗處理


  一般的登錄流程會有:用戶名不存在,密碼錯誤,驗證碼錯誤等..

  在集成shiro后,應用程序的外部訪問權限以及訪問控制交給了shiro來管理。

  shiro提供了兩個主要功能:認證(Authentication)和授權(Authorization);認證的作用是證明自身可以訪問,一般是用戶名加密碼,授權的作用是誰可以訪問哪些資源,通過開發者自己的用戶角色權限系統來控制。

  shiro的會話管理和緩存管理不在本文范圍內。

  下面通過登錄失敗的處理流程來介紹springmvc與shiro的集成。

  項目依賴:

依賴名稱  版本
spring 4.1.4.RELEASE
shiro 1.2.2
self4j 1.7.5
log4j 1.2.17

在web.xml里配置shiro

    <filter>
        <filter-name>shiroFilter</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
        <init-param>
            <param-name>targetFilterLifecycle</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>shiroFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

新建一個spring-context-shiro.xml配置shiro相關信息,使用spring加載

<?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:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd"
    default-lazy-init="true">

    <description>Shiro Configuration</description>
    <!-- 安全認證過濾器 -->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager" />
        <property name="loginUrl" value="/sys/login" />
        <property name="successUrl" value="/sys" />
        <property name="filters">
            <map>
          <!--自定義登錄驗證過濾器-->
<entry key="authc" value-ref="formAuthenticationFilter" /> </map> </property> <property name="filterChainDefinitions"> <value> /sys/login = authc /sys/logout = logout /sys/** = user </value> </property> </bean> <!-- 定義 Shiro 主要業務對象 --> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <property name="realm" ref="systemAuthorizingRealm" /> <property name="cacheManager" ref="shiroCacheManager" /> </bean> <!-- 會話ID生成器 --> <bean id="sessionIdGenerator" class="org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator"/> <!-- 會話管理器,設定會話超時及保存 --> <bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager"> <!-- 全局會話超時時間(單位毫秒),默認30分鍾 --> <property name="globalSessionTimeout" value="1800000" /> <property name="sessionDAO" ref="sessionDAO"/> </bean> <!-- 會話驗證調度器,每30分鍾執行一次驗證 --> <!-- <bean id="sessionValidationScheduler" class="org.apache.shiro.session.mgt.quartz.QuartzSessionValidationScheduler"> --> <bean id="sessionValidationScheduler" class="org.apache.shiro.session.mgt.ExecutorServiceSessionValidationScheduler"> <property name="interval" value="1800000"/> <property name="sessionManager" ref="sessionManager"/> </bean> <!-- sessionDAO保存認證信息 --> <bean id="sessionDAO" class="org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO"> <property name="activeSessionsCacheName" value="shiro-activeSessionCache" /> <property name="cacheManager" ref="shiroCacheManager" /> <property name="sessionIdGenerator" ref="sessionIdGenerator"/> </bean> <!-- 用戶授權信息Cache, 采用EhCache --> <bean id="shiroCacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager"> <property name="cacheManager" ref="cacheManager" /> </bean> <!-- Shiro生命周期處理器 --> <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" /> <!-- AOP式方法級權限檢查 --> <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor"> <property name="proxyTargetClass" value="true" /> </bean> <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor"> <property name="securityManager" ref="securityManager" /> </bean> </beans>

新建一個登錄認證過濾器FormAuthenticationFilter.java

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.web.util.WebUtils;
import org.springframework.stereotype.Service;

/**
 * 表單驗證(包含驗證碼)過濾類*/
@Service
public class FormAuthenticationFilter extends org.apache.shiro.web.filter.authc.FormAuthenticationFilter {
    public static final String DEFAULT_CAPTCHA_PARAM = "validateCode";

    private String captchaParam = DEFAULT_CAPTCHA_PARAM;

    public String getCaptchaParam() {
        return captchaParam;
    }

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

    protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) {
        String username = getUsername(request);
        String password = getPassword(request);
        String locale = request.getParameter("locale");
        
        if (password == null) {
            password = "";
        }
        boolean rememberMe = isRememberMe(request);
        String host = getHost(request);
        String captcha = getCaptcha(request);
        return new UsernamePasswordToken(username, password.toCharArray(),locale, rememberMe, host, captcha);
    }
}

新建令牌類UsernamePasswordToken.java

package com.chunhui.webservice.modules.sys.security;

/**
 * 用戶和密碼(包含驗證碼)令牌類*/
public class UsernamePasswordToken extends org.apache.shiro.authc.UsernamePasswordToken {
    private static final long serialVersionUID = 1L;
    private String captcha;
    private String locale;
    
    public String getCaptcha() {
        return captcha;
    }

    public void setCaptcha(String captcha) {
        this.captcha = captcha;
    }

    public String getLocale() {
        return locale;
    }

    public void setLocale(String locale) {
        this.locale = locale;
    }

    public UsernamePasswordToken() {
        super();
    }

    public UsernamePasswordToken(String username, char[] password, boolean rememberMe, String host, String captcha) {
        super(username, password, rememberMe, host);
        this.captcha = captcha;
    }
    public UsernamePasswordToken(String username, char[] password, String locale,boolean rememberMe, String host, String captcha) {
        super(username, password, rememberMe, host);
        this.captcha = captcha;
        this.locale = locale;
    }
}

最后一個是認證實現類SystemAuthorizationRealm:

package com.chunhui.webservice.modules.sys.security;
import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.PostConstruct;

import com.chunhui.webservice.common.utils.EmployeeType;
import com.chunhui.webservice.common.utils.VertifyStatus;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.springframework.context.annotation.DependsOn;
import org.springframework.stereotype.Service;
import com.chunhui.webservice.common.servlet.ValidateCodeServlet;
import com.chunhui.webservice.common.utils.SpringContextHolder;
import com.chunhui.webservice.modules.sys.entity.Employee;
import com.chunhui.webservice.modules.sys.entity.Menu;
import com.chunhui.webservice.modules.sys.service.SystemService;
import com.chunhui.webservice.modules.sys.utils.SystemUtils;
import com.chunhui.webservice.modules.sys.web.LoginController;

/**
 * 系統安全認證實現類*/
@Service
@DependsOn({ "employeeDao", "roleDao", "menuDao" })
public class SystemAuthorizingRealm extends AuthorizingRealm {
    private SystemService systemService;

    /**
     * 認證回調函數, 登錄時調用
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException {
        UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
// 判斷驗證碼
        Session session = SecurityUtils.getSubject().getSession();
        // 設置獨立的session會話超時時間  session.setTimeout(60000);
        String code = (String) session.getAttribute(ValidateCodeServlet.VALIDATE_CODE);
        if (token.getCaptcha() == null || !token.getCaptcha().toUpperCase().equals(code)) {
            throw new CaptchaException("驗證碼錯誤!");
        }
    
     //如果帳號不存在,輸出 //throw new UnknownAccountException(); //如果帳號被禁用,輸出 //throw new DisabledAccountException(); //保存登錄時選擇的語言 SecurityUtils.getSubject().getSession().setAttribute("locale", token.getLocale()); try{ SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(new Principal(employee), employee.getPassword(), getName()); return info; }catch (Throwable t){ t.printStackTrace(); throw new AuthenticationException(); } }/** * 授權查詢回調函數, 進行鑒權但緩存中無用戶的授權信息時調用 */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { Principal principal = (Principal) getAvailablePrincipal(principals); Employee employee = getSystemService().getEmployeeByName(principal.getUsername()); if (employee != null) { SystemUtils.putCache("employee", employee); SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); List<Menu> list = SystemUtils.getMenuList(); for (Menu menu : list) { if (StringUtils.isNotBlank(menu.getPermission())) { // 添加基於Permission的權限信息 for (String permission : StringUtils.split(menu.getPermission(), ",")) { info.addStringPermission(permission); } } } // 更新登錄IP和時間 getSystemService().updateEmployeeLoginInfo(employee.getId()); return info; } else { return null; } } /** * 清空用戶關聯權限認證,待下次使用時重新加載 */ public void clearCachedAuthorizationInfo(String principal) { SimplePrincipalCollection principals = new SimplePrincipalCollection(principal, getName()); clearCachedAuthorizationInfo(principals); } /** * 清空所有關聯認證 */ public void clearAllCachedAuthorizationInfo() { Cache<Object, AuthorizationInfo> cache = getAuthorizationCache(); if (cache != null) { for (Object key : cache.keys()) { cache.remove(key); } } } /** * 獲取系統業務對象 */ public SystemService getSystemService() { if (systemService == null) { systemService = SpringContextHolder.getBean(SystemService.class); } return systemService; } /** * 授權用戶信息 */ public static class Principal implements Serializable { private static final long serialVersionUID = 1L; private String id; private String username; private String realname; private Map<String, Object> cacheMap; public Principal(Employee employee) { this.id = employee.getId(); this.username = employee.getUsername(); this.realname = employee.getRealname(); } public String getId() { return id; } public String getUsername() { return username; } public String getRealname() { return realname; } public Map<String, Object> getCacheMap() { if (cacheMap == null) { cacheMap = new HashMap<String, Object>(); } return cacheMap; } } }

那么在JSP頁面,可以通過獲取登錄異常具體的異常類型來在頁面顯示錯誤原因

        <%String error = (String) request.getAttribute(FormAuthenticationFilter.DEFAULT_ERROR_KEY_ATTRIBUTE_NAME);%>
       <c:set var="exp_type" value="<%=error %>"/>
            <c:set var="tips" value=""></c:set>
            <c:if test="${fn:contains(exp_type,'CaptchaException')}">
                <c:set var="tips" value="驗證碼錯誤"></c:set>
            </c:if>
            <c:if test="${fn:contains(exp_type,'FailVertifyException')}">
                <c:set var="tips" value="該賬號審核未通過,不允許登陸!"></c:set>
            </c:if>
            <c:if test="${fn:contains(exp_type,'NotVertifyException')}">
                <c:set var="tips" value="該賬號正在審核中... 不允許登陸!"></c:set>
            </c:if>
            <c:if test="${fn:contains(exp_type,'UnknownAccountException')}">
                <c:set var="tips" value="賬號不存在!"></c:set>
            </c:if>
            <c:if test="${fn:contains(exp_type,'DisabledAccountException')}">
                <c:set var="tips" value="賬號不允許登陸!"></c:set>
            </c:if>
            <c:if test="${fn:contains(exp_type,'IncorrectCredentialsException')}">
                <c:set var="tips" value="密碼錯誤!"></c:set>
            </c:if>

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM