1. 程式人生 > >SpringBoot入門九,添加shiro支持

SpringBoot入門九,添加shiro支持

clip 工程 exc log4 boolean cin demo ons 存在

項目基本配置參考SpringBoot入門一,使用myEclipse新建一個SpringBoot項目,使用myEclipse新建一個SpringBoot項目即可。現在來給項目添加shiro支持,數據暫時硬編碼,不連接數據庫,具體內容如下:

1. pom.xml添加以下配置信息

<!-- 開啟shiro依賴 -->
<dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-spring-boot-starter</artifactId>
        <version>1.4.0</version>
</dependency>

2. 創建shiro配置文件

2.1 自定義權限過濾器
package com.qfx.demo.shiro;

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

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authz.AuthorizationFilter;

import com.qfx.demo.vo.SysUser;

/**
 * @功能描述:   攔截控制,用於替換shiro默認的roles攔截規則,改"並且(and)"為"或者 (or)"
 */
public class CustomRolesAuthorizationFilter extends AuthorizationFilter{
    private final Logger logger = LogManager.getLogger(getClass()); 
    /**
     * Overriding
     * @功能描述:設置同一個URL配置多個角色為"或者"的關系,默認為"並且",
     *                  如:/user/** = Role["admin,user"],默認必須滿足"admin","user"條件,
     *                 改為"或者"之後只需要滿足一個條件即可(Ini.Section中有此url,會走此方法)
     * @param request
     * @param response
     * @param obj
     * @return
     * @throws Exception
     */
    @Override
    protected boolean isAccessAllowed(ServletRequest request,ServletResponse response, Object obj) throws Exception {
        Subject subject = getSubject(request, response);
        // 驗證是否登錄
        if (null == subject.getPrincipals()) {
            return false;
        }
        // 獲取請求地址
        HttpServletRequest hsq = (HttpServletRequest) request;
        String requestUrl = hsq.getServletPath();
        // 獲取用戶信息,這裏返回的對象類型與登錄驗證時
        // new SimpleAuthenticationInfo(user, pwd, this.getName())中的第一個參數的類型需要保持一致
        SysUser user = (SysUser)subject.getPrincipals().getPrimaryPrincipal();

        System.out.println("--------1.開啟用戶["+user.getUserName()+"]訪問["+requestUrl+"]的角色過濾--------");

        // 獲取角色信息
        String[] rolesArray = (String[]) obj;
        if (rolesArray == null || rolesArray.length == 0) { //沒有角色限制,有權限訪問
            System.out.println("--------3.用戶["+user.getUserName()+"]訪問["+requestUrl+"]的角色過濾結束--------");
            logger.info("用戶["+user.getUserName()+"]訪問["+requestUrl+"]無角色限制,權限驗證通過!");
                return true;
        }    
        for (int i = 0; i < rolesArray.length; i++) {    
                if (subject.hasRole(rolesArray[i])) { //若當前用戶是rolesArray中的任何一個,則有權限訪問  
                    System.out.println("--------3.用戶["+user.getUserName()+"]訪問["+requestUrl+"]的角色過濾結束--------");
                    logger.info("用戶["+user.getUserName()+"]訪問["+requestUrl+"]權限驗證通過!");
                        return true;    
                }    
        }
        System.out.println("--------3.用戶["+user.getUserName()+"]訪問["+requestUrl+"]的角色過濾結束--------");
        logger.info("用戶["+user.getUserName()+"]訪問["+requestUrl+"]權限驗證失敗,禁止訪問!");
        return false;
    }
}
2.2 自定義realm
package com.qfx.demo.shiro;

import java.util.HashSet;
import java.util.Set;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;

import com.qfx.demo.cache.UserCache;
import com.qfx.demo.vo.SysUser;

public class UserRealm extends AuthorizingRealm {
    /**
     * Overriding
     * @功能描述:   權限驗證
     * @param arg0
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        // 1.獲取用戶信息
        //   這裏principals.getPrimaryPrincipal()的返回的對象類型與登錄驗證時
        //   new SimpleAuthenticationInfo(user, pwd, this.getName())中的第一個參數的類型需要保持一致
        SysUser user = (SysUser)principals.getPrimaryPrincipal();
        System.out.println("--------2.用戶["+user.getUserName()+"]進行權限驗證--------");
        // 2.單獨定一個集合對象放置角色信息 
        Set<String> roles = new HashSet<String>();
        roles.add(user.getRoleName());
        // 3.查到權限數據,返回授權信息(要包括 上邊的permissions)  
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(roles);
        return simpleAuthorizationInfo;
    }

    /**
     * Overriding
     * @功能描述:   登陸驗證
     * @param arg0
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(
            AuthenticationToken token) throws AuthenticationException {
        System.out.println("---------用戶登錄驗證---------");
        // 1.獲取登錄名稱
        String userName = token.getPrincipal().toString();
        // 2.根據登錄名稱獲取用戶信息(從緩存獲取,正式項目從數據庫)
        SysUser user = UserCache.getUserCacheMap(userName);
        if (null == user) {
            // 拋出賬戶不存在的異常
            throw new UnknownAccountException();
        }
        // 3.獲取查詢到到密碼和鹽值
        String pwd = user.getPassWord();
        String salt = user.getSalt();
        // 4.交給AuthenticatingRealm使用CredentialsMatcher進行密碼匹配
        //   new SimpleAuthenticationInfo(user, pwd, this.getName())中的user是SysUser對象,在其他接收的地方也要轉成SysUser對象
        SimpleAuthenticationInfo authcInfo = new SimpleAuthenticationInfo(user, pwd, this.getName());  
        // 設置鹽值(salt = pwd+userName + salt)
        authcInfo.setCredentialsSalt(ByteSource.Util.bytes(pwd+userName + salt));
        return authcInfo; 
    }
}
2.3 自定義shiro核心配置類(取代xml配置文件)
package com.qfx.demo.shiro;

import java.util.LinkedHashMap;
import java.util.Map;

import javax.servlet.Filter;

import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.mgt.eis.MemorySessionDAO;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.qfx.demo.cache.MenuRoleCache;
import com.qfx.demo.vo.SysMenuRole;

@Configuration
public class ShiroConfig {
        /**
         * <h5>功能:憑證匹配器</h5>
         * @return 
         */
        @Bean
        public HashedCredentialsMatcher hashMatcher(){
                HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
                //采用md5加密,沒有默認值.可以有MD5或者SHA-1,如果對密碼安全有更高要求可以用SHA-256或者更高
                hashedCredentialsMatcher.setHashAlgorithmName("md5");
                //散列的次數,比如散列2次,相當於md5(md5(""));
                hashedCredentialsMatcher.setHashIterations(2);
                //默認是true,true時用的是Hex編碼;false時用Base64編碼
                hashedCredentialsMatcher.setStoredCredentialsHexEncoded(true);
                return hashedCredentialsMatcher;
        }

