发现 一直调用 sessiondao 的 doReadSession 方法
doReadSession
doCreate
update
doReadSession
update
doReadSession
doReadSession
update
常常还出现这个异常
doReadSession
doCreate
update
doReadSession
update
doReadSession
doReadSession
update
常常还出现这个异常
Caused by: org.apache.shiro.session.UnknownSessionException: There is no session with id [517a249d-f921-43c4-8c07-c9c6e4cfba73]
解决方式:
这个是shiro 设计上的问题, 主要是因为shiro 框架获取 session里面的属性时,每次都去拿取session,一次请求中会有很多次 获取 session 里面的属性。
我的解决思路是首先获取Spring启动后获取可请求的地址,然后放到缓存中,Shiro框架是基于过滤器实现的,我在进入Shiro的过滤器前先判定当前请求是否合法,如果合法的话,进入下一个过滤器,不合法的话返回提示,然后也不去Redis中进行查询。
以下基于Spring Boot框架
1.创建一个监听获取Spring启动后可访问的地址
package com.abroad.common.listener;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import com.abroad.common.cache.LocalCache;
/**
* 当容器启动时获取当前容器的可请求地址,并把它放到缓存中
*
* @ClassName: RequestURLListener
* @Description: TODO()
* @author: mengxr
* @date 2017年3月29日 下午6:25:36
*/
public class RequestUrlListener implements
ApplicationListener<ContextRefreshedEvent> {
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
if (event.getApplicationContext().getParent() == null) {// root application context 没有parent,他就是老大.
try {
ApplicationContext applicationContext = event
.getApplicationContext();
RequestMappingHandlerMapping bean = applicationContext
.getBean(RequestMappingHandlerMapping.class);
Set<String> result = new HashSet<String>();
Map<RequestMappingInfo, HandlerMethod> handlerMethods = bean
.getHandlerMethods();
for (RequestMappingInfo rmi : handlerMethods.keySet()) {
PatternsRequestCondition pc = rmi.getPatternsCondition();
Set<String> pSet = pc.getPatterns();
result.addAll(pSet);
}
LocalCache.ACCESS_URL.addAll(result);
logger.info("-----------初始化RequestUrlListener成功 ");
} catch (Exception e) {
logger.error("-----------获取RequestUrlListener失败 ",e);
}
}
}
}
然后在SpringBoot启动时注册监听
package com.abroad;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import com.abroad.common.listener.RequestUrlListener;
/**
* @ClassName: SecurityApplication
* @Description: TODO(权限管理框架)
* @author: mengxr
* @date 2017年3月23日 上午10:52:13
*/
@SpringBootApplication
public class SecurityApplication {
public static void main(String[] args) {
SpringApplication springApplication = new SpringApplication(SecurityApplication.class);
springApplication.addListeners(new RequestUrlListener()); //添加一个启动后监听
springApplication.run(args);
}
}
2.获取到可访问的URL地址后创建一个过滤器Filter
package com.abroad.common.filter;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.abroad.common.cache.LocalCache;
import com.abroad.common.comn.ResponseJson;
import com.abroad.common.comn.StatusCode;
import com.abroad.common.comn.web.ServletTools;
/**
* @ClassName: AccessFilter
* @Description: TODO(访问过滤器)
* @author: mengxr
* @date 2017年3月29日 下午6:08:54
*/
public class AccessFilter implements Filter {
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
String url = ServletTools.getContextPath((HttpServletRequest)request);
boolean flag = LocalCache.ACCESS_URL.contains(url);
if(flag){ //如果还有的话继续走下一个过滤器
chain.doFilter(request, response);
}else{
//没有找到相应的资源
ServletTools.sendResponse((HttpServletResponse) response, new ResponseJson(StatusCode.NOT_FOUND, false));
}
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
logger.info("访问过滤器AccessFilter注册成功----");
}
@Override
public void destroy() {
logger.info("访问过滤器AccessFilter摧毁成功-----");
}
}
SpringBoot注册过滤器
package com.abroad.common.config;
import org.springframework.boot.context.embedded.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import com.abroad.common.filter.AccessFilter;
/**
*
* @ClassName: AccessConfigue
* @Description: TODO(访问控制)
* @author: mengxr
* @date 2017年3月29日 下午5:58:39
*/
@Configuration
public class AccessConfigue {
/**
* 由于shrio框架在每次请求的时候都要向缓存查询当前的Session效率十分低,并且shrio框架是基于过滤器实现的,
* 所以在进入shrioFilter前先对访问的数据进行过滤,如果不存在的换直接跳转到404状态
* @Title: accessFilterRegistration
* @Description: TODO(这里用一句话描述这个方法的作用)
* @param @return 设定文件
* @return FilterRegistrationBean 返回类型
* @author: mengxr
* @date 2017年3月29日 下午6:03:17
* @throws
*/
@Bean
@Order(Integer.MAX_VALUE) //拦截器优先级最高
public FilterRegistrationBean accessFilterRegistration() {
FilterRegistrationBean registration = new FilterRegistrationBean();
AccessFilter accessFilter = new AccessFilter();
registration.setFilter(accessFilter);
registration.addUrlPatterns("/*");
registration.addInitParameter("paramName", "paramValue");
registration.setName("accessFilter");
return registration;
}
}
PS: 此处 @Order是拦截器的优先级 要优于Shrio的拦截器先
3.启动服务 发现AcceeFilter要由于shiroFilter先执行
4.测试结果 再次访问URL后发现已不再进行Redis查询