拦截器与过滤器(四)应用demo

一、日志记录

1、拦截器记录用户访问模块

例1:方法注解 

(1)注解定义:

package com.demo.interceptor;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
// 声明注解作用于方法
@Target(ElementType.METHOD)
// 声明注解运行时有效
@Retention(RetentionPolicy.RUNTIME)
public @interface LogModule {
    String moduleCode();
}

 (2)拦截器定义:

package com.demo.interceptor;

import com.demo.enums.LogModuleEnums;
import com.demo.service.LogModuleService;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;

@Component
public class LogModuleInterceptor implements HandlerInterceptor{

    private LogModuleService logModuleService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            Method method = handlerMethod.getMethod();
            //方法有无加注解
            LogModule checkVer = method.getAnnotation(LogModule.class);
            //1、没有加注解的接口直接放行
            if(	checkVer == null ){
                return true;
            }
            // 2、加了注解,获取模块编码并记录
            String moduleCode = checkVer.moduleCode();
            BeanFactory factory = WebApplicationContextUtils
                    .getRequiredWebApplicationContext(request.getServletContext());
            logModuleService = (LogModuleService) factory.getBean("logModuleService");
            logModuleService.insertLogModuleRecord(moduleCode);
        }
        return true;
    }
}

配置

package com.demo.config;

import com.demo.interceptor.LogModuleInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class MyConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LogModuleInterceptor());
    }
}

 (3)服务:

package com.demo.service.impl;

import com.demo.service.LogModuleService;
import org.springframework.stereotype.Service;

@Service("logModuleService")
public class LogModuleServiceImpl implements LogModuleService {
    @Override
    public void insertLogModuleRecord(String moduleCode) {
        System.out.println("访问模块"+moduleCode);
    }
}

(4)接口拦截注解:

package com.demo.controller;

import com.demo.dto.RoleDTO;
import com.demo.enums.LogModuleEnums;
import com.demo.interceptor.LogModule;
import com.demo.service.RoleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RequestMapping("/role")
@RestController
public class RoleController {

    @Autowired
    private RoleService roleService;

    @RequestMapping("/listAllRoles")
    @LogModule(moduleCode = "role")
    public List<RoleDTO> listAllRoles(){
        return roleService.listAllRoles();
    }

    @RequestMapping("/getRoleByRoleCode")
    @LogModule(moduleCode = "role")
    public RoleDTO getRoleByRoleCode(String roleCode){
        return roleService.getRoleByRoleCode(roleCode);
    }
}
package com.demo.controller;

import com.demo.dto.UserDTO;
import com.demo.interceptor.LogModule;
import com.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private UserService userService;

    @RequestMapping("/listAllUsers")
    @LogModule(moduleCode = "user")
    public List<UserDTO> listAllUsers(){
        return userService.listAllUsers();
    }

    @RequestMapping("/getUserByUserAccount")
    @LogModule(moduleCode = "user")
    public UserDTO getUserByUserAccount(String userAccount){
        return userService.getUserByUserAccount(userAccount);
    }
}

 依次访问接口,后台打印:

 

例2:同时作用于类和方法注解:

(1)定义注解:

package com.demo.interceptor;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

// 声明注解作用于类、方法
@Target({ElementType.TYPE,ElementType.METHOD})
// 声明注解运行时有效
@Retention(RetentionPolicy.RUNTIME)
public @interface LogModule {
    String moduleCode();
}

 (2)拦截器:

package com.demo.interceptor;

import com.demo.enums.LogModuleEnums;
import com.demo.service.LogModuleService;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;

@Component
public class LogModuleInterceptor implements HandlerInterceptor{

    private LogModuleService logModuleService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            Method method = handlerMethod.getMethod();
            //方法有无加注解
            LogModule annotation = method.getAnnotation(LogModule.class);
            //1、没有加注解,判断所在类有没有加注解
            if(	annotation == null ){
                System.out.println(method.getName()+"方法无注解");
                annotation = method.getDeclaringClass().getAnnotation(LogModule.class);
                if(annotation == null){
                    System.out.println(method.getName()+"所在类无注解");
                    return true;
                }
                System.out.println(method.getName()+"所在类有注解");
            }
            // 2、加了注解,获取模块编码并记录
            String moduleCode = annotation.moduleCode();
            BeanFactory factory = WebApplicationContextUtils
                    .getRequiredWebApplicationContext(request.getServletContext());
            logModuleService = (LogModuleService) factory.getBean("logModuleService");
            logModuleService.insertLogModuleRecord(moduleCode);
        }
        return true;
    }
}

