spring boot整合shiro

ciscopuke發表於2021-09-09

安全框架Shiro和Spring Security比較,本文主要圍繞Shiro進行學習

一 Shiro 是一個強大而靈活的開源安全框架,能夠清晰的處理認證 授權 管理會話以及,密碼加密

  01 .認證與授權相關概念

    安全實體:  系統需要保護的具體物件資料

    許可權: 系統相關的功能操作,例如基本的CRUD

    Authentication:身份認證授權/登入,驗證使用者是否擁有相應的身份

    Authorization:  授權,即許可權的認證,認證某個已認證的使用者是否擁有某個許可權

    Session  Manager : 會話管理.即使用者登入後就是一次會話,在沒有退出之前,所有資訊都在會話中

    Cryptography:  加密,保護資料的安全性

    Web Support:web支援

    Caching:  快取

    Concurrency:  shiro支援多執行緒併發驗證,一個執行緒中開啟另一個執行緒,能把許可權自動傳播過去:

    Remember Me:記住我

  02. shiro四大核心,.如下是shiro架構

 圖片描述

  03 Shiro三個核心元件:Subject  .SecurityManager和Realms

    Subject:  主體,代表當前"使用者",所有Subject都繫結到SecurityManager,是一個抽象的概念,與當前應用互動任何東西都是Subject,如網路爬蟲,機器人,與Subject的所有互動都會委託給SecurityManager;

    SecurityManager:安全管理器:  即所有與安全有關的操作都會與SecuityManager互動,管理所有的Subject;是Shiro的核心,它負責與後邊介紹的其他元件進行互動

    Realm:  域 , shiro從Realm獲取安全資料(如 使用者,角色,許可權)SecurityManager要驗證使用者身份,那麼他需要從Realm獲取相應的使用者,已確定使使用者身份是否合法,也需要從Realm得到使用者相應的角色/許可權進行驗證用於是否能進行操作,可以吧Realm看成DataSource.,即安全資料來源

 

                   圖片描述

 

  04  以下已springboot的一個專案中shiro運用為例

   下面是pom.xml中相關jar 

圖片描述

<!--shiro --&gt
        
            org.apache.shiro
            shiro-core
            1.3.2
        
        
            org.apache.shiro
            shiro-spring
            1.3.2
        
        <!-- shiro ehcache --&gt
        
            org.apache.shiro
            shiro-ehcache
            1.3.2
        
        
            com.github.theborakompanioni
            thymeleaf-extras-shiro
            1.2.1
        

圖片描述

ShiroConfig配置檔案

圖片描述

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;import com.prostate.common.config.Constant;import com.prostate.common.redis.shiro.RedisCacheManager;import com.prostate.common.redis.shiro.RedisManager;import com.prostate.common.redis.shiro.RedisSessionDAO;import com.prostate.system.shiro.UserRealm;//import org.apache.shiro.cache.CacheManager;import net.sf.ehcache.CacheManager;import org.apache.shiro.cache.ehcache.EhCacheManager;import org.apache.shiro.mgt.SecurityManager;import org.apache.shiro.session.SessionListener;import org.apache.shiro.session.mgt.eis.MemorySessionDAO;import org.apache.shiro.session.mgt.eis.SessionDAO;import org.apache.shiro.spring.LifecycleBeanPostProcessor;import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;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.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Value;import org.springframework.cache.ehcache.EhCacheCacheManager;import org.springframework.cache.ehcache.EhCacheManagerFactoryBean;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.core.io.ClassPathResource;import java.util.ArrayList;import java.util.Collection;import java.util.LinkedHashMap;/**
 * @author*/

@Configurationpublic class ShiroConfig {
    @Value("${spring.redis.host}")    private String host;
    @Value("${spring.redis.password}")    private String password;
    @Value("${spring.redis.port}")    private int port;
    @Value("${spring.redis.timeout}")    private int timeout;

    @Value("${spring.cache.type}")    private String cacheType;

