1. 程式人生 > >spring boot+shiro+ehcache 使用者嘗試登入錯誤次數

spring boot+shiro+ehcache 使用者嘗試登入錯誤次數

本文主要實踐用spring boot + shiro+ ehcache實現使用者登入錯誤次數的判斷,關於spring boot, shiro, ehcache的理論知識這裡將不作為介紹,這些知識可以在它們的官網或其它部落格上能夠找到 。下面將進入正文:

pom.xml

本文主要引用的jar如下:

<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>1.4.0</version>
</dependency>
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-ehcache</artifactId>
    <version>1.4.0</version>
</dependency>

用於我們將使用shiro + ehache配合使用,所以可以不用單獨再引用ehcache.jar了,使用shiro-ehcache時,會自動新增ehcache-core 2.6.11。

ehcache.xml

由於我們使用了ehcache,所以我們需要新增一個配置檔案ehcache.xml,此檔案目錄為src/main/resources/config/ehcache.xml,內容如下:

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
    updateCheck="false"
    dynamicConfig="true"
    name="ehcache_retry">

    <!-- 磁碟快取位置 -->
    <diskStore path="java.io.tmpdir/ehcache" />

    <!-- 預設快取 -->
    <defaultCache maxEntriesLocalHeap="10000" 
        eternal="false"
        timeToIdleSeconds="120" 
        timeToLiveSeconds="120" 
        maxEntriesLocalDisk="10000000"
        diskExpiryThreadIntervalSeconds="120" 
        memoryStoreEvictionPolicy="LRU">
        <persistence strategy="localTempSwap" />
    </defaultCache>

    <!-- helloworld快取 -->
    <cache name="passwordRetryCache" 
        maxElementsInMemory="2000" 
        eternal="false"
        timeToIdleSeconds="600" 
        timeToLiveSeconds="600" 
        overflowToDisk="false"
        statistics="true" />
</ehcache>

配置中不能對ehcache標籤新增monitoring="autodetect",否側快取將無法儲存。

注:

    timeToIdleSeconds – The maximum number of seconds an element can exist in the cache without being accessed. The element expires at this limit and will no longer be returned from the cache. The default value is 0, which means no TTI eviction takes place (infinite lifetime). 

    timeToLiveSeconds – The maximum number of seconds an element can exist in the cache regardless of use. The element expires at this limit and will no longer be returned from the cache. The default value is 0, which means no TTL eviction takes place (infinite lifetime).

    maxElementsOnDisk – The maximum sum total number of elements (cache entries) allowed for a distributed cache in all Terracotta clients. If this target is exceeded, eviction occurs to bring the count within the allowed target. The default value is 0, which means no eviction takes place (infinite size is allowed). Note that this value reflects storage allocated on the Terracotta Server Array. A setting of 0 means that no eviction of the cache's entries takes place on Terracotta Server Array, and consequently can cause the servers to run out of disk space.

    eternal – If the cacheâ–s eternal flag is set, it overrides any finite TTI/TTL values that have been set

ShiroRealm.java

public class ShiroRealm extends AuthorizingRealm{
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        return authorizationInfo;
    }

	/* 登入驗證
	 * @see org.apache.shiro.realm.AuthenticatingRealm#doGetAuthenticationInfo(org.apache.shiro.authc.AuthenticationToken)
	 */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        String userName = (String)token.getPrincipal();
        User user = this.userService.findByUserName(userName);
        if (user != null) {
            SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, user.getPassword(), getName());
            info.setCredentialsSalt(new SimpleByteSource(ByteSource.Util.bytes(MD5Util.SALT)));
            return info;
        }
        return null;
    }
}

由於doGetAuthorizationInfo主要用於使用者授權部分,所以這裡沒有貼出程式碼,讀者可以根據自己的業務需求來完善該部分。對於方法doGetAuthenticationInfo中,讀者注意到info.setCredentialsSalt(new SimpleByteSource(ByteSource.Util.bytes(MD5Util.SALT)));,這個是因為我在儲存密碼時,用的shiro MD5+salt的方式加密,所以在這裡驗證的時候,必須與加密時的演算法保持一致,這樣才能夠驗證通過。由於HashedCredentialsMatcher不提供salt的設定,所以在返回AuthenticationInfo的時候需要把salt帶上一起返回出去。

