简介:redis用户缓存用户的session,EhCache用于缓存用户的权限。
问题:即使有EhCache缓存了用户的权限,但是每次访问需要权限的方法时,依然会调用授权方法进行授权。
ShiroConfig.java
import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import com.nongqitong.web.redis.RedisSessionDao;
import com.nongqitong.web.shiro.ShiroFormAuthenticationFilter;
import com.nongqitong.web.shiro.realm.LoginRealm;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.session.mgt.ExecutorServiceSessionValidationScheduler;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.authc.LogoutFilter;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.MethodInvokingFactoryBean;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.DelegatingFilterProxy;
import javax.servlet.DispatcherType;
import javax.servlet.Filter;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class ShiroConfig {
@Bean
public ShiroDialect shiroDialect() {
return new ShiroDialect();
}
@Bean
public FilterRegistrationBean filterRegistrationBean() {
FilterRegistrationBean filterRegistration = new FilterRegistrationBean();
DelegatingFilterProxy delegatingFilterProxy = new DelegatingFilterProxy("shiroFilterFactoryBean");
delegatingFilterProxy.setTargetFilterLifecycle(true);
filterRegistration.setEnabled(true);
filterRegistration.setFilter(delegatingFilterProxy); // 该值缺省为false,表示生命周期由SpringApplicationContext管理,设置为true则表示由ServletContainer管理 filterRegistration.addInitParameter("targetFilterLifecycle", "true");
filterRegistration.addUrlPatterns("/*");
filterRegistration.setDispatcherTypes(DispatcherType.REQUEST);
return filterRegistration;
}
@Bean(name = "shiroFilterFactoryBean")
public ShiroFilterFactoryBean shiroFilterFactoryBean(
@Qualifier("securityManager") DefaultWebSecurityManager securityManager) {
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
bean.setSecurityManager(securityManager);
bean.setLoginUrl("/");
Map<String, Filter> filters = new HashMap<>();
LogoutFilter logoutFilter = new LogoutFilter();
logoutFilter.setRedirectUrl("/");
ShiroFormAuthenticationFilter shiroFormAuthenticationFilter = new ShiroFormAuthenticationFilter();
filters.put("logoutFilter", logoutFilter);
filters.put("authc", shiroFormAuthenticationFilter);
bean.setFilters(filters);
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
filterChainDefinitionMap.put("/", "anon");
filterChainDefinitionMap.put("/google", "anon");
filterChainDefinitionMap.put("/libs/**", "anon");
// filterChainDefinitionMap.put("/images/**", "anon");
// filterChainDefinitionMap.put("/jquery/**", "anon");
// filterChainDefinitionMap.put("/test/**", "anon");
filterChainDefinitionMap.put("/user/login", "anon");
filterChainDefinitionMap.put("/logout", "logout");
filterChainDefinitionMap.put("/**", "authc");
bean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return bean;
}
@Bean(name = "securityManager")
public DefaultWebSecurityManager securityManager(@Qualifier("authRealm") LoginRealm authRealm,
@Qualifier("cacheManager") EhCacheManager cacheManager,
@Qualifier("sessionManager") ShiroSessionManager sessionManager) {
DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
manager.setRealm(authRealm);
manager.setCacheManager(cacheManager);
manager.setSessionManager(sessionManager);
return manager;
}
@Bean(name = "authRealm")
public LoginRealm loginRealm() {
LoginRealm realm = new LoginRealm();
realm.setCachingEnabled(true);
return realm;
}
@Bean(name = "cacheManager")
public EhCacheManager cacheManager() {
EhCacheManager manager = new EhCacheManager();
manager.setCacheManagerConfigFile("classpath:ehcache.xml");
return manager;
}
@Bean(name = "sessionManager")
public ShiroSessionManager sessionManager(@Qualifier("sessionDAO") RedisSessionDao sessionDAO, @Qualifier("sessionIdCookie") SimpleCookie sessionIdCookie) {
ShiroSessionManager manager = new ShiroSessionManager();
manager.setGlobalSessionTimeout(1800000);
manager.setDeleteInvalidSessions(true);
manager.setSessionValidationSchedulerEnabled(true);
// manager.setSessionValidationScheduler(sessionValidationScheduler);
manager.setSessionDAO(sessionDAO);
manager.setSessionIdCookieEnabled(true);
manager.setSessionIdCookie(sessionIdCookie);
manager.setSessionIdUrlRewritingEnabled(false);
return manager;
}
@Bean(name = "sessionValidationScheduler")
public ExecutorServiceSessionValidationScheduler sessionValidationScheduler(
@Qualifier("sessionManager") ShiroSessionManager sessionManager) {
ExecutorServiceSessionValidationScheduler scheduler = new ExecutorServiceSessionValidationScheduler();
scheduler.setInterval(1800000);
scheduler.setSessionManager(sessionManager);
return scheduler;
}
// @Bean(name = "sessionDAO")
// public EnterpriseCacheSessionDAO sessionDAO() {
// EnterpriseCacheSessionDAO sessionDAO = new EnterpriseCacheSessionDAO();
// sessionDAO.setSessionIdGenerator(new JavaUuidSessionIdGenerator());
// return sessionDAO;
// }
@Bean(name = "sessionDAO")
public RedisSessionDao getRedisSessionDao() {
return new RedisSessionDao();
}
@Bean(name = "sessionIdCookie")
public SimpleCookie sessionIdCookie() {
SimpleCookie cookie = new SimpleCookie("ryzh.session.id");
cookie.setHttpOnly(true);
cookie.setMaxAge(-1);
return cookie;
}
@Bean
public MethodInvokingFactoryBean methodInvokingFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager) {
MethodInvokingFactoryBean methodInvokingFactoryBean = new MethodInvokingFactoryBean();
methodInvokingFactoryBean.setStaticMethod("org.apache.shiro.SecurityUtils.setSecurityManager");
methodInvokingFactoryBean.setArguments(securityManager);
return methodInvokingFactoryBean;
}
// @Bean("lifecycleBeanPostProcessor")
// public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
// return new LifecycleBeanPostProcessor();
// }
@Bean
// @DependsOn("lifecycleBeanPostProcessor")
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator shiroAutoProxyCreator =
new DefaultAdvisorAutoProxyCreator();
shiroAutoProxyCreator.setProxyTargetClass(true);
return shiroAutoProxyCreator;
}
/**
* 开启shiro aop注解支持.
* 使用代理方式;所以需要开启代码支持;否则@RequiresRoles等注解无法生效
* @param securityManager
* @return
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("securityManager") DefaultWebSecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
}
ehcache.xml
<?xml version="1.0" encoding="UTF-8"?>
<ehcache name="shirocache">
<diskStore path="java.io.tmpdir"/>
<!-- 登录记录缓存 锁定10分钟 -->
<cache name="passwordRetryCache" eternal="false"
timeToIdleSeconds="3600" timeToLiveSeconds="0" overflowToDisk="false"
statistics="true">
</cache>
<cache name="authorizationCache" eternal="true"
timeToIdleSeconds="3600" timeToLiveSeconds="0" overflowToDisk="false"
statistics="true">
</cache>
<cache name="authenticationCache" eternal="true"
timeToIdleSeconds="3600" timeToLiveSeconds="0" overflowToDisk="false"
statistics="true">
</cache>
<cache name="shiro-activeSessionCache" eternal="false"
timeToIdleSeconds="3600" timeToLiveSeconds="0" overflowToDisk="false"
statistics="true">
</cache>
<cache name="shiro-kickout-session" eternal="false"
timeToIdleSeconds="3600" timeToLiveSeconds="0" overflowToDisk="false"
statistics="true">
</cache>
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
overflowToDisk="false"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"
/>
<!--maxElementsInMemory="10000"-->
<!--external 设置为true后就不会失效,未测试logout,但切换账号后再切换回来,就会需要重新授权-->
<!--eternal="false"-->
<!--缓存空闲时间,只有空闲到一定时长则失效,下次需要重新授权-->
<!--timeToIdleSeconds="120"-->
<!--缓存Live时间,超时立刻失效-->
<!--timeToLiveSeconds="120"-->
<!--overflowToDisk="false"-->
<!--diskPersistent="false"-->
<!--diskExpiryThreadIntervalSeconds="120"-->
</ehcache>
RedisSessionDao.java
import org.apache.shiro.session.Session;
import org.apache.shiro.session.UnknownSessionException;
import org.apache.shiro.session.mgt.eis.AbstractSessionDAO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.io.Serializable;
import java.util.Collection;
import java.util.concurrent.TimeUnit;
@Service
public class RedisSessionDao extends AbstractSessionDAO {
// Session超时时间,单位为毫秒
private long expireTime = 1800000;
@Autowired
private RedisTemplate redisTemplate;// Redis操作类,对这个使用不熟悉的,可以参考前面的博客
public RedisSessionDao() {
super();
}
public RedisSessionDao(long expireTime, RedisTemplate redisTemplate) {
super();
this.expireTime = expireTime;
this.redisTemplate = redisTemplate;
}
@Override // 更新session
public void update(Session session) throws UnknownSessionException {
System.out.println("===============update================");
if (session == null || session.getId() == null) {
return;
}
session.setTimeout(expireTime);
redisTemplate.opsForValue().set(session.getId(), session, expireTime, TimeUnit.MILLISECONDS);
}
@Override // 删除session
public void delete(Session session) {
System.out.println("===============delete================");
if (null == session) {
return;
}
redisTemplate.opsForValue().getOperations().delete(session.getId());
}
@Override
// 获取活跃的session,可以用来统计在线人数,如果要实现这个功能,可以在将session加入redis时指定一个session前缀,统计的时候则使用keys("session-prefix*")的方式来模糊查找redis中所有的session集合
public Collection<Session> getActiveSessions() {
System.out.println("==============getActiveSessions=================");
return redisTemplate.keys("*");
}
@Override// 加入session
protected Serializable doCreate(Session session) {
System.out.println("===============doCreate================");
Serializable sessionId = this.generateSessionId(session);
this.assignSessionId(session, sessionId);
redisTemplate.opsForValue().set(session.getId(), session, expireTime, TimeUnit.MILLISECONDS);
return sessionId;
}
@Override// 读取session
protected Session doReadSession(Serializable sessionId) {
System.out.println("==============doReadSession================= "+sessionId);
if (sessionId == null) {
return null;
}
return (Session) redisTemplate.opsForValue().get(sessionId);
}
public long getExpireTime() {
return expireTime;
}
public void setExpireTime(long expireTime) {
this.expireTime = expireTime;
}
public RedisTemplate getRedisTemplate() {
return redisTemplate;
}
public void setRedisTemplate(RedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
}
}