    @Value("${server.session-timeout}")    private int tomcatTimeout;//    @Autowired//    CacheManager cacheManager;
    @Bean    public static LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {        return new LifecycleBeanPostProcessor();
    }    /**
     * ShiroDialect,為了在thymeleaf裡使用shiro的標籤的bean
     *
     * @return
     */
    @Bean    public ShiroDialect shiroDialect() {        return new ShiroDialect();
    }
//  ShiroFilterFactoryBean  為了生成ShiroFilter,處理攔截資原始檔問題
//它主要保持三項資料,securityManager,filters,fiterChainDefinition
//  Shiro驗證URL時,匹配成功就不在匹配,自上而下
//
    @Bean
    ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        shiroFilterFactoryBean.setLoginUrl("/login");
        shiroFilterFactoryBean.setSuccessUrl("/index");
        shiroFilterFactoryBean.setUnauthorizedUrl("/403");
        LinkedHashMap filterChainDefinitionMap = new LinkedHashMap();
        filterChainDefinitionMap.put("/css/**", "anon");
        filterChainDefinitionMap.put("/js/**", "anon");
        filterChainDefinitionMap.put("/fonts/**", "anon");
        filterChainDefinitionMap.put("/img/**", "anon");
        filterChainDefinitionMap.put("/docs/**", "anon");
        filterChainDefinitionMap.put("/druid/**", "anon");
        filterChainDefinitionMap.put("/upload/**", "anon");
        filterChainDefinitionMap.put("/files/**", "anon");
        filterChainDefinitionMap.put("/logout", "logout");
        filterChainDefinitionMap.put("/", "anon");
        filterChainDefinitionMap.put("/blog", "anon");
        filterChainDefinitionMap.put("/blog/open/**", "anon");
        filterChainDefinitionMap.put("/**", "authc");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);        return shiroFilterFactoryBean;
    }


    @Bean    public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();        //設定realm.        securityManager.setRealm(userRealm());        // 自定義快取實現 使用redis
        if (Constant.CACHE_TYPE_REDIS.equals(cacheType)) {
            securityManager.setCacheManager(cacheManager());
        } else {
            securityManager.setCacheManager(ehCacheManager());
        }
        securityManager.setSessionManager(sessionManager());        return securityManager;
    }

    @Bean
    UserRealm userRealm() {
        UserRealm userRealm = new UserRealm();        return userRealm;
    }    /**
     * 開啟shiro aop註解支援.
     * 使用代理方式;所以需要開啟程式碼支援;
     *
     * @param securityManager
     * @return
     */
    @Bean    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);        return authorizationAttributeSourceAdvisor;
    }    /**
     * 配置shiro redisManager
     *
     * @return
     */
    @Bean    public RedisManager redisManager() {
        RedisManager redisManager = new RedisManager();
        redisManager.setHost(host);
        redisManager.setPort(port);
        redisManager.setExpire(1800);// 配置快取過期時間        //redisManager.setTimeout(1800);        redisManager.setPassword(password);        return redisManager;
    }    /**
     * cacheManager 快取 redis實現
     * 使用的是shiro-redis開源外掛
     *
     * @return
     */
    public RedisCacheManager cacheManager() {
        RedisCacheManager redisCacheManager = new RedisCacheManager();
        redisCacheManager.setRedisManager(redisManager());        return redisCacheManager;
    }    /**
     * RedisSessionDAO shiro sessionDao層的實現 透過redis
     * 使用的是shiro-redis開源外掛     */
    @Bean    public RedisSessionDAO redisSessionDAO() {
        RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
        redisSessionDAO.setRedisManager(redisManager());        return redisSessionDAO;
    }

    @Bean    public SessionDAO sessionDAO() {        if (Constant.CACHE_TYPE_REDIS.equals(cacheType)) {            return redisSessionDAO();
        } else {            return new MemorySessionDAO();
        }
    }    /**
     * shiro session的管理     */
    @Bean    public DefaultWebSessionManager sessionManager() {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        sessionManager.setGlobalSessionTimeout(tomcatTimeout * 1000);
        sessionManager.setSessionDAO(sessionDAO());
        Collection listeners = new ArrayList();
        listeners.add(new BDSessionListener());
        sessionManager.setSessionListeners(listeners);        return sessionManager;
    }

    @Bean    public EhCacheManager ehCacheManager() {
        EhCacheManager em = new EhCacheManager();
        em.setCacheManager(CacheManager.create());        return em;
    }


}

圖片描述

 

 這裡補充一下ehcache和redis比較

  ehcache直接在jvm虛擬機器中快取,速度快,效率高,但是快取共享麻煩,叢集分散式應用不方便

   redis是透過socket訪問快取務,效率比ehcachedi,處理叢集和分散式快取方便,有成熟的方案

UserRealm

圖片描述

