Spring Boot Shiro 权限信息缓存处理,记住我,thymleaf使用shiro标签

转:
http://412887952-qq-com.iteye.com/blog/2299784

权限信息缓存处理

实际中我们的权限信息是不怎么会改变的,所以我们希望是第一次访问,然后进行缓存处理,那么Shiro是否支持呢,答案是肯定的,我们在下一小节进行讲解,如何在Shiro中加入缓存机制。

主要分这么几个步骤:在pom.xml中加入缓存依赖;注入缓存;
(a) 在pom.xml文件中加入依赖:


		
			org.apache.shiro
			shiro-ehcache
			1.2.3
		
		
		
			org.springframework
			spring-context-support
		
	
(b)注入缓存
在ShiroConfiguration中加入如下方法:

	/**
     * shiro缓存管理器;
     * 需要注入对应的其它的实体类中:
     * 1、安全管理器:securityManager
     * 可见securityManager是整个shiro的核心;
     * @return
     */

    @Bean
    public EhCacheManager ehCacheManager(){
       System.out.println("ShiroConfiguration.getEhCacheManager()");
       EhCacheManager cacheManager = new EhCacheManager();
       cacheManager.setCacheManagerConfigFile("classpath:config/ehcache-shiro.xml");
       return cacheManager;

    }

将缓存对象注入到SecurityManager中:

@Bean
public SecurityManager securityManager(){
       DefaultWebSecurityManager securityManager =  new DefaultWebSecurityManager();
       //设置realm.
       securityManager.setRealm(myShiroRealm());
       //注入缓存管理器;
       securityManager.setCacheManager(ehCacheManager());//这个如果执行多次,也是同样的一个对象;
       return securityManager;
    }
(c)添加缓存配置文件:
在src/main/resouces/config添加ehcache-shiro.xml配置文件:



    
         
    
    
    
    

在配置文件上已经有很详细的解释了,所以这里就过多介绍ehcache的配置了。
运行程序访问:http://127.0.0.1:8080/userInfo/userAdd
查看控制台的打印信息:
权限配置-->MyShiroRealm.doGetAuthorizationInfo()

这个信息就只打印一次了,说明我们的缓存生效了


密码多次输入错误

CredentialsMatcher是shiro提供的用于加密密码和验证密码服务的接口,而HashedCredentialsMatcher正是CredentialsMatcher的一个实现类

public class SimpleCredentialsMatcher extends CodecSupport implements CredentialsMatcher

public class HashedCredentialsMatcher extends SimpleCredentialsMatcher

自定义RetryLimitHashedCredentialsMatcher继承HashedCredentialsMatcher

package com.example.config.shiro;

import java.util.concurrent.atomic.AtomicInteger;

import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.ExcessiveAttemptsException;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheManager;

public class RetryLimitHashedCredentialsMatcher extends  HashedCredentialsMatcher{
	private Cache passwordRetryCache;  
	  
    public RetryLimitHashedCredentialsMatcher(CacheManager cacheManager) {  
        passwordRetryCache = cacheManager.getCache("passwordRetryCache");  
    }  
  
    @Override  
    public boolean doCredentialsMatch(AuthenticationToken token,  
            AuthenticationInfo info) {  
        String username = (String) token.getPrincipal();  
        // retry count + 1  
        AtomicInteger retryCount = passwordRetryCache.get(username);  
        if (retryCount == null) {  
            retryCount = new AtomicInteger(0);  
            passwordRetryCache.put(username, retryCount);  
        }  
        if (retryCount.incrementAndGet() > 5) {  
            // if retry count > 5 throw  
            throw new ExcessiveAttemptsException();  
        }  
  
        boolean matches = super.doCredentialsMatch(token, info);  
        if (matches) {  
            // clear retry count  
            passwordRetryCache.remove(username);  
        }  
        return matches;  
    }  

}

