springboot使用shiro-整合redis作爲緩存(十)

原文地址,轉載請註明出處: https://blog.csdn.net/qq_34021712/article/details/80774649     ©王賽超 

說在前面
本來的整合過程是順着博客的順序來的,越往下,集成的越多,由於之前是使用ehcache緩存,現在改爲redis,限制登錄人數 以及 限制登錄次數等 都需要改動,本篇爲了簡單,目前先將這兩個功能下線,配置暫時是註銷的,原類保存,在下篇博客中改。 
還有之前是使用SessionListener監聽session創建來統計在線人數,在本篇中也將改爲統計redis中的key數目。 
如果是單機,使用ehcache是最快的,項目一般都不是單節點,爲了方便之後使用sso單點登錄,以及多節點部署,所以使用shiro整合redis。這裏有一個開源項目,git地址爲: https://github.com/alexxiyang/shiro-redis.git 在此感謝作者無私奉獻。

整合過程
shiro用redis實現緩存需要重寫cache、cacheManager、SessionDAO和初始化redis配置。


pom添加依賴

<!-- 整合shiro框架 -->
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>1.4.0</version>
</dependency>
<!-- shiro-thymeleaf 2.0.0-->
<dependency>
    <groupId>com.github.theborakompanioni</groupId>
    <artifactId>thymeleaf-extras-shiro</artifactId>
    <version>1.2.1</version>
</dependency>
<!-- shiro-redis -->
<dependency>
  <groupId>org.crazycake</groupId>
    <artifactId>shiro-redis</artifactId>
    <version>3.1.0</version>
</dependency>

ShiroConfig.java

package com.springboot.test.shiro.config;

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import com.springboot.test.shiro.config.shiro.*;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.session.SessionListener;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator;
import org.apache.shiro.session.mgt.eis.SessionDAO;
import org.apache.shiro.session.mgt.eis.SessionIdGenerator;
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.mgt.SecurityManager;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.MethodInvokingFactoryBean;
import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer;
import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer;
import org.springframework.boot.web.servlet.ErrorPage;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver;

import javax.servlet.Filter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Properties;

/**
 * @author: wangsaichao
 * @date: 2018/5/10
 * @description: Shiro配置
 */
@Configuration
public class ShiroConfig {


    /**
     * ShiroFilterFactoryBean 處理攔截資源文件問題。
     * 注意:初始化ShiroFilterFactoryBean的時候需要注入:SecurityManager
     * Web應用中,Shiro可控制的Web請求必須經過Shiro主過濾器的攔截
     * @param securityManager
     * @return
     */
    @Bean(name = "shirFilter")
    public ShiroFilterFactoryBean shiroFilter(@Qualifier("securityManager") SecurityManager securityManager) {

        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();

        //必須設置 SecurityManager,Shiro的核心安全接口
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        //這裏的/login是後臺的接口名,非頁面,如果不設置默認會自動尋找Web工程根目錄下的"/login.jsp"頁面
        shiroFilterFactoryBean.setLoginUrl("/");
        //這裏的/index是後臺的接口名,非頁面,登錄成功後要跳轉的鏈接
        shiroFilterFactoryBean.setSuccessUrl("/index");
        //未授權界面,該配置無效,並不會進行頁面跳轉
        shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorized");

        //自定義攔截器限制併發人數,參考博客:
        LinkedHashMap<String, Filter> filtersMap = new LinkedHashMap<>();
        //限制同一帳號同時在線的個數
        //filtersMap.put("kickout", kickoutSessionControlFilter());
        //統計登錄人數
        shiroFilterFactoryBean.setFilters(filtersMap);

        // 配置訪問權限 必須是LinkedHashMap,因爲它必須保證有序
        // 過濾鏈定義,從上向下順序執行,一般將 /**放在最爲下邊 --> : 這是一個坑,一不小心代碼就不好使了
        LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        //配置不登錄可以訪問的資源,anon 表示資源都可以匿名訪問
        //配置記住我或認證通過可以訪問的地址
        filterChainDefinitionMap.put("/login", "anon");
        filterChainDefinitionMap.put("/", "anon");
        filterChainDefinitionMap.put("/css/**", "anon");
        filterChainDefinitionMap.put("/js/**", "anon");
        filterChainDefinitionMap.put("/img/**", "anon");
        filterChainDefinitionMap.put("/druid/**", "anon");
        //解鎖用戶專用 測試用的
        filterChainDefinitionMap.put("/unlockAccount","anon");
        filterChainDefinitionMap.put("/Captcha.jpg","anon");
        //logout是shiro提供的過濾器
        filterChainDefinitionMap.put("/logout", "logout");
        //此時訪問/user/delete需要delete權限,在自定義Realm中爲用戶授權。
        //filterChainDefinitionMap.put("/user/delete", "perms[\"user:delete\"]");

        //其他資源都需要認證  authc 表示需要認證才能進行訪問 user表示配置記住我或認證通過可以訪問的地址
        //如果開啓限制同一賬號登錄,改爲 .put("/**", "kickout,user");
        filterChainDefinitionMap.put("/**", "user");

        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);