        /**
         * <h5>功能:配置shiro session 的一個管理器</h5>
         * @return 
         */
        @Bean
        public DefaultWebSessionManager sessionManager(){
            DefaultWebSessionManager defaultWebSessionManager = new DefaultWebSessionManager();
            defaultWebSessionManager.setSessionDAO(new MemorySessionDAO());
            // 隱藏在地址欄中顯示的JSESSIONID
            defaultWebSessionManager.setSessionIdUrlRewritingEnabled(false);
            // session的失效時長,單位毫秒(這裏設置為30分鐘,實際項目請自行修改)
            defaultWebSessionManager.setGlobalSessionTimeout(1800000);
            // 間隔一定時間清理失效會話,單位毫秒(這裏設置為每5分鐘清理一次用戶直接關閉瀏覽器造成的孤立會話,實際項目請自行修改)
            defaultWebSessionManager.setSessionValidationInterval(300000);
            // 描session線程,負責清理超時會話
            defaultWebSessionManager.setSessionValidationSchedulerEnabled(true);
            return defaultWebSessionManager;
        }

    /**
     * <h5>功能:自定義realm認證類,繼承自AuthorizingRealm,負責用戶的認證和權限的處理</h5>
     * @param hashMatcher
     * @return 
     */
    @Bean
    public UserRealm userRealm(HashedCredentialsMatcher hashMatcher){
        UserRealm userRealm = new UserRealm();
        userRealm.setCredentialsMatcher(hashMatcher);
        return userRealm;
    }

    /**
         * <h5>功能:安全管理器</h5>
         * 權限管理,這個類組合了登陸,登出,權限,session的處理,是個比較重要的類
     * @param userRealm
     * @param sessionManager
     * @return 
     */
    @Bean
        public DefaultWebSecurityManager securityManager(UserRealm userRealm, DefaultWebSessionManager sessionManager){
                DefaultWebSecurityManager securityManager =  new DefaultWebSecurityManager();
                // 自定義realm
                securityManager.setRealm(userRealm);
                // shiro 會話管理
                securityManager.setSessionManager(sessionManager);
                return securityManager;
        }

        /**
         * <h5>功能:自定義權限過濾器</h5>
         * @param securityManager
         * @return 
         */
        @Bean
        public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
            ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
            // 調用我們配置的安全管理器
            shiroFilterFactoryBean.setSecurityManager(securityManager);
            // 配置我們的登錄請求地址,非必須的屬性,默認會自動尋找Web工程根目錄下的"/login.jsp"頁面 或 "/login" 映射
            shiroFilterFactoryBean.setLoginUrl("/view/error/401.jsp");
            // 設置無權限時跳轉的URL
            shiroFilterFactoryBean.setUnauthorizedUrl("/view/error/403.jsp");

            Map<String, Filter> filter = shiroFilterFactoryBean.getFilters();
            filter.put("roles", new CustomRolesAuthorizationFilter());

            // 設置攔截器
            Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
            shiroFilterFactoryBean.setFilters(filter);

            // 對靜態資源設置匿名訪問,從resoutces/static後面開始寫
            filterChainDefinitionMap.put("/css/**", "anon");
            // 可匿名訪問的地址
            filterChainDefinitionMap.put("/", "anon");
            filterChainDefinitionMap.put("/index.jsp", "anon");
            filterChainDefinitionMap.put("/login/loginPage", "anon");
            filterChainDefinitionMap.put("/login/register", "anon");
            filterChainDefinitionMap.put("/login/login", "anon");
            // 請求 logout.do地址,shiro去清除session
            filterChainDefinitionMap.put("/logout", "logout");

            //循環url,逐個添加到section中。section就是filterChainDefinitionMap,
            //裏面的鍵就是鏈接URL,值就是存在什麽條件才能訪問該鏈接
            Map<String, SysMenuRole> menuRoleMap = MenuRoleCache.menuRoleCacheMap;
            for (String key : menuRoleMap.keySet()) {
                filterChainDefinitionMap.put(key, "roles["+menuRoleMap.get(key).getRoleNames()+"]");
            }

            //所有url都必須認證通過才可以訪問,必須放在最後
            filterChainDefinitionMap.put("/**", "authc");
            shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
            return shiroFilterFactoryBean;
        }
}

3.其他文件
javaBean文件和緩存類等信息就不從這裏貼出來了,核心的就是上面的幾個文件,想要應用的話,只要把對應的獲取數據的地方修改成自己的實現就可以了;或者想要本示例源碼的話,請點擊這裏進行下載。

SpringBoot入門九,添加shiro支持