SpringMVC的攔截器實現(xiàn)權(quán)限的驗證
攔截器攔截請求,然后再判斷相關(guān)信息,是否請求可以繼續(xù)下去
Springmvc如何實現(xiàn)攔截器功能:
只需要自定義一個類, 去實現(xiàn)springmvc提供的一個接口,HandlerInterceptor
/**
* 自定義攔截器
* Author menglanyingfei
* Created on 2018.01.25 9:41
*/
public class InterceptorDemo1 implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
/**
* 在執(zhí)行Handler方法前調(diào)用該方法, 真正實現(xiàn)攔截的方法
* return true: 表示將該請求放行
* return false: 表示不讓請求繼續(xù)往下執(zhí)行
*/
System.out.println("InterceptorDemo1 攔截請求! preHandle");
return true;
}
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
/**
* 在進(jìn)入Handler以后, 返回ModelAndView之前執(zhí)行
*/
System.out.println("InterceptorDemo1 postHandle ");
}
/**
* 該方法是在Handler執(zhí)行完畢以后再調(diào)用該方法
* @param httpServletRequest
* @param httpServletResponse
* @param o
* @param e
* @throws Exception
*/
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
System.out.println("InterceptorDemo1 afterCompletion");
}
}
然后只要在springmvc.xml文件中配置一個攔截器,配置具體攔截的請求url
<!-- 配置攔截器 -->
<mvc:interceptors>
<mvc:interceptor>
<!-- path: 指定將要攔截的路徑
/**: 表示攔截所有請求
class: 自定義攔截器的全限定名 -->
<mvc:mapping path="/**"/>
<bean class="com.wtu.ssm.interceptor.InterceptorDemo1"/>
</mvc:interceptor>
<mvc:interceptor>
<!-- path: 指定將要攔截的路徑
/**: 表示攔截所有請求
class: 自定義攔截器的全限定名 -->
<mvc:mapping path="/**"/>
<bean class="com.wtu.ssm.interceptor.InterceptorDemo2"/>
</mvc:interceptor>
<mvc:interceptor>
<!-- path: 指定將要攔截的路徑
/**: 表示攔截所有請求
class: 自定義攔截器的全限定名 -->
<mvc:mapping path="/**"/>
<bean class="com.wtu.ssm.interceptor.LoginInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
攔截器鏈的執(zhí)行順序:
Interceptor1 ----> interceptor2 ----> intercptor3 ...
-
如果兩個攔截器都放行:
攔截的方法是根據(jù)配置文件的配置順序進(jìn)行攔截。其他方法相反
image.png
2.如果Interceptor1放行, interceptor2不放行
要執(zhí)行Controller的方法, 一定要所有的攔截器都放行才能執(zhí)行
3.如果兩個Interceptor都返回false
示例演示: 實現(xiàn)用戶登錄權(quán)限的校驗
- 攔截器攔截所有的請求
- 判斷session中是否存在用戶信息,如果存在則放行,如果不存在,接續(xù)判斷請求路徑是否是登錄頁面
- 如果請求路徑是登錄頁面,那么就放行,如果不是, 則重定向到登錄頁面進(jìn)行登錄
定義登錄的Controller
@Controller
public class UserController {
@RequestMapping("/login.do")
public String login(HttpSession session, String username, String password) {
if (username != null && !"".equals(username)) {
// 將用戶信息保存到session中
session.setAttribute("username", username);
return "redirect:/findItemsByName.do";
} else {
return "forward:/jsp/login.jsp";
}
}
@RequestMapping("/exit.do")
public String exit(HttpSession session) {
// 清除session
session.invalidate();
return "redirect:/jsp/login.jsp";
}
}
定義攔截器
public class LoginInterceptor implements HandlerInterceptor {
// 攔截所有請求
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o) throws Exception {
// 判斷session中是否存在用戶信息
HttpSession session = request.getSession();
// 獲取session中的用戶信息
String username = (String) session.getAttribute("username");
if (username != null) {
return true; // 放行
}
// 獲取請求路徑
String path = request.getRequestURI();
if (path.contains("/login.do")) {
return true;
}
// 重定向到登錄頁面
// request.getRequestDispatcher("/jsp/login.jsp").forward(request, response);
response.sendRedirect(request.getContextPath() + "/jsp/login.jsp");
return false;
}
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
}
}
配置攔截器
<mvc:interceptors>
<mvc:interceptor>
<!-- path: 指定將要攔截的路徑
/**: 表示攔截所有請求
class: 自定義攔截器的全限定名 -->
<mvc:mapping path="/**"/>
<bean class="com.wtu.ssm.interceptor.LoginInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
SpringMVC參數(shù)校驗
使用Hibernate-validator對controller綁定的POJO形參進(jìn)行校驗。
-
導(dǎo)入校驗所需要的jar包
image.png 在xml文件中配置校驗器
<!-- 配置參數(shù)校驗器 -->
<bean id="validator"
class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
<!-- 校驗器-->
<property name="providerClass" value="org.hibernate.validator.HibernateValidator" />
<!-- 指定校驗使用的資源文件,如果不指定則默認(rèn)使用classpath下的ValidationMessages.properties -->
<property name="validationMessageSource" ref="messageSource" />
</bean>
<!-- 校驗錯誤信息配置文件 -->
<bean id="messageSource"
class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<!-- 資源文件名-->
<property name="basenames">
<list>
<value>classpath:CustomValidationMessages</value>
</list>
</property>
<!-- 資源文件編碼格式 -->
<property name="fileEncodings" value="utf-8" />
<!-- 對資源文件內(nèi)容緩存時間,單位秒 -->
<property name="cacheSeconds" value="120" />
</bean>
3.引入校驗器:
<!-- 配置注解形式的適配器和映射器
conversion-service: 配置自定義參數(shù)綁定器
validator: 引入一個校驗器
-->
<mvc:annotation-driven conversion-service="conversionService"
validator="validator"/>
錯誤信息屬性文件:CustomValidationMessages.properties
#配置錯誤提示信息
name.length.error=商品名稱過長或者過短!!!
date.is.null=date can't be null
4.在pojo屬性中添加參數(shù)的校驗
public class Items {
private Integer id;
@Size(min = 2, max = 15, message = "{name.length.error}")
private String name;
private Double price;
private String detail;
private String pic;
@NotNull(message = "{date.is.null}")
private Date createtime;
// ...
}
- Controller方法中的代碼
// 修改信息
@RequestMapping(value = "/updateItems.do")
public String updateItems(Model model,
@Validated Items items, BindingResult bindingResult) throws Exception {
if (bindingResult.hasErrors()) {
// 獲取錯誤的信息
List<ObjectError> errorList = bindingResult.getAllErrors();
// 將錯誤信息保存到request域中
// errorList.get(0).getDefaultMessage();
model.addAttribute("errorList", errorList);
// 轉(zhuǎn)發(fā)到商品修改頁面
return "forward:/jsp/editItem.jsp";
}
itemsService.updateItems(items);
return "redirect:/findItemsByName.do";
}
如果添加了參數(shù)校驗, 那么必須在pojo參數(shù)前面加上
@Validated
注解,然后在參數(shù)后面加上BindingResult
參數(shù), 用來獲取錯誤的信息
數(shù)據(jù)的回寫
比如在商品修改頁面 提交以后 如果出現(xiàn)錯誤 那么重新回到商品修改頁面的時候,商品的信息還在 這就是數(shù)據(jù)的回寫
默認(rèn)情況下,如果出現(xiàn)異常,那么會自動將pojo的類型名 并且首字母小寫作為request域的鍵,然后將pojo的對象作為值, 然后傳遞到頁面。
如果在頁面接收值的時候使用的變量名不是pojo的類型首字母小寫,那么在POJO參數(shù)的前面加上@ModelAttribute("items1") 明確指定 request域中的鍵為items1,那么這個時候就可以在頁面上 通過items1來獲取值
// 修改信息
@RequestMapping(value = "/updateItems.do")
public String updateItems(Model model,
@Validated @ModelAttribute("items1") Items items, BindingResult bindingResult ) throws Exception {
如果上面的方式不能理解,那么可以手動回寫: 加上如下代碼即可
如果是簡單類型的參數(shù)回寫:那么只能通過
model.addAttribute("key", value);
Springmvc的全局異常處理
- 自定義一個異常類
/**
* 自定義異常類
* Author menglanyingfei
* Created on 2018.01.25 16:37
*/
public class MyException extends Exception {
// 保存錯誤信息
private String message;
public MyException(String message) {
this.message = message;
}
@Override
public String getMessage() {
return message;
}
}
- 定義一個類實現(xiàn)
HandlerExceptionResolver
接口
/**
* 這個異常解析器會在遇到異常的時候
* 會被處理器適配器來調(diào)用
* Author menglanyingfei
* Created on 2018.01.25 16:40
*/
public class MyExceptionResolver implements HandlerExceptionResolver {
/*
判斷異常的信息: 如果是我們自定義的異常就直接獲取異常信息
然后跳轉(zhuǎn)到錯誤頁面:
如果不是自定義異常, 那么給出一個未知錯誤的提示
*/
@Override
public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
ModelAndView mv = new ModelAndView();
MyException me = null;
// 判斷捕獲到的異常是否為自定義異常
if (e instanceof MyException) {
// 將異常強轉(zhuǎn)為MyException類型
me = (MyException) e;
// 獲取異常信息
String errorMessage = me.getMessage();
mv.addObject("errorMessage", errorMessage);
} else {
mv.addObject("errorMessage", "未知錯誤, 請于管理員聯(lián)系!");
}
// 這里配置了視圖解析器
mv.setViewName("/error");
return mv;
}
}
3.在xml文件中配置異常解析器:
<!-- 配置異常解析器 -->
<bean class="com.wtu.ssm.exception.MyExceptionResolver"/>
錯誤頁面error.jsp:
<%--
User: menglanyingfei
Date: 2018/1/25
Time: 16:50
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>錯誤提示</title>
</head>
<body>
${errorMessage}
</body>
</html>
關(guān)于項目中異常處理注意的問題:
- 如果使用springmvc Mapper的異常拋給service ,service的異常拋給controller
- 開發(fā)階段不需要將異常處理掉
- 在controller也是將異常給拋出去,那么會由處理器適配器捕獲異常調(diào)用我們自己編寫的異常解析器進(jìn)行異常的處理。
完整代碼地址
https://github.com/menglanyingfei/SSMLearning/tree/master/jar%E5%8C%85
https://github.com/menglanyingfei/SSMLearning/tree/master/SSM