(3)接口注解情况:

package com.demo.controller;

import com.demo.dto.UserDTO;
import com.demo.interceptor.LogModule;
import com.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private UserService userService;

    @RequestMapping("/listAllUsers")
    public List<UserDTO> listAllUsers(){
        return userService.listAllUsers();
    }

    @RequestMapping("/getUserByUserAccount")
    @LogModule(moduleCode = "user")
    public UserDTO getUserByUserAccount(String userAccount){
        return userService.getUserByUserAccount(userAccount);
    }
}
package com.demo.controller;

import com.demo.dto.RoleDTO;
import com.demo.enums.LogModuleEnums;
import com.demo.interceptor.LogModule;
import com.demo.service.RoleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RequestMapping("/role")
@RestController
@LogModule(moduleCode = "role")
public class RoleController {

    @Autowired
    private RoleService roleService;

    @RequestMapping("/listAllRoles")
    public List<RoleDTO> listAllRoles(){
        return roleService.listAllRoles();
    }

    @RequestMapping("/getRoleByRoleCode")
    public RoleDTO getRoleByRoleCode(String roleCode){
        return roleService.getRoleByRoleCode(roleCode);
    }
}

依次访问接口,后台打印:

例3:自定义注入枚举

同样同时作用于类和方法:

(1)定义枚举

package com.demo.enums;

public enum  LogModuleEnums {
    USER("user","用户"),
    ROLE("role","角色");

    public final String moduleCode;

    public final String moduleName;

    LogModuleEnums(String moduleCode,String moduleName){
        this.moduleCode = moduleCode;
        this.moduleName = moduleName;
    }
}

(2)定义注解:

package com.demo.interceptor;

import com.demo.enums.LogModuleEnums;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

// 声明注解作用于类、方法
@Target({ElementType.TYPE,ElementType.METHOD})
// 声明注解运行时有效
@Retention(RetentionPolicy.RUNTIME)
public @interface LogModule {
    LogModuleEnums module();
}

(3)拦截器修改:

LogModuleEnums module = annotation.module();
            BeanFactory factory = WebApplicationContextUtils
                    .getRequiredWebApplicationContext(request.getServletContext());
            logModuleService = (LogModuleService) factory.getBean("logModuleService");
            logModuleService.insertLogModuleRecord(module);

(4)接口拦截:

@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private UserService userService;

    @RequestMapping("/listAllUsers")
    public List<UserDTO> listAllUsers(){
        return userService.listAllUsers();
    }

    @RequestMapping("/getUserByUserAccount")
    @LogModule(module = LogModuleEnums.USER)
    public UserDTO getUserByUserAccount(String userAccount){
        return userService.getUserByUserAccount(userAccount);
    }
}
@RequestMapping("/role")
@RestController
@LogModule(module = LogModuleEnums.ROLE)
public class RoleController {

    @Autowired
    private RoleService roleService;

    @RequestMapping("/listAllRoles")
    public List<RoleDTO> listAllRoles(){
        return roleService.listAllRoles();
    }

    @RequestMapping("/getRoleByRoleCode")
    public RoleDTO getRoleByRoleCode(String roleCode){
        return roleService.getRoleByRoleCode(roleCode);
    }
}

(5)服务:

package com.demo.service.impl;

import com.demo.enums.LogModuleEnums;
import com.demo.service.LogModuleService;
import org.springframework.stereotype.Service;

@Service("logModuleService")
public class LogModuleServiceImpl implements LogModuleService {
    @Override
    public void insertLogModuleRecord(LogModuleEnums module) {
        System.out.println("访问模块"+module.moduleCode+" "+module.moduleName);
    }
}

