会话管理
Shiro提供了完整的企业级会话管理功能,不依赖于底层容器(如Tomcat),不管是J2SE还是J2EE环境都可以使用,提供了会话管理,会话事件监听,会话存储/持久化,容器无关的集群,失效/过期支持,对Web的透明支持,SSO单点登录的支持等特性。
会话管理
Shiro提供了完整的企业级会话管理功能,不依赖于底层容器(如Tomcat),不管是J2SE还是J2EE环境都可以使用,提供了会话管理,会话事件监听,会话存储/持久化,容器无关的集群,失效/过期支持,对Web的透明支持,SSO单点登录的支持等特性。
会话使用
建议在开发中,Controller层使用原生的HttpSession对象,在Service层中使用Shiro提供的Session对象。如果在Service层中使用HttpSession对象,那么属于侵入式,并不建议这么做。
shiro提供的Session能够很好的解决这个问题。
在Controller中,通过request.getSession()获取会话session,该session到底来源于ServletRequest还是由Shiro管理并创建会话,主要由安全管理器SecurityManage和SessionManager会话管理器决定。
在使用默认SessionManager会话管理器的情况下,不管是通过request.getSession()或者subject.getSession()获取到session,操作session,两者都是等价的,请大家放心使用!
缓存
问题分析
每次在访问设置了权限的页面时,都会去执行doGetAuthorizationInfo()方法来获取权限信息判断当前用户是否具备访问权限。由于在实际情况中,权限是不会经常改变的,能否不用每次都去执行doGetAuthorizationInfo()方法获取权限呢?
解决方法
解决方法就是对权限授权数据进行缓存处理,我们会使用第三方的shiro-redis集成redis实现缓存。
具体实现
1.添加依赖
<dependency><groupId>org.crazycake</groupId><artifactId>shiro-redis</artifactId><version>3.1.0</version>
</dependency>
2.application.properties配置文件中添加Redis配置
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/springboot?useTimezone=true&serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8&autoReconnect=true&useSSL=falseusername: rootpassword: hzx
3.改造ShiroConfig
ShiroConfig改造的步骤如下:
注入Rrdis参数:@Value注解从application.properties配置文件中获取
添加redisManager():创建RedisManager
添加cacheManager():创建RedisCacheManager,注入RedisManager
添加redisSessionDao():创建RedisSessionDAO,注入RedusSessionDAO
修改myShiroRealm():创建MyShiroRealm,启用缓存
修改securityManager():创建SecurityManager,注入MyShiroRealm、RedisCacheManager、SessionManager
@Configuration
public class ShiroConfig {//注入Redis参数,从application.properties获得@Value("${spring.redis.host}")private String host;@Value("${spring.redis.port}")private int port;/* @Value("${spring.redis.password}")private String password;*/@Value("${spring.redis.timeout}")private int timeout;@Resourceprivate RoleService roleService;/* @Bean(name="shiroDialect")public ShiroDialect shiroDialect(){return new ShiroDialect();}*//*** 开启Shiro注解(如@RequiresRoles,@RequiresPermissions),* 需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证* 配置以下两个bean(DefaultAdvisorAutoProxyCreator和AuthorizationAttributeSourceAdvisor)* @return*/@Beanpublic DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator(){DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator=new DefaultAdvisorAutoProxyCreator();advisorAutoProxyCreator.setProxyTargetClass(true);return advisorAutoProxyCreator;}/*** 开启aop注释支持*/@Beanpublic AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor=new AuthorizationAttributeSourceAdvisor();authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);return authorizationAttributeSourceAdvisor;}public RedisManager redisManager(){RedisManager redisManager=new RedisManager();redisManager.setHost(host);redisManager.setPort(port);/* redisManager.setPassword(password);*/redisManager.setTimeout(timeout);return redisManager;}public RedisCacheManager cacheManager(){RedisCacheManager cacheManager=new RedisCacheManager();cacheManager.setRedisManager(redisManager());//缓存名称cacheManager.setPrincipalIdFieldName("usrName");//缓存有效时间cacheManager.setExpire(1800);return cacheManager;}//会话操作public RedisSessionDAO redisSessionDAO(){RedisSessionDAO sessionDAO=new RedisSessionDAO();sessionDAO.setRedisManager(redisManager());return sessionDAO;}//会话管理public DefaultWebSessionManager sessionManager(){DefaultWebSessionManager sessionManager=new DefaultWebSessionManager();sessionManager.setSessionDAO(redisSessionDAO());return sessionManager;}@Beanpublic MyShiroRealm myShiroRealm(){//自定义RealmMyShiroRealm shiroRealm=new MyShiroRealm();//设置启用缓存,并设置缓存名称shiroRealm.setCachingEnabled(true);shiroRealm.setAuthorizationCachingEnabled(true);shiroRealm.setAuthenticationCacheName("authorizationCache");//设置凭证(密码)匹配器shiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());return shiroRealm;}@Beanpublic SecurityManager securityManager(){//安全管理器SecurityManagerDefaultWebSecurityManager securityManager=new DefaultWebSecurityManager();//注入RealmsecurityManager.setRealm(myShiroRealm());//注入缓存管理器securityManager.setCacheManager(cacheManager());//注入会话管理器securityManager.setSessionManager(sessionManager());return securityManager;}@Beanpublic ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager){//Shiro过滤器:权限认证ShiroFilterFactoryBean shiroFilterFactory=new ShiroFilterFactoryBean();//注入SecurityManagershiroFilterFactory.setSecurityManager(securityManager);//权限验证:使用Filter控制资源(URL)的访问shiroFilterFactory.setLoginUrl("/dologin");shiroFilterFactory.setSuccessUrl("/main");shiroFilterFactory.setUnauthorizedUrl("/403");//没有权限跳转403页面Map<String,String> filterChianDefinitionMap=new LinkedCaseInsensitiveMap<String>();//必须使用LinkedHashMap(有序集合)//配置可以匿名访问的资源(URL):静态资源filterChianDefinitionMap.put("/css/**","anon");filterChianDefinitionMap.put("/fonts/**","anon");filterChianDefinitionMap.put("/images/**","anon");filterChianDefinitionMap.put("/js/**","anon");filterChianDefinitionMap.put("/localcss/**","anon");filterChianDefinitionMap.put("/localjs/**","anon");filterChianDefinitionMap.put("/login","anon");filterChianDefinitionMap.put("/logout","logout");//注销过滤器,自动注销//配置需要特定权限才能访问的资源(URL)//静态权限:包括全部需要特定权限才能访问的资源(URL)/* filterChianDefinitionMap.put("/user/list","perms[用户列表]");filterChianDefinitionMap.put("/user/add","perms[用户添加]");filterChianDefinitionMap.put("/user/edit","perms[用户修改]");filterChianDefinitionMap.put("/user/del","perms[用户删除]");*///动态授权List<Right> rights=roleService.findAllRights();for(Right right:rights){if(right.getRightUrl()!=null&&!right.getRightUrl().trim().equals("")){filterChianDefinitionMap.put(right.getRightUrl(),"perms["+right.getRightCode()+"]");}}//配置认证访问,其他资源(URL)必须认证通过才能访问filterChianDefinitionMap.put("/**","authc");//必须放在过滤器链最后面shiroFilterFactory.setFilterChainDefinitionMap(filterChianDefinitionMap);return shiroFilterFactory;}
}