        return shiroFilterFactoryBean;
    }

    /**
     * 配置核心安全事務管理器
     * @return
     */
    @Bean(name="securityManager")
    public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager =  new DefaultWebSecurityManager();
        //設置自定義realm.
        securityManager.setRealm(shiroRealm());
        //配置記住我
        securityManager.setRememberMeManager(rememberMeManager());
        //配置redis緩存
        securityManager.setCacheManager(cacheManager());
        //配置自定義session管理,使用redis
        securityManager.setSessionManager(sessionManager());
        return securityManager;
    }

    /**
     * 配置Shiro生命週期處理器
     * @return
     */
    @Bean(name = "lifecycleBeanPostProcessor")
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    /**
     *  身份認證realm; (這個需要自己寫,賬號密碼校驗;權限等)
     * @return
     */
    @Bean
    public ShiroRealm shiroRealm(){
        ShiroRealm shiroRealm = new ShiroRealm();
        shiroRealm.setCachingEnabled(true);
        //啓用身份驗證緩存,即緩存AuthenticationInfo信息,默認false
        shiroRealm.setAuthenticationCachingEnabled(true);
        //緩存AuthenticationInfo信息的緩存名稱 在ehcache-shiro.xml中有對應緩存的配置
        shiroRealm.setAuthenticationCacheName("authenticationCache");
        //啓用授權緩存,即緩存AuthorizationInfo信息,默認false
        shiroRealm.setAuthorizationCachingEnabled(true);
        //緩存AuthorizationInfo信息的緩存名稱  在ehcache-shiro.xml中有對應緩存的配置
        shiroRealm.setAuthorizationCacheName("authorizationCache");
        //配置自定義密碼比較器
        //shiroRealm.setCredentialsMatcher(retryLimitHashedCredentialsMatcher());
        return shiroRealm;
    }

    /**
     * 必須(thymeleaf頁面使用shiro標籤控制按鈕是否顯示)
     * 未引入thymeleaf包,Caused by: java.lang.ClassNotFoundException: org.thymeleaf.dialect.AbstractProcessorDialect
     * @return
     */
    @Bean
    public ShiroDialect shiroDialect() {
        return new ShiroDialect();
    }

    /**
     * 開啓shiro 註解模式
     * 可以在controller中的方法前加上註解
     * 如 @RequiresPermissions("userInfo:add")
     * @param securityManager
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("securityManager") SecurityManager securityManager){
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

    /**
     * 解決: 無權限頁面不跳轉 shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorized") 無效
     * shiro的源代碼ShiroFilterFactoryBean.Java定義的filter必須滿足filter instanceof AuthorizationFilter,
     * 只有perms,roles,ssl,rest,port纔是屬於AuthorizationFilter,而anon,authcBasic,auchc,user是AuthenticationFilter,
     * 所以unauthorizedUrl設置後頁面不跳轉 Shiro註解模式下,登錄失敗與沒有權限都是通過拋出異常。
     * 並且默認並沒有去處理或者捕獲這些異常。在SpringMVC下需要配置捕獲相應異常來通知用戶信息
     * @return
     */
    @Bean
    public SimpleMappingExceptionResolver simpleMappingExceptionResolver() {
        SimpleMappingExceptionResolver simpleMappingExceptionResolver=new SimpleMappingExceptionResolver();
        Properties properties=new Properties();
        //這裏的 /unauthorized 是頁面,不是訪問的路徑
        properties.setProperty("org.apache.shiro.authz.UnauthorizedException","/unauthorized");
        properties.setProperty("org.apache.shiro.authz.UnauthenticatedException","/unauthorized");
        simpleMappingExceptionResolver.setExceptionMappings(properties);
        return simpleMappingExceptionResolver;
    }

    /**
     * 解決spring-boot Whitelabel Error Page
     * @return
     */
    @Bean
    public EmbeddedServletContainerCustomizer containerCustomizer() {

        return new EmbeddedServletContainerCustomizer() {
            @Override
            public void customize(ConfigurableEmbeddedServletContainer container) {

                ErrorPage error401Page = new ErrorPage(HttpStatus.UNAUTHORIZED, "/unauthorized.html");
                ErrorPage error404Page = new ErrorPage(HttpStatus.NOT_FOUND, "/404.html");
                ErrorPage error500Page = new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/500.html");

                container.addErrorPages(error401Page, error404Page, error500Page);
            }
        };
    }

    /**
     * cookie對象;會話Cookie模板 ,默認爲: JSESSIONID 問題: 與SERVLET容器名衝突,重新定義爲sid或rememberMe,自定義
     * @return
     */
    @Bean
    public SimpleCookie rememberMeCookie(){
        //這個參數是cookie的名稱,對應前端的checkbox的name = rememberMe
        SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
        //setcookie的httponly屬性如果設爲true的話,會增加對xss防護的安全係數。它有以下特點:
        //setcookie()的第七個參數
        //設爲true後,只能通過http訪問,javascript無法訪問
        //防止xss讀取cookie
        simpleCookie.setHttpOnly(true);
        simpleCookie.setPath("/");
        //<!-- 記住我cookie生效時間30天 ,單位秒;-->
        simpleCookie.setMaxAge(2592000);
        return simpleCookie;
    }

    /**
     * cookie管理對象;記住我功能,rememberMe管理器
     * @return
     */
    @Bean
    public CookieRememberMeManager rememberMeManager(){
        CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
        cookieRememberMeManager.setCookie(rememberMeCookie());
        //rememberMe cookie加密的密鑰 建議每個項目都不一樣 默認AES算法 密鑰長度(128 256 512 位)
        cookieRememberMeManager.setCipherKey(Base64.decode("4AvVhmFLUs0KTA3Kprsdag=="));
        return cookieRememberMeManager;
    }

    /**
     * FormAuthenticationFilter 過濾器 過濾記住我
     * @return
     */
    @Bean
    public FormAuthenticationFilter formAuthenticationFilter(){
        FormAuthenticationFilter formAuthenticationFilter = new FormAuthenticationFilter();
        //對應前端的checkbox的name = rememberMe
        formAuthenticationFilter.setRememberMeParam("rememberMe");
        return formAuthenticationFilter;
    }

    /**
     * shiro緩存管理器;
     * 需要添加到securityManager中
     * @return
     */
    @Bean
    public RedisCacheManager cacheManager(){
        RedisCacheManager redisCacheManager = new RedisCacheManager();
        redisCacheManager.setRedisManager(redisManager());
        //redis中針對不同用戶緩存
        redisCacheManager.setPrincipalIdFieldName("username");
        //用戶權限信息緩存時間
        redisCacheManager.setExpire(200000);
        return redisCacheManager;
    }

    /**
     * 讓某個實例的某個方法的返回值注入爲Bean的實例
     * Spring靜態注入
     * @return
     */
    @Bean
    public MethodInvokingFactoryBean getMethodInvokingFactoryBean(){
        MethodInvokingFactoryBean factoryBean = new MethodInvokingFactoryBean();
        factoryBean.setStaticMethod("org.apache.shiro.SecurityUtils.setSecurityManager");
        factoryBean.setArguments(new Object[]{securityManager()});
        return factoryBean;
    }

    /**
     * 配置session監聽
     * @return
     */
    @Bean("sessionListener")
    public ShiroSessionListener sessionListener(){
        ShiroSessionListener sessionListener = new ShiroSessionListener();
        return sessionListener;
    }

    /**
     * 配置會話ID生成器
     * @return
     */
    @Bean
    public SessionIdGenerator sessionIdGenerator() {
        return new JavaUuidSessionIdGenerator();
    }

    @Bean
    public RedisManager redisManager(){
        RedisManager redisManager = new RedisManager();
        redisManager.setHost("127.0.0.1");
        redisManager.setPort(6379);
        redisManager.setPassword("123456");
        return redisManager;
    }



    /**
     * SessionDAO的作用是爲Session提供CRUD並進行持久化的一個shiro組件
     * MemorySessionDAO 直接在內存中進行會話維護
     * EnterpriseCacheSessionDAO  提供了緩存功能的會話維護,默認情況下使用MapCache實現,內部使用ConcurrentHashMap保存緩存的會話。
     * @return
     */
    @Bean
    public SessionDAO sessionDAO() {
        RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
        redisSessionDAO.setRedisManager(redisManager());
        //session在redis中的保存時間,最好大於session會話超時時間
        redisSessionDAO.setExpire(12000);
        return redisSessionDAO;
    }

    /**
     * 配置保存sessionId的cookie
     * 注意:這裏的cookie 不是上面的記住我 cookie 記住我需要一個cookie session管理 也需要自己的cookie
     * 默認爲: JSESSIONID 問題: 與SERVLET容器名衝突,重新定義爲sid
     * @return
     */
    @Bean("sessionIdCookie")
    public SimpleCookie sessionIdCookie(){
        //這個參數是cookie的名稱
        SimpleCookie simpleCookie = new SimpleCookie("sid");
        //setcookie的httponly屬性如果設爲true的話,會增加對xss防護的安全係數。它有以下特點:

        //setcookie()的第七個參數
        //設爲true後,只能通過http訪問,javascript無法訪問
        //防止xss讀取cookie
        simpleCookie.setHttpOnly(true);
        simpleCookie.setPath("/");
        //maxAge=-1表示瀏覽器關閉時失效此Cookie
        simpleCookie.setMaxAge(-1);
        return simpleCookie;
    }

    /**
     * 配置會話管理器,設定會話超時及保存
     * @return
     */
    @Bean("sessionManager")
    public SessionManager sessionManager() {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        Collection<SessionListener> listeners = new ArrayList<SessionListener>();
        //配置監聽
        listeners.add(sessionListener());
        sessionManager.setSessionListeners(listeners);
        sessionManager.setSessionIdCookie(sessionIdCookie());
        sessionManager.setSessionDAO(sessionDAO());
        sessionManager.setCacheManager(cacheManager());

        //全局會話超時時間(單位毫秒),默認30分鐘  暫時設置爲10秒鐘 用來測試
        sessionManager.setGlobalSessionTimeout(1800000);
        //是否開啓刪除無效的session對象  默認爲true
        sessionManager.setDeleteInvalidSessions(true);
        //是否開啓定時調度器進行檢測過期session 默認爲true
        sessionManager.setSessionValidationSchedulerEnabled(true);
        //設置session失效的掃描時間, 清理用戶直接關閉瀏覽器造成的孤立會話 默認爲 1個小時
        //設置該屬性 就不需要設置 ExecutorServiceSessionValidationScheduler 底層也是默認自動調用ExecutorServiceSessionValidationScheduler
        //暫時設置爲 5秒 用來測試
        sessionManager.setSessionValidationInterval(3600000);
        //取消url 後面的 JSESSIONID
        sessionManager.setSessionIdUrlRewritingEnabled(false);
        return sessionManager;

    }


    /**
     * 併發登錄控制
     * @return
     */