依次访问接口,打印:

以上使用过滤器实现:

二、参数校验

1、拦截器对必须要传入token的接口做拦截

 (1)自定义注解:

package com.demo.interceptor.tokencheck;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

// 声明注解作用于类、方法
@Target({ElementType.TYPE,ElementType.METHOD})
// 声明注解运行时有效
@Retention(RetentionPolicy.RUNTIME)
public @interface TokenCheck {
}

(2)自定义拦截器:

package com.demo.interceptor.tokencheck;

import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;

@Component
public class TokenCheckInterceptor implements HandlerInterceptor{

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            Method method = handlerMethod.getMethod();
            //方法有无加注解
            TokenCheck annotation = method.getAnnotation(TokenCheck.class);
            //1、没有加注解,判断所在类有没有加注解
            if(	annotation == null ){
                annotation = method.getDeclaringClass().getAnnotation(TokenCheck.class);
                if(annotation == null){
                    return true;
                }
            }
            // 2、加了注解,校验请求头是否加了token
            String token = request.getHeader("token");
            if(StringUtils.isEmpty(token)){
                System.out.println("头部未传入token");
                return false;
            }
        }
        return true;
    }
}

(3)配置拦截器:

package com.demo.config;

import com.demo.interceptor.logmodule.LogModuleInterceptor;
import com.demo.interceptor.tokencheck.TokenCheckInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class MyConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new TokenCheckInterceptor());
        registry.addInterceptor(new LogModuleInterceptor());
    }
}

(4)接口加注解:

@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private UserService userService;

    @RequestMapping("/listAllUsers")
    @TokenCheck
    public List<UserDTO> listAllUsers(){
        return userService.listAllUsers();
    }

    @RequestMapping("/getUserByUserAccount")
    @LogModule(module = LogModuleEnums.USER)
    public UserDTO getUserByUserAccount(String userAccount){
        return userService.getUserByUserAccount(userAccount);
    }
}
@RequestMapping("/role")
@RestController
@LogModule(module = LogModuleEnums.ROLE)
@TokenCheck
public class RoleController {

    @Autowired
    private RoleService roleService;

    @RequestMapping("/listAllRoles")
    public List<RoleDTO> listAllRoles(){
        return roleService.listAllRoles();
    }

    @RequestMapping("/getRoleByRoleCode")
    public RoleDTO getRoleByRoleCode(String roleCode){
        return roleService.getRoleByRoleCode(roleCode);
    }
}

测试:

(1)访问http://localhost:7777/filterDemo/user/listAllUsers    不加token

 后台打印:头部未传入token

加上token:

 后台打印:token校验通过 

(2)访问http://localhost:7777/filterDemo/user/getUserByUserAccount?userAccount=zhangsan 

后台打印:无需校验token,不拦截

(3)访问/role下接口,情况同(1)。

2、参数校验

export function updateReport(report, emails, views) {
  const id = report.id
  const reportName = report.reportName
  const safeLevel = report.safeLevel
  const reportUrl = report.reportUrl
  const remark = report.remark
  const reportType = report.reportType
  return request({
 
    url: '/report/updateReport',
    method: 'post',
    data: {
      id,
      reportName,
      reportUrl,
      safeLevel,
      remark,
      reportType,
      emails,
      views
    }
  })
 
 
export function deleteReport(reportId) {
  return request({
    url: '/report/deleteReport',
    method: 'post',
    data: {
      reportId
    }
  })
}

 列表有查看、编辑、删除功能,前端判断该条数据的creator与当前登录用户是不是同一个人,如果是则展示编辑和删除按钮。为防止恶意攻击,可以在后端再加个拦截器,双重控制。

 
@Component
public class ReportHandler implements HandlerInterceptor{
	
	@Autowired
	private ReportService reportService;
 
	@Override
	public void afterCompletion(HttpServletRequest arg0,
			HttpServletResponse arg1, Object arg2, Exception arg3)
			throws Exception {
		// TODO Auto-generated method stub
		
	}
 
	@Override
	public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1,
			Object arg2, ModelAndView arg3) throws Exception {
		// TODO Auto-generated method stub
		
	}
 
	@Override
	public boolean preHandle(HttpServletRequest req, HttpServletResponse arg1,
			Object arg2) throws Exception {
		
		try{
			String requestURI = req.getRequestURI();
            //是删除或者编辑请求
			if(requestURI.indexOf("updateReport")!=-1 || requestURI.indexOf("deleteReport")!=-1){
				String reportId = null;
              //删除的参数为reportId
				if(req.getParameter("reportId") != null){
					reportId = req.getParameter("reportId");
				}
             //编辑的参数为id
				if(req.getParameter("id") != null){
					reportId = req.getParameter("id");
				}
				Report report = reportService.getById(reportId);
				User user = new SessionUtil().getCurrentUser();
				if(!report.getCreator().equals(user.getId())){
					return false;
				}
			}
			return true;
		}catch (Exception e) {
			return true;
		}
		
	}
 
}