import java.util.HashMap;import java.util.Map;import java.util.Set;import com.prostate.common.config.ApplicationContextRegister;import com.prostate.system.domain.UserToken;import org.apache.shiro.authc.AuthenticationException;import org.apache.shiro.authc.AuthenticationInfo;import org.apache.shiro.authc.AuthenticationToken;import org.apache.shiro.authc.IncorrectCredentialsException;import org.apache.shiro.authc.LockedAccountException;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.springframework.beans.factory.annotation.Autowired;import com.prostate.common.utils.ShiroUtils;import com.prostate.system.dao.UserDao;import com.prostate.system.domain.UserDO;import com.prostate.system.service.MenuService;/**
 * 自定義realm 安全的資料庫 */public class UserRealm extends AuthorizingRealm {/*    @Autowired
    UserDao userMapper;
    @Autowired
    MenuService menuService;*/

    /**
     *
     * @param arg0
     * @return
     */
    @Override    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) {
        Long userId = ShiroUtils.getUserId();
        MenuService menuService = ApplicationContextRegister.getBean(MenuService.class);
        Set perms = menuService.listPerms(userId);
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.setStringPermissions(perms);        return info;
    }

    @Override    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        String username = (String) token.getPrincipal();
        Map map = new HashMap(16);
        map.put("username", username);
        String password = new String((char[]) token.getCredentials());

        UserDao userMapper = ApplicationContextRegister.getBean(UserDao.class);        // 查詢使用者資訊
        UserDO user = userMapper.list(map).get(0);        // 賬號不存在
        if (user == null) {            throw new UnknownAccountException("賬號或密碼不正確");
        }        // 密碼錯誤
        if (!password.equals(user.getPassword())) {            throw new IncorrectCredentialsException("賬號或密碼不正確");
        }        // 賬號鎖定
        if (user.getStatus() == 0) {            throw new LockedAccountException("賬號已被鎖定,請聯絡管理員");
        }
        SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, password, getName());        return info;
    }

}

圖片描述

ShiroUtils

圖片描述

package com.prostate.common.utils;import com.prostate.system.domain.UserToken;import org.apache.commons.beanutils.BeanUtils;import org.apache.shiro.SecurityUtils;import org.apache.shiro.session.Session;import org.apache.shiro.session.mgt.eis.SessionDAO;import org.apache.shiro.subject.Subject;import com.prostate.system.domain.UserDO;import org.springframework.beans.factory.annotation.Autowired;import java.lang.reflect.InvocationTargetException;import java.security.Principal;import java.util.Collection;import java.util.List;public class ShiroUtils {
    @Autowired    private static SessionDAO sessionDAO;    public static Subject getSubjct() {        return SecurityUtils.getSubject();
    }    public static UserDO getUser() {
        Object object = getSubjct().getPrincipal();        return (UserDO)object;
    }    public static Long getUserId() {        return getUser().getUserId();
    }    public static void logout() {
        getSubjct().logout();
    }    public static List getPrinciples() {
        List principals = null;
        Collection sessions = sessionDAO.getActiveSessions();        return principals;
    }
}

圖片描述

 

Shiro 分析

①:  Shiro常見的丟擲異常

UnknownAccountException(賬號不存在)
IncorrectCredentialsException(密碼不存在)
DisabledAccountException(帳號被禁用)
LockedAccountException(帳號被鎖定)
ExcessiveAttemptsException(登入失敗次數過多)
ExpiredCredentialsException(憑證過期)等

②:Shiro標籤  

圖片描述

 登入之後 不在登入狀態時 使用者在沒有RememberMe時 使用者在RememberMe時 在有abc或者123角色時 擁有角色abc 沒有角色abc 擁有許可權abc 沒有許可權abc 顯示使用者登入名

圖片描述

③ Shiro過濾器鏈中的幾種內建過濾器

圖片描述

--(1)認證過濾器
anon              匿名過濾器
authc             需要認證
authcBasic     表示httpBasic認證  
user               表示必須存在使用者,當登入操作時不做檢查--(2)授權過濾器
roles            /admins/user/**=authc,roles[admin] (必須認證過,並擁有admin 角色)
perms
port
rest
ssl 

--(3) shiro 登入退出
shiro 內建的logout 過濾器(先配置logout,再在過濾器鏈中配置)
 
         
   
filterChainDefinitions 配置  /logout = logout 

--(4) 關於登入過濾器的配置
通常將登入請求和使用的資源(js.css等)放開設定為anon,將其他的所有資源/** 設定為authc

--(5)關於Session 失效的問題
每次請求都會經過過濾器鏈的過濾,對於authc的資源,如果登入失效,自動返回到預設登入首頁 配置的loginUrl 中;
或者可以自定義個session 攔截的過濾器放入shiro 的過濾器鏈

圖片描述

 

二  Spring Security是一個能夠為Spring的企業應用系統提供宣告式安全訪問控制解決方案的安全框架.它提供一組Spring應用上下文中配置Bean,充分利用Spring Ioc,DI,AOP,減少了為企業系統安全控制編寫大量重複程式碼,它是一個輕量級的安全框架,他確保基於Spring的應用程式提供身份驗證和授權支援,並配備了流行的安全演算法實現捆綁在一起

後續詳解  Spring Security

     

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/4369/viewspace-2802507/,如需轉載,請註明出處,否則將追究法律責任。

相關文章