2018.8.2 java電商從1到2--chapter10 SpringMVC攔截器實現許可權統一驗證
阿新 • • 發佈:2019-02-18
目錄
第10章 SpringMVC攔截器實現許可權統一驗證
10.1 總覽
- 一期程式碼關於許可權判定的演進
- SpringMVC攔截流程圖
- 攔截器配置與使用
- HttpServletResponse的重置
- 登入不攔截
- 程式碼重構
10.2 一期程式碼回顧
一期程式碼中涉及許可權校驗的部分:
- 是否登入?
- 是否是管理員?
如下所示。這部分程式碼大量重複,可以抽取到攔截器的邏輯中。
@RequestMapping("add_category.do") @ResponseBody public ServerResponse addCategory(HttpSession session,String categoryName,@RequestParam(value = "parentId",defaultValue = "0") int parentId){ User user = (User)session.getAttribute(Const.CURRENT_USER); if(user == null){ return ServerResponse.createByErrorCodeMessage(ResponseCode.NEED_LOGIN.getCode(),"使用者未登入,請登入"); } //校驗一下是否是管理員 if(iUserService.checkAdminRole(user).isSuccess()){ //是管理員 //增加我們處理分類的邏輯 return iCategoryService.addCategory(categoryName,parentId); }else{ return ServerResponse.createByErrorMessage("無許可權操作,需要管理員許可權"); } }
10.3 攔截器的配置
注意,這裡的path格式代表攔截的範圍不同。
因為登入不需要攔截,因為它確實需要進行登入邏輯,否則會陷入死迴圈(想要登入,卻被攔截提示請登入)。
可以在配置檔案中去掉對登入的攔截,也可以在程式碼裡去掉對登入的攔截,這裡採用後者,效果一樣。
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd"> <!-- 其餘略 --> <mvc:interceptors> <!-- 定義在這裡的,所有的都會攔截--> <mvc:interceptor> <!--manage/a.do /manage/*--> <!--manage/b.do /manage/*--> <!--manage/product/save.do /manage/**--> <!--manage/order/detail.do /manage/**--> <mvc:mapping path="/manage/**"/> <!--<mvc:exclude-mapping path="/manage/user/login.do"/>--> <bean class="com.mmall.controller.common.interceptor.AuthorityInterceptor" /> </mvc:interceptor> </mvc:interceptors> </beans>
10.4 攔截器的使用
package com.mmall.controller.common.interceptor; import com.google.common.collect.Maps; import com.mmall.common.Const; import com.mmall.common.ServerResponse; import com.mmall.pojo.User; import com.mmall.util.CookieUtil; import com.mmall.util.JsonUtil; import com.mmall.util.RedisShardedPoolUtil; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang.StringUtils; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.PrintWriter; import java.util.Arrays; import java.util.Iterator; import java.util.Map; /** * Created by geely */ @Slf4j public class AuthorityInterceptor implements HandlerInterceptor{ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { log.info("preHandle"); //請求中Controller中的方法名 HandlerMethod handlerMethod = (HandlerMethod)handler; //解析HandlerMethod String methodName = handlerMethod.getMethod().getName(); String className = handlerMethod.getBean().getClass().getSimpleName(); //解析引數,具體的引數key以及value是什麼,我們列印日誌 StringBuffer requestParamBuffer = new StringBuffer(); Map paramMap = request.getParameterMap(); Iterator it = paramMap.entrySet().iterator(); while (it.hasNext()){ Map.Entry entry = (Map.Entry)it.next(); String mapKey = (String)entry.getKey(); String mapValue = StringUtils.EMPTY; //request這個引數的map,裡面的value返回的是一個String[] Object obj = entry.getValue(); if(obj instanceof String[]){ String[] strs = (String[])obj; mapValue = Arrays.toString(strs); } requestParamBuffer.append(mapKey).append("=").append(mapValue); } if(StringUtils.equals(className,"UserManageController") && StringUtils.equals(methodName,"login")){ log.info("許可權攔截器攔截到請求,className:{},methodName:{}",className,methodName); //如果是攔截到登入請求,不列印引數,因為引數裡面有密碼,全部會列印到日誌中,防止日誌洩露 return true; } log.info("許可權攔截器攔截到請求,className:{},methodName:{},param:{}",className,methodName,requestParamBuffer.toString()); User user = null; String loginToken = CookieUtil.readLoginToken(request); if(StringUtils.isNotEmpty(loginToken)){ String userJsonStr = RedisShardedPoolUtil.get(loginToken); user = JsonUtil.string2Obj(userJsonStr,User.class); } if(user == null || (user.getRole().intValue() != Const.Role.ROLE_ADMIN)){ //返回false.即不會呼叫controller裡的方法 response.reset();//geelynote 這裡要新增reset,否則報異常 getWriter() has already been called for this response. response.setCharacterEncoding("UTF-8");//geelynote 這裡要設定編碼,否則會亂碼 response.setContentType("application/json;charset=UTF-8");//geelynote 這裡要設定返回值的型別,因為全部是json介面。 PrintWriter out = response.getWriter(); //上傳由於富文字的控制元件要求,要特殊處理返回值,這裡面區分是否登入以及是否有許可權 if(user == null){ if(StringUtils.equals(className,"ProductManageController") && StringUtils.equals(methodName,"richtextImgUpload")){ Map resultMap = Maps.newHashMap(); resultMap.put("success",false); resultMap.put("msg","請登入管理員"); out.print(JsonUtil.obj2String(resultMap)); }else{ out.print(JsonUtil.obj2String(ServerResponse.createByErrorMessage("攔截器攔截,使用者未登入"))); } }else{ if(StringUtils.equals(className,"ProductManageController") && StringUtils.equals(methodName,"richtextImgUpload")){ Map resultMap = Maps.newHashMap(); resultMap.put("success",false); resultMap.put("msg","無許可權操作"); out.print(JsonUtil.obj2String(resultMap)); }else{ out.print(JsonUtil.obj2String(ServerResponse.createByErrorMessage("攔截器攔截,使用者無許可權操作"))); } } out.flush(); out.close();//geelynote 這裡要關閉 return false; } return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { log.info("postHandle"); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { log.info("afterCompletion"); } }
10.5 重構程式碼
@RequestMapping("add_category.do")
@ResponseBody
public ServerResponse addCategory(HttpServletRequest httpServletRequest, String categoryName, @RequestParam(value = "parentId",defaultValue = "0") int parentId){
// String loginToken = CookieUtil.readLoginToken(httpServletRequest);
// if(StringUtils.isEmpty(loginToken)){
// return ServerResponse.createByErrorMessage("使用者未登入,無法獲取當前使用者的資訊");
// }
// String userJsonStr = RedisShardedPoolUtil.get(loginToken);
// User user = JsonUtil.string2Obj(userJsonStr,User.class);
//
// if(user == null){
// return ServerResponse.createByErrorCodeMessage(ResponseCode.NEED_LOGIN.getCode(),"使用者未登入,請登入");
// }
// //校驗一下是否是管理員
// if(iUserService.checkAdminRole(user).isSuccess()){
// //是管理員
// //增加我們處理分類的邏輯
// return iCategoryService.addCategory(categoryName,parentId);
//
// }else{
// return ServerResponse.createByErrorMessage("無許可權操作,需要管理員許可權");
// }
//全部通過攔截器驗證是否登入以及許可權
return iCategoryService.addCategory(categoryName,parentId);
}