三、鉴权

1、垂直越权

判断用户是否有访问功能接口的角色或权限,下面用自定义拦截的方式解决垂直越权问题。

(1)自定义拦截

从接口中获取两个参数,菜单类型(菜单or按钮)、接口对应的权限编码,菜单类型默认为菜单,因为一个接口可能在多个菜单页面都有调用,所以这里权限编码是数组

@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AuthVerify {
    /**
     * 操作类型,MenuTypeEnums
     */
    MenuTypeEnums menuType() default MenuTypeEnums.MENU_TYPE_MENU;

    /**
     * 权限编码,MenuCodeEnums
     */
    MenuCodeEnums[] authMenuCodes();
}

对应的枚举:

public enum MenuTypeEnums {

    MENU_TYPE_MENU(1,"菜单"),
    MENU_TYPE_ACTION(2,"按钮"),
    SYSTEM(3,"系统资源")
    ;
    public final Integer code;

    public final String message;

    MenuTypeEnums(int code, String message ) {
        this.code = code;
        this.message = message;
    }
}
public enum MenuCodeEnums {

    USER_MANAGE("web:userManage","用户管理"),
    USER_MANAGE_ADD("web:userManage:add","新增用户")
    ;
    public final String code;

    public final String name;

    MenuCodeEnums(String code, String name) {
        this.code = code;
        this.name = name;
    }
}

(2)业务接口注解:

@PostMapping(value = "/user/getPageList")
@AuthVerify(authMenuCodes = {MenuCodeEnums.USER_MANAGE,MenuCodeEnums.USER_MANAGE_ADD})
    public ResponseMessage getPageList(){
        //
    }
@GetMapping("/listPositionByName")
    @AuthVerify(menuType = MenuTypeEnums.SYSTEM,authMenuCodes = {})
    public ResponseMessage listPositionByName(@RequestParam(value = "positionName", required = false) String positionName){
       //
    }

(3)拦截判断:

@Component
public class AuthCheckInterceptor implements HandlerInterceptor {

    private static Logger logger = LoggerFactory.getLogger(HandlerInterceptor.class);

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        try {
            if (handler instanceof HandlerMethod) {
                HandlerMethod handlerMethod = (HandlerMethod) handler;
                Method method = handlerMethod.getMethod();
                //方法有无加注解
                AuthVerify annotation = method.getAnnotation(AuthVerify.class);
                if (annotation == null){
                    return true;
                }
                //有注解
                //1、资源类型是否是系统,是系统均有查看权限
                Integer authMenuType = annotation.menuType().code;
                if(MenuTypeEnums.SYSTEM.code.equals(authMenuType)){
                    logger.info("AuthCheckInterceptor.preHandle 接口资源类型无需校验,放行");
                    return true;
                }
                //2、资源类型是普通菜单/按钮,控制访问权限
                MenuCodeEnums[] authMenuCodeEnums = annotation.authMenuCodes();
                JwtUser jwtUser = (JwtUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
                String userName = jwtUser.getUsername();
                List<MenuVO> userMenus = jwtUser.getMenuVOList();
                List<String> userMenuCodes =  userMenus.stream().map(MenuVO::getMenuCode).collect(Collectors.toList());
                for(MenuCodeEnums menuCodeEnum : authMenuCodeEnums){
                    String authMenuCode = menuCodeEnum.menuCode;
                    if(userMenuCodes.contains(authMenuCode)){
                        return true;
                    }
                }
                //处理请求失败返回
                logger.warn("AuthCheckInterceptor.preHandle,userName={}无权限访问",userName);
                setErrorRes(response);
                return false;
            }
        } catch (Exception e) {
            logger.error("AuthCheckInterceptor.preHandle异常", e);
            return true;
        }
        return true;
    }

