Spring Boot Shiro 权限信息缓存处理,记住我,thymleaf使用shiro标签
来源:互联网 发布:信用卡在淘宝限额 编辑:程序博客网 时间:2024/05/21 08:48
转:
http://412887952-qq-com.iteye.com/blog/2299784
权限信息缓存处理
实际中我们的权限信息是不怎么会改变的,所以我们希望是第一次访问,然后进行缓存处理,那么Shiro是否支持呢,答案是肯定的,我们在下一小节进行讲解,如何在Shiro中加入缓存机制。
主要分这么几个步骤:在pom.xml中加入缓存依赖;注入缓存;
(a) 在pom.xml文件中加入依赖:
<!-- shiro ehcache --><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-ehcache</artifactId><version>1.2.3</version></dependency><!-- 包含支持UI模版(Velocity,FreeMarker,JasperReports), 邮件服务, 脚本服务(JRuby), 缓存Cache(EHCache), 任务计划Scheduling(uartz)。 --><dependency><groupId>org.springframework</groupId><artifactId>spring-context-support</artifactId></dependency></dependencies>(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中:
@Beanpublic SecurityManager securityManager(){ DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); //设置realm. securityManager.setRealm(myShiroRealm()); //注入缓存管理器; securityManager.setCacheManager(ehCacheManager());//这个如果执行多次,也是同样的一个对象; return securityManager; }(c)添加缓存配置文件:
在src/main/resouces/config添加ehcache-shiro.xml配置文件:
<?xml version="1.0" encoding="UTF-8"?><ehcache name="es"> <diskStore path="java.io.tmpdir"/> <!-- name:缓存名称。 maxElementsInMemory:缓存最大数目 maxElementsOnDisk:硬盘最大缓存个数。 eternal:对象是否永久有效,一但设置了,timeout将不起作用。 overflowToDisk:是否保存到磁盘,当系统当机时 timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。 timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。 diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false. diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。 diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。 memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。 clearOnFlush:内存数量最大时是否清除。 memoryStoreEvictionPolicy: Ehcache的三种清空策略; FIFO,first in first out,这个是大家最熟的,先进先出。 LFU, Less Frequently Used,就是上面例子中使用的策略,直白一点就是讲一直以来最少被使用的。如上面所讲,缓存的元素有一个hit属性,hit值最小的将会被清出缓存。 LRU,Least Recently Used,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。 --> <defaultCache maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="false" diskPersistent="false" diskExpiryThreadIntervalSeconds="120" /> <!-- 登录记录缓存锁定10分钟 --> <cache name="passwordRetryCache" maxEntriesLocalHeap="2000" eternal="false" timeToIdleSeconds="3600" timeToLiveSeconds="0" overflowToDisk="false" statistics="true"> </cache></ehcache>在配置文件上已经有很详细的解释了,所以这里就过多介绍ehcache的配置了。
运行程序访问:http://127.0.0.1:8080/userInfo/userAdd
查看控制台的打印信息:
权限配置-->MyShiroRealm.doGetAuthorizationInfo()
这个信息就只打印一次了,说明我们的缓存生效了
密码多次输入错误
CredentialsMatcher是shiro提供的用于加密密码和验证密码服务的接口,而HashedCredentialsMatcher正是CredentialsMatcher的一个实现类
public class SimpleCredentialsMatcher extends CodecSupport implements CredentialsMatcherpublic 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<String, AtomicInteger> 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就会从缓存中移除,从而实现了如果登录次数超出指定的值就锁定。
/** * 凭证匹配器 (由于我们的密码校验交给Shiro的SimpleAuthenticationInfo进行处理了 * 所以我们需要修改下doGetAuthenticationInfo中的代码; ) * * @return */@Beanpublic HashedCredentialsMatcher hashedCredentialsMatcher() {HashedCredentialsMatcher hashedCredentialsMatcher = new RetryLimitHashedCredentialsMatcher(ehCacheManager());//new HashedCredentialsMatcher();hashedCredentialsMatcher.setHashAlgorithmName("md5");// 散列算法:这里使用MD5算法;hashedCredentialsMatcher.setHashIterations(2);// 散列的次数,比如散列两次,相当于// md5(md5(""));return hashedCredentialsMatcher;}ehcache-shiro.xml 加入配置
<!-- 登录记录缓存锁定10分钟 --> <cache name="passwordRetryCache" maxEntriesLocalHeap="2000" eternal="false" timeToIdleSeconds="3600" timeToLiveSeconds="0" overflowToDisk="false" statistics="true"> </cache>
登录方法加入异常判断
else if (ExcessiveAttemptsException.class.getName().equals(exception)) { System.out.println("ExcessiveAttemptsException -- > 登录失败次数过多:"); msg = "ExcessiveAttemptsException -- > 登录失败次数过多:"; }
记住我
记住密码实现起来也是比较简单的,主要看下是如何实现的。
在ShiroConfiguration加入两个方法:
@Beanpublic SimpleCookie rememberMeCookie(){ System.out.println("ShiroConfiguration.rememberMeCookie()"); //这个参数是cookie的名称,对应前端的checkbox的name = rememberMe SimpleCookie simpleCookie = new SimpleCookie("rememberMe"); //<!-- 记住我cookie生效时间30天 ,单位秒;--> simpleCookie.setMaxAge(259200); return simpleCookie;}/** * cookie管理对象; * @return */@Beanpublic CookieRememberMeManager rememberMeManager(){ System.out.println("ShiroConfiguration.rememberMeManager()"); CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager(); cookieRememberMeManager.setCookie(rememberMeCookie()); return cookieRememberMeManager;}
将rememberMeManager注入到SecurityManager中
@Beanpublic SecurityManager securityManager() {DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();// 设置realm.securityManager.setRealm(myShiroRealm());//注入缓存管理器; securityManager.setCacheManager(ehCacheManager());//这个如果执行多次,也是同样的一个对象;//注入记住我管理器; securityManager.setRememberMeManager(rememberMeManager());return securityManager;}在ShiroFilterFactoryBean添加记住我过滤器
@Beanpublic ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {System.out.println("ShiroConfiguration.shiroFilter()");ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();// 必须设置SecuritManagershiroFilterFactoryBean.setSecurityManager(securityManager);// 拦截器Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();// 配置退出过滤器,其中的具体代码Shiro已经替我们实现了filterChainDefinitionMap.put("/logout", "logout");//配置记住我或认证通过可以访问的地址 filterChainDefinitionMap.put("/index", "user"); filterChainDefinitionMap.put("/", "user"); // <!-- 过滤链定义,从上向下顺序执行,一般将 /**放在最为下边 -->:这是一个坑呢,一不小心代码就不好使了;// <!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问-->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中加入:
<P><input type="checkbox" name="rememberMe" />记住我</P>
这时候运行程序,登录之后跳转到/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中加入如下依赖:
<dependency> <groupId>com.github.theborakompanioni</groupId> <artifactId>thymeleaf-extras-shiro</artifactId> <version>1.2.1</version></dependency>
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">
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro"><head> <meta charset="UTF-8" /> <title>Insert title here</title> </head> <body> <h3>index</h3> <!-- 验证当前用户是否为“访客”,即未认证(包含未记住)的用户。 --> <p shiro:guest="">Please <a href="login.html">login</a></p> <!-- 认证通过或已记住的用户。 --> <p shiro:user=""> Welcome back John! Not John? Click <a href="login.html">here</a> to login. </p> <!-- 已认证通过的用户。不包含已记住的用户,这是与user标签的区别所在。 --> <p shiro:authenticated=""> Hello, <span shiro:principal=""></span>, how are you today? </p> <a shiro:authenticated="" href="updateAccount.html">Update your contact information</a> <!-- 输出当前用户信息,通常为登录帐号信息。 --> <p>Hello, <shiro:principal/>, how are you today?</p> <!-- 未认证通过用户,与authenticated标签相对应。与guest标签的区别是,该标签包含已记住用户。 --> <p shiro:notAuthenticated=""> Please <a href="login.html">login</a> in order to update your credit card information. </p> <!-- 验证当前用户是否属于该角色。 --> <a shiro:hasRole="admin" href="admin.html">Administer the system</a><!-- 拥有该角色 --> <!-- 与hasRole标签逻辑相反,当用户不属于该角色时验证通过。 --> <p shiro:lacksRole="developer"><!-- 没有该角色 --> Sorry, you are not allowed to developer the system. </p> <!-- 验证当前用户是否属于以下所有角色。 --> <p shiro:hasAllRoles="developer, admin"><!-- 角色与判断 --> You are a developer and a admin. </p> <!-- 验证当前用户是否属于以下任意一个角色。 --> <p shiro:hasAnyRoles="admin, vip, developer"><!-- 角色或判断 --> You are a admin, vip, or developer. </p> <!--验证当前用户是否拥有指定权限。 --> <a shiro:hasPermission="userInfo:add" href="createUser.html">添加用户</a><!-- 拥有权限 --> <!-- 与hasPermission标签逻辑相反,当前用户没有制定权限时,验证通过。 --> <p shiro:lacksPermission="userInfo:del"><!-- 没有权限 --> Sorry, you are not allowed to delete user accounts. </p> <!-- 验证当前用户是否拥有以下所有角色。 --> <p shiro:hasAllPermissions="userInfo:view, userInfo:add"><!-- 权限与判断 --> You can see or add users. </p> <!-- 验证当前用户是否拥有以下任意一个权限。 --> <p shiro:hasAnyPermissions="userInfo:view, userInfo:del"><!-- 权限或判断 --> You can see or delete users. </p> </body> </html>
-----------------------------------------------------转:http://itindex.net/detail/55726-apache-shiro-spring?utm_source=tuicool&utm_medium=referral
但是Spring Security有点太复杂,custmize起来比较费力,不如shiro来的简单。
如果想要在Spring boot里使用shiro,需要进行以下配置,首先pom.xml里要添加shiro的依赖
<dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.2.5</version></dependency><dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-ehcache</artifactId> <version>1.2.5</version></dependency><dependency> <groupId>com.github.theborakompanioni</groupId> <artifactId>thymeleaf-extras-shiro</artifactId> <version>1.2.1</version></dependency>
shiro官方只提供了jsp的标签,没有提供thymeleaf的,而thymeleaf在spring boot里应用已经很广泛了,这里依赖了一个第三方包。
然后就是shiro的配置文件,这里我们使用java-based 配置
@Configurationpublic 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<String, Filter> filters = new LinkedHashMap<String, Filter>();LogoutFilter logoutFilter = new LogoutFilter();logoutFilter.setRedirectUrl("/login");filters.put("logout", logoutFilter);shiroFilterFactoryBean.setFilters(filters);Map<String, String> filterChainDefinitionManager = new LinkedHashMap<String, String>();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<? extends Annotation>[] AUTHZ_ANNOTATION_CLASSES =
new Class[] {
RequiresPermissions.class, RequiresRoles.class,
RequiresUser.class, RequiresGuest.class, RequiresAuthentication.class
};
9.ShiroDialect,为了在thymeleaf里使用shiro的标签的bean
- Spring Boot Shiro 权限信息缓存处理,记住我,thymleaf使用shiro标签
- Spring Boot Shiro 权限信息缓存处理,记住我,thymleaf使用shiro标签
- spring boot 集成shiro记住我实现
- spring boot freemark 使用shiro标签
- spring boot使用shiro
- Spring Boot Shiro 权限管理
- Spring Boot Shiro 权限管理
- Spring Boot Shiro权限管理
- Spring Boot Shiro 权限管理
- Spring Boot Shiro权限管理
- Spring Boot Shiro 权限管理
- Spring Boot Shiro 权限管理
- Spring Boot Shiro 权限管理
- spring boot shiro 权限管理
- spring boot shiro权限管理
- Spring Boot Shiro 权限管理
- shiro+spring使用标签
- 我的shiro之旅: 九 shiro 清理缓存的权限信息
- 重新启动(结束并打开)explorer.exe C++
- 消息队列与如何spring mvc 项目中加入消息队列
- csdn下载频道资源整理
- C++中引用(&)的用法详解
- Java 面试题
- Spring Boot Shiro 权限信息缓存处理,记住我,thymleaf使用shiro标签
- Python数据类型——序列(列表、元组、字符串)
- WPF技术特点
- Visual studio 学习
- 后缀数组模板(详细理解)
- 并查集应用:入门级
- Shader结构学习手记
- AMQP协议介绍
- Leetcode 18 4Sum