//    @Bean
//    public KickoutSessionControlFilter kickoutSessionControlFilter(){
//        KickoutSessionControlFilter kickoutSessionControlFilter = new KickoutSessionControlFilter();
//        //用於根據會話ID,獲取會話進行踢出操作的;
//        kickoutSessionControlFilter.setSessionManager(sessionManager());
//        //使用cacheManager獲取相應的cache來緩存用戶登錄的會話;用於保存用戶—會話之間的關係的;
//        kickoutSessionControlFilter.setCacheManager(cacheManager());
//        //是否踢出後來登錄的,默認是false;即後者登錄的用戶踢出前者登錄的用戶;
//        kickoutSessionControlFilter.setKickoutAfter(false);
//        //同一個用戶最大的會話數,默認1;比如2的意思是同一個用戶允許最多同時兩個人登錄;
//        kickoutSessionControlFilter.setMaxSession(1);
//        //被踢出後重定向到的地址;
//        kickoutSessionControlFilter.setKickoutUrl("/login?kickout=1");
//        return kickoutSessionControlFilter;
//    }

    /**
     * 配置密碼比較器
     * @return
     */
//    @Bean("credentialsMatcher")
//    public RetryLimitHashedCredentialsMatcher retryLimitHashedCredentialsMatcher(){
//        RetryLimitHashedCredentialsMatcher retryLimitHashedCredentialsMatcher = new RetryLimitHashedCredentialsMatcher(cacheManager());
//
//        //如果密碼加密,可以打開下面配置
//        //加密算法的名稱
//        //retryLimitHashedCredentialsMatcher.setHashAlgorithmName("MD5");
//        //配置加密的次數
//        //retryLimitHashedCredentialsMatcher.setHashIterations(1024);
//        //是否存儲爲16進制
//        //retryLimitHashedCredentialsMatcher.setStoredCredentialsHexEncoded(true);
//
//        return retryLimitHashedCredentialsMatcher;
//    }

}