    private void setErrorRes(HttpServletResponse response) {
        try{
            ResponseMessage responseMessage = ResponseMessage.error("没有权限访问");
            response.setCharacterEncoding("UTF-8");
            response.setContentType("application/json;charset=utf-8");
            PrintWriter out = response.getWriter();
            out.write(JSON.toJSONString(responseMessage));
        }catch (Exception e){
            logger.error("AuthCheckInterceptor.preHandle设置自定义返回异常",e);
        }
    }
}

拦截通过则正常返回数据,拦截校验失败返回错误信息:

{"code":400,"message":"没有权限访问","status":"error","success":false}

(4)改进

因只有部分接口需要权限拦截校验,使用拦截器的话大部分接口都是走到无注解这步,可以采用AOP切面,只拦截注解的接口。

<!-- aop和aspect -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-aop</artifactId>
		</dependency>
@Aspect
@Component
public class AuthCheckAspect {
    private static Logger logger = LoggerFactory.getLogger(AuthCheckAspect.class);

    @Around("@annotation(com.demo.common.AuthVerify)")
    public Object doAuthFilter(ProceedingJoinPoint pjp) throws Throwable {
        try{
            Signature signature = pjp.getSignature();
            MethodSignature methodSignature = (MethodSignature) signature;
            Method targetMethod = methodSignature.getMethod();
            AuthVerify annotation = targetMethod.getAnnotation(AuthVerify.class);
            //非AuthVerify权限类注解,放开
            if (annotation == null) {
                return pjp.proceed();
            }
            //有注解
            //1、资源类型是否是系统,是系统均有查看权限
            Integer authMenuType = annotation.menuType().code;
            if(MenuTypeEnums.SYSTEM.code.equals(authMenuType)){
                logger.info("AuthCheckAspect.doAuthFilter 接口资源类型无需校验,放行");
                return pjp.proceed();
            }
            //2、资源类型是普通菜单/按钮,控制访问权限
            MenuCodeEnums[] authMenuCodeEnums = annotation.authMenuCodes();
            JwtUser jwtUser = (JwtUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
            String userName = jwtUser.getUsername();
            List<MenuVO> userMenus = jwtUser.getMenuVOList();
            List<String> userMenuCodes =  userMenus.stream().map(MenuVO::getMenuCode).collect(Collectors.toList());
            for(MenuCodeEnums menuCodeEnum : authMenuCodeEnums){
                String authMenuCode = menuCodeEnum.menuCode;
                if(userMenuCodes.contains(authMenuCode)){
                    return pjp.proceed();
                }
            }
            //请求失败
            logger.warn("AuthCheckAspect.doAuthFilter,userName={}无权限访问",userName);
            return ResponseMessage.error("没有权限访问");
        }catch (Exception e){
            return pjp.proceed();
        }
    }
}

四、参数获取

1、获取@RequestBody封装的参数

 controller接口接收参数,@RequestBody注解的与@RequestParam(缺省)注解的,下面来看下这两种方式对于拦截器和过滤器的差别:

1.1、不带@RequestBody注解的情况:

   

(1)pom:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>myDemo</artifactId>
    <version>1.0-SNAPSHOT</version>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.4.1.RELEASE</version>
    </parent>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>

</project>

(2)dto:

package com.demo.dto;

public class User {

    private Integer id;
    private String userName;
    private Integer age;
    //省略set、get方法

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", userName='" + userName + '\'' +
                ", age=" + age +
                '}';
    }
}

(3)controller:

package com.demo.controller;

import com.demo.dto.User;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/user")
public class UserController {

