天天看點

Shiro(一)—— spring環境下shiro認證過程

  1. web.xml配置
    <!-- 攔截到所有請求,由spring交給shiro處理 -->
        <filter>
            <filter-name>shiroFilter</filter-name>
            <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
            <!-- 是否filter中的 init 和 destroy-->
            <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>
               
  2. spring-shiro.xml配置

    配置緩存管理器、憑證比對器(雜湊演算法,加鹽,散列次數)、會話管理器、自定義Realm(可以注入憑證比對器)、安全管理器(将自定義Realm注入)、自定義過濾器Filter、Shiro的Web過濾器(注入安全管理器,filterChainDefinitions,loginUrl等)

    <?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:aop="http://www.springframework.org/schema/aop"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
    	    http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/aop
            http://www.springframework.org/schema/aop/spring-aop.xsd">
    
        <!--shiro核心過濾器-->
        <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
            <!--
            如果配置了 loginUrl  沒有認證  執行對應的login請求 
            -->
            <property name="loginUrl" value="/static/login.html"/>
    
    
            <!--配置安全管理器-->
            <property name="securityManager" ref="securityManager"></property>
    
            <!-- 配置shiro過濾器pattern -->
            <property name="filterChainDefinitions">
                <value>
                    /static/css/** = anon <!--靜态檔案不需要登入驗證-->
                    /static/images/** = anon
                    /static/easyui/** = anon
                    /static/json/** = anon
                    /static/js/** = anon
                    /static/login.html = anon
                    /login = anon   <!--登入請求不攔截-->
                    /** = authc     <!--除指定請求外,其它所有的請求都需要身份驗證-->   
                </value>
            </property>
        </bean>
    
        <!-- 配置shiro安全管理器 -->
        <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
            <!--注入realm-->
            <property name="realm" ref="employeeRealm"/>
        </bean>
    
        <!--自定義Realm-->
        <bean class="fei.web.shiro.realm.EmployeeRealm" id="employeeRealm">
            <!--注入憑證管理器-->
            <property name="credentialsMatcher" ref="credentialsMatcher"/>
        </bean>
    
        <!--憑證管理器-->
        <bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
            <!--加密算法-->
            <property name="hashAlgorithmName" value="MD5"/>
            <!--加密次數-->
            <property name="hashIterations" value="3"/>
        </bean>
    </beans>
               
  3. LoginController中執行

    SecurityUtils.getSubject().login(token);

    token是UsernamePasswordToken類或者其子類

    token會被一直傳,直到最後的自定義realm中,執行

    doGetAuthenticationInfo()

    方法,查詢資料庫,如果存在傳回SimpleAuthenticationInfo,自動進行密碼比對,判斷登陸成不成功
    /**
         * @function: 認證
         */
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
    
            LOG.info("來到了認證");
            //擷取身份資訊
            String username = (String) token.getPrincipal();
            LOG.info("使用者名:" + username);
    
            //根據使用者名查詢有沒有目前使用者
            Employee employee = employeeService.getEmployeeByUserName(username);
    
            //沒有查詢到或者不是管理者,直接傳回null
            if (employee == null) {
                return null;
            }
    
            if (employee.getAdmin() == false){
                throw  new NotAdminException("你不是管理者,不能通路");
            }
    
            //定義加密的鹽值,是ByteSource類型
            ByteSource salt = ByteSource.Util.bytes(employee.getUsername());
    
            LOG.info("密碼明文:" + employee.getPassword());
    
            /**
             * @function: 資料庫密碼加密(因為存的還是明文)
             * 參數:加密算法,密碼(需要加密的字段),加密的鹽值(通常為了區分各使用者,使用使用者主鍵),加密疊代次數
             */
            SimpleHash newpassword =
                    new SimpleHash("MD5", employee.getPassword(), salt, 3);
    
            LOG.info("密碼密文:" + newpassword);
    
    
            /**
             * 因為目前realm配置了憑證管理器,是以token中的密碼會自動加密
             * 就是将SimpleAuthenticationInfo的密碼與token的密碼進行比對判斷是否可以登入成功
             * 參數: 主體,正确的密碼,鹽,目前realm名稱
             * */
            SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(employee, newpassword, salt, this.getName());
    
            return info;
        }
               
  4. LoginController中捕獲相關異常,傳回json回報資訊給前端
    @RequestMapping("/login")
        public ResultMessage userLogin(Employee employee){
    
            ResultMessage message = new ResultMessage();
    
            try {
                TokenManager.login(employee);
                LOG.info("登入成功");
                return ResultMessage.succ();
            }catch (UnknownAccountException e){
                message.setResultCode(200);
                message.setResultMes("賬戶不存在");
                LOG.warn("賬戶不存在");
                return message;
            }catch (NotAdminException e){
            /這個是我的自定義異常
                message.setResultCode(200);
                message.setResultMes(e.getMessage());
                LOG.warn(e.getMessage());
                return message;
            }
            catch (IncorrectCredentialsException e){
                message.setResultCode(200);
                message.setResultMes("密碼錯誤");
                LOG.warn("密碼錯誤");
                return message;
            }catch (Exception e){
                message.setResultCode(500);
                message.setResultMes("伺服器發生異常");
                LOG.error(e.getMessage());
                return message;
            }
        }
               

這就是我使用shiro進行登陸認證的流程及代碼,如果有問題謝謝指正,要不點個贊吧,哈哈

繼續閱讀