RetryLimitCredentialsMatcher.java

public class RetryLimitCredentialsMatcher extends HashedCredentialsMatcher{
    private static final int MAX_LOGIN_RETRY_TIMES = 5;
    private Cache<String, AtomicInteger> passwordRetryCache;
	
    public RetryLimitCredentialsMatcher(EhCacheManager ehCacheManager) {
        passwordRetryCache = ehCacheManager.getCache("passwordRetryCache");
    }
	
    @Override
    public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) throws ExcessiveAttemptsException{
        String userName = (String) token.getPrincipal();
        AtomicInteger retryCount = passwordRetryCache.get(userName);
        if (retryCount == null) {
            // 高併發下使用的執行緒安全的int類
            retryCount = new AtomicInteger(0);
            passwordRetryCache.put(userName, retryCount);
        }
        if (retryCount.incrementAndGet() > MAX_LOGIN_RETRY_TIMES) {
            throw new ExcessiveAttemptsException();
        }
	
        boolean match = super.doCredentialsMatch(token, info);
        if (match) {
            passwordRetryCache.remove(userName);
        }
		
        return match;
    }
}

這個類的主要作用就是計算並快取使用者嘗試登陸的次數,如果大於了5次,那麼該使用者將被禁止登陸直到10分鐘以後。這個時間在ehcache.xml中timeToIdleSeconds設定。

ShiroConfig.java

對shiro進行配置:

@Configuration
public class ShiroConfig {
    @Bean
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
		
        filterChainDefinitionMap.put("/logout", "logout");
        filterChainDefinitionMap.put("/login", "user");
        //<!-- authc:所有url都必須認證通過才可以訪問; anon:所有url都都可以匿名訪問-->
        filterChainDefinitionMap.put("/css/**", "anon");
        filterChainDefinitionMap.put("/js/**", "anon");
        filterChainDefinitionMap.put("/img/**", "anon");
        filterChainDefinitionMap.put("/**", "authc");//if authc, all url should be authenticated 
        // 如果不設定預設會自動尋找Web工程根目錄下的"/login.jsp"頁面
        shiroFilterFactoryBean.setLoginUrl("/login");
        // 登入成功後要跳轉的連結
        shiroFilterFactoryBean.setSuccessUrl("/index");
		
        //未授權介面;
        shiroFilterFactoryBean.setUnauthorizedUrl("/403");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
		
        return shiroFilterFactoryBean;
    }
	
    @Bean
    public ShiroRealm shiroRealm() {
        ShiroRealm shiroRealm = new ShiroRealm();
        shiroRealm.setCredentialsMatcher(this.retryLimitCredentialsMatcher());
        return shiroRealm;
    }
	
    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(this.shiroRealm());
        return securityManager;
    }
	
    @Bean
    public EhCacheManager ehCacheManager() {
        EhCacheManager ehCacheManager = new EhCacheManager();
        ehCacheManager.setCacheManagerConfigFile("classpath:config/ehcache.xml");
        return ehCacheManager;
    }
	
    @Bean
    public CredentialsMatcher retryLimitCredentialsMatcher() {
        RetryLimitCredentialsMatcher retryLimitCredentialsMatcher = new RetryLimitCredentialsMatcher(this.ehCacheManager());
        retryLimitCredentialsMatcher.setHashAlgorithmName(MD5Util.ALGORITH_NAME);
        retryLimitCredentialsMatcher.setHashIterations(MD5Util.HASH_ITERATIONS);
        retryLimitCredentialsMatcher.setStoredCredentialsHexEncoded(true);
        return retryLimitCredentialsMatcher;
    }
}

