1. 程式人生 > >2018.8.2 java電商從1到2--chapter10 SpringMVC攔截器實現許可權統一驗證

2018.8.2 java電商從1到2--chapter10 SpringMVC攔截器實現許可權統一驗證

目錄

第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);
    }