问题描述:
在Java Web项目中,用户可访问Url一般只有一个,即index或login。而用户的其他Url请求都会引导到index页。如何来避免未登录用户直接访问Spring的Conroller和jsp文件?
解决方案:
一、阻止用户访问jsp。
Spring的MVC模式是不提倡直接通过URL形式访问.jsp页面的,建议通过Controller跳转至View页面。
把jsp文件放在WEB-INF目录下,js和css等资源文件放在WEB-INF的同级目录下,WEB-INF对用户不可见,可以起到隔离作用。
同时修改mvc配置文件,配置jsp的路径。
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
配置资源文件夹的路径,以便jsp能访问到所引用的静态资源文件。
<mvc:resources mapping="/**" location="/resources" />
二、Interceptor登录拦截
Spring拦截器介绍:
SpringMVC中使用Interceptor拦截器
拦截器HandlerInterceptor接口有三个回调方法
1.preHandle方法,顾名思义,该方法将在请求处理之前进行调用。SpringMVC 中的Interceptor 是链式的调用的,在一个应用中或者说是在一个请求中可以同时存在多个Interceptor 。每个Interceptor 的调用会依据它的声明顺序依次执行,而且最先执行的都是Interceptor 中的preHandle 方法,所以可以在这个方法中进行一些前置初始化操作或者是对当前请求的一个预处理,也可以在这个方法中进行一些判断来决定请求是否要继续进行下去。该方法的返回值是布尔值Boolean类型的,当它返回为false 时,表示请求结束,后续的Interceptor 和Controller 都不会再执行;当返回值为true 时就会继续调用下一个Interceptor 的preHandle 方法,如果已经是最后一个Interceptor 的时候就会是调用当前请求的Controller 方法。
2.postHandle方法,由preHandle 方法的解释我们知道这个方法包括后面要说到的afterCompletion 方法都只能是在当前所属的Interceptor 的preHandle 方法的返回值为true 时才能被调用。postHandle 方法,顾名思义就是在当前请求进行处理之后,也就是Controller 方法调用之后执行,但是它会在DispatcherServlet 进行视图返回渲染之前被调用,所以我们可以在这个方法中对Controller 处理之后的ModelAndView 对象进行操作。postHandle 方法被调用的方向跟preHandle 是相反的,也就是说先声明的Interceptor 的postHandle 方法反而会后执行。
3.afterCompletion方法,该方法也是需要当前对应的Interceptor 的preHandle 方法的返回值为true 时才会执行。顾名思义,该方法将在整个请求结束之后,也就是在DispatcherServlet 渲染了对应的视图之后执行。这个方法的主要作用是用于进行资源清理工作的。
package org.springframework.web.servlet;
public interface HandlerInterceptor {
boolean preHandle(
HttpServletRequest request, HttpServletResponse response,
Object handler)
throws Exception;
void postHandle(
HttpServletRequest request, HttpServletResponse response,
Object handler, ModelAndView modelAndView)
throws Exception;
void afterCompletion(
HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex)
throws Exception;
}
写自己的拦截器:
public class LoginInterceptor extends HandlerInterceptorAdapter {
private List<String> excludedUrls;
public void setExcludeUrls(List<String> excludeUrls) {
this.excludedUrls = excludeUrls;
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String requestUri = request.getRequestURI();
for (String url : excludedUrls) {
if (requestUri.endsWith(url)) {
return true;
}
}
HttpSession session = request.getSession();
if (session.getAttribute("login")==null) {
throw new WebAuthException();
} else {
return true;
}
}
}
HandlerInterceptorAdapter是适配器模式的接口,这里允许我们只实现一个方法即可。excludedUrls中声明了不需要拦截的URL,如果访问的URL以指定字符串结尾,preHandle将返回true,他们将不被拦截而直接访问到对应的Controller。声明方式可以在Spring配置文件中,如下
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/*"/>
<bean id="loginInterceptor" class="LoginInterceptor">
<property name="excludeUrls">
<list>
<value>/index</value>
<value>/</value>
</list>
</property>
</bean>
</mvc:interceptor>
</mvc:interceptors>
也就是说“http://xxxxxxxxxx/index”,“http://xxxxxxxxxx/login”,“http://xxxxxxxxxx/”是不会被拦截的。
如果URL不以指定字符串结尾,那么将判断session中是否已经有登录信息(用户是否已经登录),如果登录了,正常跳转到Controller。如果没有登录则抛出异常。该异常是自定义的:
public class WebAuthException extends Exception {
}
然后再配置文件中指定,抛出异常时,重定向到登录页。
<bean id="handlerExceptionResolver"
class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<prop key="com.thisone.apiserver.exception.WebAuthException">redirect:/index</prop>
</props>
</property>
</bean>
注意,如果没有redirect:,则直接跳转至名为index.jsp的页面。有redirect则会由Controller中@RequestMapping(value="/login")的方法来处理它。
@Controller
public class SystemController {
@RequestMapping(value = {"/index","/"},
method = RequestMethod.GET)
public String home() {
return "login"; //返回到login.jsp登录页,而不是Controller的方法。
}
}
@RequestMapping(value = "/login",
method = RequestMethod.POST)
public String login(HttpServletRequest request,
@RequestParam String loginname,
@RequestParam String password) throws Exception {
if (!loginname.equals("xxxx")) {
request.getSession().setAttribute("loginReply", "用户名错误, 请重新登录");
return "login";
}
if (!password.equals("xxxx")) {
request.getSession().setAttribute("loginReply", "密码错误, 请重新登录");
return "login";
}
request.getSession().setAttribute("login", "yes");
return "welcome";
}
这样,用户只能通过/index,/,三个URL后缀来正常访问到页面,其他都是会重定向到/index,再有Cntroller方法引导至login.jsp。
参考:
Spring3 MVC Login Interceptor(Spring 拦截器)