注:在這個類中,org.apache.shiro.cache.ehcache.EhCacheManager的作用是為了載入ehcache.xml,另外,retryLimitCredentialsMatcher的作用是為了設定一個和密碼加密演算法一樣的演算法,其中一定要設定Hash Algorithm Name(我用的是MD5),否則會有exception提示HashAlgorithmName必須設定,這是因為我們使用的ehcache版本為2.5及以上,在此時,如果還有異常

net.sf.ehcache.CacheException: Another CacheManager with same name 'MD5' already exists in the same VM.

這是由於在對密碼加密的時候也是使用的HashAlgorithmName = MD5,這個時候記憶體中已經存在了一個叫做MD5 CacheManager ,同時我們為了讓加密演算法和驗證時候的演算法也一樣,我們不得不在此也設定為MD5,所以需要在ehcache.xml中為cache設定一個name="ehcache_retry",這也是為什麼讀者在前文中ehcache.xml中看到有name。

為了能夠使用RetryLimitCredentialsMatcher,我們需要在shiroRealm中將其設定進去,這樣一個使用者登入嘗試錯誤次數的判斷器就算做好了。

當然,我們使用的是spring boot,同時開啟了自動編譯,所以當代碼改動後,會自動重啟伺服器,這個時候你會發現又會出現以下exception

Caused by: net.sf.ehcache.CacheException: Another CacheManager with same name 'ehcache_retry' already exists in the same VM. Please provide unique names for each CacheManager in the config or do one of following:
1. Use one of the CacheManager.create() static factory methods to reuse same CacheManager with same name or create one if necessary
2. Shutdown the earlier cacheManager before creating new one with same name.
The source of the existing CacheManager is: InputStreamConfigurationSource [[email protected]]

這是用於之前的EhCacheManager還存在,並沒有因自動重啟而失效(除非是手動的關閉再重啟),在重啟的時候回再次建立一個EhCacheManager,導致重複,為了解決這個問題,我們需要新增一個Listener來監聽當前application是否是關閉了又重啟。如果是,那麼我們將之前的EhCacheManager也關掉。

SpringEhcacheShutdownListener.java

@Component
public class SpringEhcacheShutdownListener implements ApplicationListener<ApplicationEvent>{

    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        if (event instanceof ContextClosedEvent) {
            ApplicationContext context = ((ContextClosedEvent) event).getApplicationContext();
            EhCacheManager ehCacheManager = (EhCacheManager) context.getBean("ehCacheManager");
            if (ehCacheManager != null) {
                ehCacheManager.destroy();
            }
        }
    }
}

本文是我初次寫部落格,如有不好之處,請多多指教。若文章有疑問之處,可以留言一起討論。

相關推薦

spring boot+shiro+ehcache 使用者嘗試登入錯誤次數

本文主要實踐用spring boot + shiro+ ehcache實現使用者登入錯誤次數的判斷,關於spring boot, shiro, ehcache的理論知識這裡將不作為介紹,這些知識可以在它們的官網或其它部落格上能夠找到 。下面將進入正文: pom.xml 本

Spring Boot +Shiro 驗證碼Filter和限制密碼錯誤次數

驗證碼校驗 CustomFormAuthenticationFilter 將我們自己實現的Filter,放到ShiroFilterFactoryBean中。 ShiroFilterFactor