在回调方法doCredentialsMatch(AuthenticationToken token,AuthenticationInfo info)中进行身份认证的密码匹配,这里我们引入了Ehcahe用于保存用户登录次数,如果登录失败retryCount变量则会一直累加,如果登录成功,那么这个count就会从缓存中移除,从而实现了如果登录次数超出指定的值就锁定。


修改ShiroConfiguration的方法

	/**
	 * 凭证匹配器 (由于我们的密码校验交给Shiro的SimpleAuthenticationInfo进行处理了
	 * 所以我们需要修改下doGetAuthenticationInfo中的代码; )
	 * 
	 * @return
	 */
	@Bean
	public HashedCredentialsMatcher hashedCredentialsMatcher() {
		HashedCredentialsMatcher hashedCredentialsMatcher = new RetryLimitHashedCredentialsMatcher(ehCacheManager());
				//new HashedCredentialsMatcher();
		hashedCredentialsMatcher.setHashAlgorithmName("md5");// 散列算法:这里使用MD5算法;
		hashedCredentialsMatcher.setHashIterations(2);// 散列的次数,比如散列两次,相当于
														// md5(md5(""));

		return hashedCredentialsMatcher;
	}
ehcache-shiro.xml 加入配置


    
    

登录方法加入异常判断

else if (ExcessiveAttemptsException.class.getName().equals(exception)) {  
                System.out.println("ExcessiveAttemptsException -- > 登录失败次数过多:");  
                msg = "ExcessiveAttemptsException -- > 登录失败次数过多:";  
            }




记住我

记住密码实现起来也是比较简单的,主要看下是如何实现的。
在ShiroConfiguration加入两个方法:

@Bean
public SimpleCookie rememberMeCookie(){

       System.out.println("ShiroConfiguration.rememberMeCookie()");
       //这个参数是cookie的名称,对应前端的checkbox的name = rememberMe
       SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
       //
       simpleCookie.setMaxAge(259200);
       return simpleCookie;
}
/**
  * cookie管理对象;
  * @return
  */
@Bean
public CookieRememberMeManager rememberMeManager(){

       System.out.println("ShiroConfiguration.rememberMeManager()");
       CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
       cookieRememberMeManager.setCookie(rememberMeCookie());
       return cookieRememberMeManager;
}

将rememberMeManager注入到SecurityManager中

	@Bean
	public SecurityManager securityManager() {
		DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
		// 设置realm.
		securityManager.setRealm(myShiroRealm());
		//注入缓存管理器;
	     securityManager.setCacheManager(ehCacheManager());//这个如果执行多次,也是同样的一个对象;
		//注入记住我管理器;
	    securityManager.setRememberMeManager(rememberMeManager());
		return securityManager;
	}
在ShiroFilterFactoryBean添加记住我过滤器
	@Bean
	public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
		System.out.println("ShiroConfiguration.shiroFilter()");
		ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();

		// 必须设置SecuritManager
		shiroFilterFactoryBean.setSecurityManager(securityManager);

		// 拦截器
		Map filterChainDefinitionMap = new LinkedHashMap();

		// 配置退出过滤器,其中的具体代码Shiro已经替我们实现了
		filterChainDefinitionMap.put("/logout", "logout");
		
		//配置记住我或认证通过可以访问的地址

        filterChainDefinitionMap.put("/index", "user");
        filterChainDefinitionMap.put("/", "user");
        
		// :这是一个坑呢,一不小心代码就不好使了;
		// 
		filterChainDefinitionMap.put("/servlet/safecode", "anon");
		filterChainDefinitionMap.put("/**", "authc");

		// 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
		shiroFilterFactoryBean.setLoginUrl("/login");
		// 登录成功后要跳转的链接
		shiroFilterFactoryBean.setSuccessUrl("/index");
		// 未授权界面;
		shiroFilterFactoryBean.setUnauthorizedUrl("/403");
		
		shiroFilterFactoryBean
				.setFilterChainDefinitionMap(filterChainDefinitionMap);
		return shiroFilterFactoryBean;

	}