ShiroRealm.java

package com.springboot.test.shiro.config.shiro;

import com.springboot.test.shiro.modules.user.dao.PermissionMapper;
import com.springboot.test.shiro.modules.user.dao.RoleMapper;
import com.springboot.test.shiro.modules.user.dao.entity.Permission;
import com.springboot.test.shiro.modules.user.dao.entity.Role;
import com.springboot.test.shiro.modules.user.dao.UserMapper;
import com.springboot.test.shiro.modules.user.dao.entity.User;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
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 java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author: wangsaichao
 * @date: 2018/5/10
 * @description: 在Shiro中,最終是通過Realm來獲取應用程序中的用戶、角色及權限信息的
 * 在Realm中會直接從我們的數據源中獲取Shiro需要的驗證信息。可以說,Realm是專用於安全框架的DAO.
 */
public class ShiroRealm extends AuthorizingRealm {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private RoleMapper roleMapper;

    @Autowired
    private PermissionMapper permissionMapper;

    /**
     * 驗證用戶身份
     * @param authenticationToken
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {

        //獲取用戶名密碼 第一種方式
        //String username = (String) authenticationToken.getPrincipal();
        //String password = new String((char[]) authenticationToken.getCredentials());

        //獲取用戶名 密碼 第二種方式
        UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) authenticationToken;
        String username = usernamePasswordToken.getUsername();
        String password = new String(usernamePasswordToken.getPassword());

        //從數據庫查詢用戶信息
        User user = this.userMapper.findByUserName(username);

        //可以在這裏直接對用戶名校驗,或者調用 CredentialsMatcher 校驗
        if (user == null) {
            throw new UnknownAccountException("用戶名或密碼錯誤!");
        }
        //這裏將 密碼對比 註銷掉,否則 無法鎖定  要將密碼對比 交給 密碼比較器
        //if (!password.equals(user.getPassword())) {
        //    throw new IncorrectCredentialsException("用戶名或密碼錯誤!");
        //}
        if ("1".equals(user.getState())) {
            throw new LockedAccountException("賬號已被鎖定,請聯繫管理員!");
        }

        //調用 CredentialsMatcher 校驗 還需要創建一個類 繼承CredentialsMatcher  如果在上面校驗了,這個就不需要了
        //配置自定義權限登錄器 參考博客:

        SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, user.getPassword(), getName());
        return info;
    }

    /**
     * 授權用戶權限
     * 授權的方法是在碰到<shiro:hasPermission name=''></shiro:hasPermission>標籤的時候調用的
     * 它會去檢測shiro框架中的權限(這裏的permissions)是否包含有該標籤的name值,如果有,裏面的內容顯示
     * 如果沒有,裏面的內容不予顯示(這就完成了對於權限的認證.)
     *
     * shiro的權限授權是通過繼承AuthorizingRealm抽象類,重載doGetAuthorizationInfo();
     * 當訪問到頁面的時候,鏈接配置了相應的權限或者shiro標籤纔會執行此方法否則不會執行
     * 所以如果只是簡單的身份認證沒有權限的控制的話,那麼這個方法可以不進行實現,直接返回null即可。
     *
     * 在這個方法中主要是使用類:SimpleAuthorizationInfo 進行角色的添加和權限的添加。
     * authorizationInfo.addRole(role.getRole()); authorizationInfo.addStringPermission(p.getPermission());
     *
     * 當然也可以添加set集合:roles是從數據庫查詢的當前用戶的角色,stringPermissions是從數據庫查詢的當前用戶對應的權限
     * authorizationInfo.setRoles(roles); authorizationInfo.setStringPermissions(stringPermissions);
     *
     * 就是說如果在shiro配置文件中添加了filterChainDefinitionMap.put("/add", "perms[權限添加]");
     * 就說明訪問/add這個鏈接必須要有“權限添加”這個權限纔可以訪問
     *
     * 如果在shiro配置文件中添加了filterChainDefinitionMap.put("/add", "roles[100002],perms[權限添加]");
     * 就說明訪問/add這個鏈接必須要有 "權限添加" 這個權限和具有 "100002" 這個角色纔可以訪問
     * @param principalCollection
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {

        System.out.println("查詢權限方法調用了!!!");

        //獲取用戶
        User user = (User) SecurityUtils.getSubject().getPrincipal();

        //獲取用戶角色
        Set<Role> roles =this.roleMapper.findRolesByUserId(user.getUid());
        //添加角色
        SimpleAuthorizationInfo authorizationInfo =  new SimpleAuthorizationInfo();
        for (Role role : roles) {
            authorizationInfo.addRole(role.getRole());
        }

        //獲取用戶權限
        Set<Permission> permissions = this.permissionMapper.findPermissionsByRoleId(roles);
        //添加權限
        for (Permission permission:permissions) {
            authorizationInfo.addStringPermission(permission.getPermission());
        }

        return authorizationInfo;
    }

    /**
     * 重寫方法,清除當前用戶的的 授權緩存
     * @param principals
     */
    @Override
    public void clearCachedAuthorizationInfo(PrincipalCollection principals) {
        super.clearCachedAuthorizationInfo(principals);
    }

    /**
     * 重寫方法,清除當前用戶的 認證緩存
     * @param principals
     */
    @Override
    public void clearCachedAuthenticationInfo(PrincipalCollection principals) {
        super.clearCachedAuthenticationInfo(principals);
    }

    @Override
    public void clearCache(PrincipalCollection principals) {
        super.clearCache(principals);
    }

    /**
     * 自定義方法:清除所有 授權緩存
     */
    public void clearAllCachedAuthorizationInfo() {
        getAuthorizationCache().clear();
    }

    /**
     * 自定義方法:清除所有 認證緩存
     */
    public void clearAllCachedAuthenticationInfo() {
        getAuthenticationCache().clear();
    }

    /**
     * 自定義方法:清除所有的  認證緩存  和 授權緩存
     */
    public void clearAllCache() {
        clearAllCachedAuthenticationInfo();
        clearAllCachedAuthorizationInfo();
    }

}

我們可以看一下,在redis中的緩存,如下: 

 

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