Spring boot + shiro + redis 實現session共享(偽單點登入

    為實現Web應用的分散式叢集部署,要解決登入session的統一。本文利用shiro做許可權控制,redis做session儲存,結合spring boot快速配置實現session共享。注意本文未解決跨域的問題。不過對於一般的情況能夠很好的起到作用,具體已經在不同埠

spring boot shiro redis整合principals.getPrimaryPrincipal()強制轉換型別錯誤

 spring boot專案使用ShiroUser shiroUser=(ShiroUser)principals.getPrimaryPrincipal() 報錯 錯誤:java.lang.ClassCastException:com.zyc.springboot.shi

spring boot + shiro 登錄驗證

real ron 身份驗證 style feedback red ack ide color form提交 <form th:action="@{/login}" method="POST"> <d

spring boot shiro -權限管理

開發人員 訪問控制 代碼 -s crypt 登錄 phy 集成測試 單元測試 spring boot shiro -權限管理 定義 java最常用的框架有spring security 和Apache shiro,因為spring security 龐大和負責,一般使用都是

spring boot: GlobalDefaultExceptionHandler方法內的友好錯誤提示,全局異常捕獲

clas urn ice servlet 提示 ram oca release format spring boot: GlobalDefaultExceptionHandler方法內的友好錯誤提示,全局異常捕獲 當你的某個控制器內的某個方法報錯,基本上回顯示出java錯誤

Spring boot 學習筆記 1 - 自定義錯誤

note ride 覆蓋 ide rac med exception cat 異常 Spring Boot提供了WebExceptionHandler一個以合理的方式處理所有錯誤的方法。它在處理順序中的位置就在WebFlux提供的處理程序之前,這被認為是最後一個處理程序。

spring-boot + shiro + mybatis 的 demo

這裡總結下用springboot實現shiro的幾個要點,如果要下載完整的專案,請到https://download.csdn.net/download/howard789/10740550(下載後先在本地建立test_shiro資料庫,然後執行resources的sql包下的5個sql檔案),啟動

spring boot security 防止使用者重複登入(原創)

原理:在認證成功通過後,在顯示登入成功頁面之前,也就是在SavedRequestAwareAuthenticationSuccessHandler類中操作。 新增一個集合sessionMap 用於儲存認證成功的會話,鍵名為會話ID, 每次有使用者登入認證通過都要判斷一下是否重複登入

spring boot整合ehcache 2.x 用於hibernate二級快取

spring boot整合ehcache 2x 用於hibernate二級快取 專案依賴 Ehcache簡介 hibernate二級快取配置 ehcache配置檔案 ehcache事件監聽 註解方式使用二級快取 完整程式碼 本文將介紹如何在spring boot中整合ehcache作為hiberna

Spring Boot整合ehcache

1.pom檔案引入 <!-- caching --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-star

spring boot 使用ehcache 實現快取

所謂快取是一種儲存機制,可將資料儲存在某一個地方,並以一種更快的方式為以後的請求提供服務 spring 對於快取提供了宣告式快取註解 ,並提供了四種類型的宣告式快取註解(同樣是使用了AOP 技術實現),這些註解定義了那些 方法的返回值將要被快取或者從快取儲存器中移除 ,需要注意的是,

Spring Boot shiro 許可權管理 小例子

1.引入基本依賴 <project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   xsi:schema

Spring Boot整合Ehcache步驟

SpringBoot 整合 Ehcache 1 、修改 pom 檔案              <groupId>com.bjsxt</groupId>              <artifactId>23-spring-boot-

spring-boot + shiro + mybatis 的 demo

這裡總結下用springboot實現shiro的幾個要點,如果要下載完整的專案,請到********(下載後先在本地建立test_shiro資料庫,然後執行resources的sql包下的5個sql檔案),啟動專案即可看到網頁並且測試 說一下要點: 資料庫一般至少

spring boot 使用ssm框架之登入

Spring Boot 開發Spring Boot的主要動機是簡化配置和部署spring應用程式的過程。 Spring Boot的主要特點: 建立獨立的Spring應用程式 直接嵌入Tomcat,Jetty或Undertow(無需部署WAR檔案) 提供“初始”的PO

Spring boot+Security OAuth2 自定義登入和授權頁面

Spring boot+Security OAuth2 自定義登入和授權頁面   1. 依賴 <!---------thymeleaf 模板引擎--------> <dependency> <groupId>org.springfra

spring boot 整合 EHcache 實現本地快取

需要的依賴如下,pom檔案新增 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-

Spring Boot Shiro許可權管理1

原文地址:https://412887952-qq-com.iteye.com/blog/2299732 (1). Shiro簡單介紹 (2). 整合Shiro核心分析 (3). 無Shiro的Spring Boot (4). 整合Shiro 進行使用者授權