    @RequestMapping("/test")
    public void  test(User user){
        System.out.println("接口:"+user);
    }
}

(4)拦截器:

package com.demo.intercepter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
@Component
public class ActionHandle implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
        try {

            String requestUrl = httpServletRequest.getRequestURI();
            Map<String, String[]> originRequestMap = httpServletRequest.getParameterMap();
            Map<String,String> requestMap = new HashMap<String,String>();
            for (String key : originRequestMap.keySet()) {
                String[] values = originRequestMap.get(key);
                requestMap.put(key,values[0]);
            }
            System.out.println("拦截器:"+requestMap);
        } catch (Exception e) {
        }
        return true;

    }

    @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 {

    }
}
package com.demo.intercepter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

@SpringBootConfiguration
public class MySpringMVCConfig extends WebMvcConfigurerAdapter {

    @Autowired
    private ActionHandle myInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(myInterceptor).addPathPatterns("/**");
    }
}

 (5)过滤器:

package com.demo.filter;
import org.springframework.stereotype.Component;

import javax.servlet.*;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

@Component
public class MyFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        Map<String, String[]> originRequestMap = request.getParameterMap();
        Map<String,String> requestMap = new HashMap<String,String>();
        for (String key : originRequestMap.keySet()) {
            String[] values = originRequestMap.get(key);
            requestMap.put(key,values[0]);
        }
        System.out.println("过滤器:"+requestMap);
        chain.doFilter(request,response);
    }

    @Override
    public void destroy() {

    }
}

测试:访问:localhost:9999/test/user/test?id=1&userName=张三&age=6,后台打印:

1.2、@RequestBody注解

在controller中加一个接口

@RequestMapping("/test1")
    public void  test1( @RequestBody User user){
        System.out.println("接口:"+user);
    }

  

 后台打印:

 

 原因分析:普通的参数可以从request的getParameterMap中获取,而@RequestBody的参数需要从request的InputStream中获取。但是InputStream只能读取一次,如果过滤器读取了参数,后面拦截器和controler层就读取不到参数了,所以这类参数需要单独获取,可以把request封装一下,copy一份requet,一个用于在拦截器(过滤器)中读取参数,一个放行给controller使用。

解决方法:改造下代码:

       

(1)pom加入以下依赖:

 <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>commons-beanutils</groupId>
            <artifactId>commons-beanutils</artifactId>
            <version>1.9.3</version>
        </dependency>
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.5.6</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.4</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.31</version>
        </dependency>

(2)新增两个util:这是关键,复制input流

package com.demo.util;

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
import java.net.URLDecoder;
import java.util.*;

public class BodyReaderHttpServletRequestWrapper extends HttpServletRequestWrapper {
    private Map<String, String[]> paramsMap;

    @Override
    public Map getParameterMap() {
        return paramsMap;
    }

    @Override
    public String getParameter(String name) {
        String[] values = paramsMap.get(name);
        if (values == null || values.length == 0) {
            return null;
        }
        return values[0];
    }

    @Override
    public String[] getParameterValues(String name) {
        return paramsMap.get(name);
    }

    @Override
    public Enumeration getParameterNames() {
        return Collections.enumeration(paramsMap.keySet());
    }