主要是加入了:
//配置记住我或认证通过可以访问的地址
filterChainDefinitionMap.put("/index", "user");
filterChainDefinitionMap.put("/", "user");

修改登录界面加入rememberMe复选框:
在login.html中加入:

记住我

这时候运行程序,登录之后跳转到/index页面,然后我们关闭浏览器,然后直接访问/index还是可以访问的,说明我们写的记住密码已经生效了,如果访问http://127.0.0.1:8080/userInfo/userAdd 的
话还是需要重新登录的。

------

thymleaf使用shiro标签

shiro权限框架,前端验证是为jsp设计的,其中的tag只能用于jsp系列的模板引擎。最近项目使用了thymeleaf作为前端模板引擎,使用HTML文件,没法引入shiro的tag lib,此时如果要使用shiro的话,可以引入 thymeleaf-extras-shiro.jar这个拓展包来曲线实现shiro的前端验证。

https://github.com/theborakompanioni/thymeleaf-extras-shiro

在pom.xml中加入如下依赖:


		    com.github.theborakompanioni
		    thymeleaf-extras-shiro
		    1.2.1
		

ShiroConfiguration.java中添加

    /**
     * ShiroDialect,为了在thymeleaf里使用shiro的标签的bean
     * @return
     */
    @Bean
    public ShiroDialect shiroDialect(){
		return new ShiroDialect();
	}

index.html引入

<html xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">

  

  
  
Insert title here  
  
  
    

index

Please login

Welcome back John! Not John? Click here to login.

Hello, , how are you today?

Update your contact information

Hello, , how are you today?

Please login in order to update your credit card information.

Administer the system

Sorry, you are not allowed to developer the system.

You are a developer and a admin.

You are a admin, vip, or developer.

添加用户

Sorry, you are not allowed to delete user accounts.

You can see or add users.

You can see or delete users.



-----------------------------------------------------转:http://itindex.net/detail/55726-apache-shiro-spring?utm_source=tuicool&utm_medium=referral

实际上在Spring boot里用Spring Security最合适,毕竟是自家东西,最重要的一点是Spring Security里自带有csrf filter,防止csrf攻击,shiro里就没有。
但是Spring Security有点太复杂,custmize起来比较费力,不如shiro来的简单。

如果想要在Spring boot里使用shiro,需要进行以下配置,首先pom.xml里要添加shiro的依赖


		 org.apache.shiro
		    shiro-spring
		    1.2.5
		
		
		    org.apache.shiro
		    shiro-ehcache
		    1.2.5
		
		
		    com.github.theborakompanioni
		    thymeleaf-extras-shiro
		 1.2.1

shiro官方只提供了jsp的标签,没有提供thymeleaf的,而thymeleaf在spring boot里应用已经很广泛了,这里依赖了一个第三方包。
然后就是shiro的配置文件,这里我们使用java-based 配置

@Configuration
public class ShiroConfiguration {
	
