1. 问题提出的根源:
处理404 notfound错误。
2. 一般做法:
在web.xml里配置error-page.
<error-page> <error-code>404</error-code> <location>/WEB-INF/views/error/404.jsp</location> </error-page> |
3. 特殊场景
项目当中使用shiro安全框架,配置了非ServletContainer的session管理器。
如果自定义了sessionManager,即session不由web容器来管理,而是我们自己处理,比如讲session放到couchbase上面或者其他分布式缓存上面,
<bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager"> |
此时,如果想同时使用< error-page>来处理404错误,就会造成session的重新建立,表现为已登录用户,访问一个不存在的url,得到一个返回404页面,然后再访问正确的url地址又需要重新登录。
4. 原因分析
shiro的DefaultWebSessionManager把session自己管理起来了,不放到web容器里,那么当发生404时,web容器在处理过程里发现容器里没有session,那么就会建立一个新的sessionid,然后把sessionid绑定到请求上。于是shiro框架就认为是一个新的session来了,但是没有登录,所以自动跳转到login页面。
5. 失败的解决方案
不用web容器的error-page处理。改用spring mvc框架自身处理。配置异常处理器:
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"> <property name="exceptionMappings"> <props> <prop key="org.apache.shiro.authz.UnauthorizedException">error/403</prop> <prop key="NoSuchRequestHandlingMethodException">error/404</prop> <prop key="java.lang.Throwable">error/500</prop> </props> </property> </bean> |
如上面标黄色的设置项,实际是不起作用的。因为正常的404找不到地址错误是不会抛出任何异常的。
6. 成功的方案
不用web容器的error-page处理。在spring mvc里配置一个能匹配所有请求的处理器,但是要保证这个默认的处理器排在url映射匹配排序的最后。
在spring-mvc.xml里添加如下的bean
<bean id="defaultController" class="com.ikang.base.web.DefaultController" /> <bean id="defaultUrlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> <property name="order" value="2147483647" /><!-- 此处的order很重要,要低于 mvc:resources的默认值2147483646--> <property name="mappings"> <props> <prop key="/*/**">defaultController</prop> </props> </property> </bean> |
DefaultController.java类
public class DefaultController implements Controller { @Override public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception { return new ModelAndView("error/404"); } } |
注意:此controller一定不要用annotation方式,必须如上实现Controller接口。因为如果用@Controller/@RequestMapping注解方式的话,就不能保证顺序排在匹配映射顺序的最后了。