    private String getRequestBody(InputStream stream) {
        String line = "";
        StringBuilder body = new StringBuilder();
        int counter = 0;

        // 读取POST提交的数据内容
        BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
        try {
            while ((line = reader.readLine()) != null) {
                if (counter > 0) {
                    body.append("rn");
                }
                body.append(line);
                counter++;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        return body.toString();
    }

    private HashMap<String, String[]> getParamMapFromPost(HttpServletRequest request) {

        String body = "";
        try {
            body = getRequestBody(request.getInputStream());
        } catch (IOException e) {
            e.printStackTrace();
        }
        HashMap<String, String[]> result = new HashMap<String, String[]>();

        if (null == body || 0 == body.length()) {
            return result;
        }

        return parseQueryString(body);
    }

    // 自定义解码函数
    private String decodeValue(String value) {
        if (value.contains("%u")) {
            return Encodes.urlDecode(value);
        } else {
            try {
                return URLDecoder.decode(value, "UTF-8");
            } catch (UnsupportedEncodingException e) {
                // 非UTF-8编码
                return "";
            }
        }
    }

    public HashMap<String, String[]> parseQueryString(String s) {
        String valArray[] = null;
        if (s == null) {
            throw new IllegalArgumentException();
        }
        HashMap<String, String[]> ht = new HashMap<String, String[]>();
        StringTokenizer st = new StringTokenizer(s, "&");
        while (st.hasMoreTokens()) {
            String pair = (String) st.nextToken();
            int pos = pair.indexOf('=');
            if (pos == -1) {
                continue;
            }
            String key = pair.substring(0, pos);
            String val = pair.substring(pos + 1, pair.length());
            if (ht.containsKey(key)) {
                String oldVals[] = (String[]) ht.get(key);
                valArray = new String[oldVals.length + 1];
                for (int i = 0; i < oldVals.length; i++) {
                    valArray[i] = oldVals[i];
                }
                valArray[oldVals.length] = decodeValue(val);
            } else {
                valArray = new String[1];
                valArray[0] = decodeValue(val);
            }
            ht.put(key, valArray);
        }
        return ht;
    }

    private Map<String, String[]> getParamMapFromGet(HttpServletRequest request) {
        return parseQueryString(request.getQueryString());
    }

    public String getBody(){
        return new String(body);
    }

    // 报文
    private final byte[] body;

    public BodyReaderHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        body = readBytes(request.getInputStream());

        //
        /*if ("POST".equals(request.getMethod().toUpperCase())) {
            paramsMap = getParamMapFromPost(this);
        } else {
            paramsMap = getParamMapFromGet(this);
        }*/

    }

    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(getInputStream()));
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        final ByteArrayInputStream bais = new ByteArrayInputStream(body);
        return new ServletInputStream() {

            @Override
            public int read() throws IOException {
                return bais.read();
            }

            @Override
            public boolean isFinished() {
                return false;
            }

            @Override
            public boolean isReady() {
                return false;
            }

            @Override
            public void setReadListener(ReadListener arg0) {

            }
        };
    }

    private static byte[] readBytes(InputStream in) throws IOException {
        BufferedInputStream bufin = new BufferedInputStream(in);
        int buffSize = 1024;
        ByteArrayOutputStream out = new ByteArrayOutputStream(buffSize);

        byte[] temp = new byte[buffSize];
        int size = 0;
        while ((size = bufin.read(temp)) != -1) {
            out.write(temp, 0, size);
        }
        bufin.close();

        byte[] content = out.toByteArray();
        return content;
    }

}
package com.demo.util;


import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.lang3.StringEscapeUtils;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;

public class Encodes {

    private static final String DEFAULT_URL_ENCODING = "UTF-8";
    private static final char[] BASE62 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".toCharArray();

    /**
     * Hex编码.
     */
    public static String encodeHex(byte[] input) {
        return Hex.encodeHexString(input);
    }

    /**
     * Hex解码.
     */
    public static byte[] decodeHex(String input) {
        try {
            return Hex.decodeHex(input.toCharArray());
        } catch (DecoderException e) {
            return null;
        }
    }

    /**
     * Base64编码.
     */
    public static String encodeBase64(byte[] input) {
        return Base64.encodeBase64String(input);
    }

    /**
     * Base64编码, URL安全(将Base64中的URL非法字符'+'和'/'转为'-'和'_', 见RFC3548).
     */
    public static String encodeUrlSafeBase64(byte[] input) {
        return Base64.encodeBase64URLSafeString(input);
    }

    /**
     * Base64解码.
     */
    public static byte[] decodeBase64(String input) {
        return Base64.decodeBase64(input);
    }