    @Bean(name = "lifecycleBeanPostProcessor")
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }
	
	@Bean(name = "hashedCredentialsMatcher")
	public HashedCredentialsMatcher hashedCredentialsMatcher() {
		HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
		credentialsMatcher.setHashAlgorithmName("MD5");
		credentialsMatcher.setHashIterations(2);
		credentialsMatcher.setStoredCredentialsHexEncoded(true);
		return credentialsMatcher;
	}
	
    @Bean(name = "shiroRealm")
    @DependsOn("lifecycleBeanPostProcessor")
    public ShiroRealm shiroRealm() {
		ShiroRealm realm = new ShiroRealm(); 
		realm.setCredentialsMatcher(hashedCredentialsMatcher());
        return realm;
    }
	
	@Bean(name = "ehCacheManager")
	@DependsOn("lifecycleBeanPostProcessor")
	public EhCacheManager ehCacheManager(){
		EhCacheManager ehCacheManager = new EhCacheManager();
		return ehCacheManager;
	}
	
	@Bean(name = "securityManager")
	public SecurityManager securityManager(){
		DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
		securityManager.setRealm(shiroRealm());
		securityManager.setCacheManager(ehCacheManager());
		return securityManager;
	}

	@Bean(name = "shiroFilter")
	public ShiroFilterFactoryBean shiroFilterFactoryBean(){
		ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
		shiroFilterFactoryBean.setSecurityManager(securityManager());
		
		Map filters = new LinkedHashMap();
		LogoutFilter logoutFilter = new LogoutFilter();
		logoutFilter.setRedirectUrl("/login");
		filters.put("logout", logoutFilter);
		shiroFilterFactoryBean.setFilters(filters);
		
		Map filterChainDefinitionManager = new LinkedHashMap();
		filterChainDefinitionManager.put("/logout", "logout");
		filterChainDefinitionManager.put("/user/**", "authc,roles[user]");
		filterChainDefinitionManager.put("/shop/**", "authc,roles[shop]");
		filterChainDefinitionManager.put("/admin/**","authc,roles[admin]");
		filterChainDefinitionManager.put("/**", "anon");
		shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionManager);
		
		shiroFilterFactoryBean.setLoginUrl("/login");
		shiroFilterFactoryBean.setSuccessUrl("/");
		shiroFilterFactoryBean.setUnauthorizedUrl("/403");
		
		return shiroFilterFactoryBean;
	}

	@Bean
	@ConditionalOnMissingBean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator daap = new DefaultAdvisorAutoProxyCreator();
        daap.setProxyTargetClass(true);
        return daap;
    }
	
	@Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
        AuthorizationAttributeSourceAdvisor aasa = new AuthorizationAttributeSourceAdvisor();
        aasa.setSecurityManager(securityManager());
        return aasa;
    }
	
	@Bean(name = "shiroDialect")
	public ShiroDialect shiroDialect(){
		return new ShiroDialect();
	}
}


1.LifecycleBeanPostProcessor,这是个DestructionAwareBeanPostProcessor的子类,负责org.apache.shiro.util.Initializable类型bean的生命周期的,初始化和销毁。主要是AuthorizingRealm类的子类,以及EhCacheManager类。

2.HashedCredentialsMatcher,这个类是为了对密码进行编码的,防止密码在数据库里明码保存,当然在登陆认证的生活,这个类也负责对form里输入的密码进行编码。

3.ShiroRealm,这是个自定义的认证类,继承自AuthorizingRealm,负责用户的认证和权限的处理,可以参考JdbcRealm的实现。
4.EhCacheManager,缓存管理,用户登陆成功后,把用户信息和权限信息缓存起来,然后每次用户请求时,放入用户的session中,如果不设置这个bean,每个请求都会查询一次数据库。
5.SecurityManager,权限管理,这个类组合了登陆,登出,权限,session的处理,是个比较重要的类。
6.ShiroFilterFactoryBean,是个factorybean,为了生成ShiroFilter。它主要保持了三项数据,securityManager,filters,filterChainDefinitionManager。
7.DefaultAdvisorAutoProxyCreator,Spring的一个bean,由Advisor决定对哪些类的方法进行AOP代理。
 8.AuthorizationAttributeSourceAdvisor,shiro里实现的Advisor类,内部使用AopAllianceAnnotationsAuthorizingMethodInterceptor来拦截用以下注解的方法。老实说,这里注入securityManager,我不知道有啥用,从source上看不出它在什么地方会被调用。

    private static final Class[] AUTHZ_ANNOTATION_CLASSES =
            new Class[] {
                    RequiresPermissions.class, RequiresRoles.class,
                    RequiresUser.class, RequiresGuest.class, RequiresAuthentication.class
            };

9.ShiroDialect,为了在thymeleaf里使用shiro的标签的bean


你可能感兴趣的:(springBoot)