    /**
     * Base62编码。
     */
    public static String encodeBase62(byte[] input) {
        char[] chars = new char[input.length];
        for (int i = 0; i < input.length; i++) {
            chars[i] = BASE62[(input[i] & 0xFF) % BASE62.length];
        }
        return new String(chars);
    }
    /**
     * Html 转码.
     */
    public static String escapeHtml(String html) {
        return StringEscapeUtils.escapeHtml4(html);
    }
    /**
     * Html 解码.
     */
    public static String unescapeHtml(String htmlEscaped) {
        return StringEscapeUtils.unescapeHtml4(htmlEscaped);
    }
    /**
     * Xml 转码.
     */
    public static String escapeXml(String xml) {
        return StringEscapeUtils.escapeXml(xml);
    }
    /**
     * Xml 解码.
     */
    public static String unescapeXml(String xmlEscaped) {
        return StringEscapeUtils.unescapeXml(xmlEscaped);
    }
    /**
     * URL 编码, Encode默认为UTF-8.
     */
    public static String urlEncode(String part) {
        try {
            return URLEncoder.encode(part, DEFAULT_URL_ENCODING);
        } catch (UnsupportedEncodingException e) {
            //throw Exceptions.unchecked(e);
            return null;
        }
    }
    /**
     * URL 解码, Encode默认为UTF-8.
     */
    public static String urlDecode(String part) {
        try {
            return URLDecoder.decode(part, DEFAULT_URL_ENCODING);
        } catch (UnsupportedEncodingException e) {
            //throw Exceptions.unchecked(e);
            return null;
        }
    }
}

(3)改造拦截器的获取参数部分:

package com.demo.intercepter;
import com.alibaba.fastjson.JSON;
import com.demo.util.BodyReaderHttpServletRequestWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
@Component
public class ActionHandle implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
        try {

            String requestUrl = httpServletRequest.getRequestURI();
            ServletRequest requestWrapper = null;
            String param = "";
            String method = httpServletRequest.getMethod().toUpperCase();
            String type = httpServletRequest.getContentType();
            if ("POST".equals(method)
                    && "application/json".equalsIgnoreCase(type)) {
                requestWrapper = new BodyReaderHttpServletRequestWrapper(httpServletRequest);
            }

            if (requestWrapper == null) {
                Map<String, String[]> originRequestMap = httpServletRequest.getParameterMap();
                Map<String,String> requestMap = new HashMap<String,String>();
                for (String key : originRequestMap.keySet()) {
                    String[] values = originRequestMap.get(key);
                    requestMap.put(key,values[0]);
                }
                param = JSON.toJSONString(requestMap);
            } else {
                param  = ((BodyReaderHttpServletRequestWrapper) requestWrapper).getBody();
            }
            System.out.println("拦截器:"+param);
        } catch (Exception e) {
        }
        return true;

    }

    @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 {

    }
}

(4)改造过滤器的获取参数部分:

package com.demo.filter;
import com.alibaba.fastjson.JSON;
import com.demo.util.BodyReaderHttpServletRequestWrapper;
import org.springframework.stereotype.Component;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

@Component
public class MyFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

        ServletRequest requestWrapper = null;
        String param = "";
        if (request instanceof HttpServletRequest) {
            HttpServletRequest httpServletRequest = (HttpServletRequest) request;
            String method = httpServletRequest.getMethod().toUpperCase();
            String type = httpServletRequest.getContentType();
            if ("POST".equals(method)
                    && "application/json".equalsIgnoreCase(type)) {
                requestWrapper = new BodyReaderHttpServletRequestWrapper(
                        (HttpServletRequest) request);
            }
        }

        if (requestWrapper == null) {
            Map<String, String[]> originRequestMap = request.getParameterMap();
            Map<String,String> requestMap = new HashMap<String,String>();
            for (String key : originRequestMap.keySet()) {
                String[] values = originRequestMap.get(key);
                requestMap.put(key,values[0]);
            }
            param = JSON.toJSONString(requestMap);
        } else {
            param  = ((BodyReaderHttpServletRequestWrapper) requestWrapper).getBody();
        }
        System.out.println("过滤器:"+param);
        //放行
        if (requestWrapper == null) {
            chain.doFilter(request, response);
        }else{
            chain.doFilter(requestWrapper, response);
        }
    }

    @Override
    public void destroy() {

    }
}

测试:

1)带@RequestBody注解的请求:

    

     

2)不带@RequestBody注解的请求:

     

     

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

w_t_y_y

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值