参考

SSM 框架讲解:http://www.notedeep.com/note/6644/page/12419

框架搭建:https://www.cnblogs.com/hcbin/p/5397977.html

https://zhuanlan.zhihu.com/p/158613585

过滤器

概述

  • 基于函数回调,依赖于 servlet 容器
  • 过滤器类实现 Filter 接口,需要在 web.xml 中配置过滤器
  • 在实现上,基于函数回调
  • 场景:业务无关的、宽泛的:例如字符编码(CharacterEncodingFilter)过滤低俗文字危险字符
  • url-pattern 中配置了 /* 之后,可以对所有要访问的资源进行拦截
  • Filter 的执行顺序在 Interceptor 之前
  • 过滤作用:拦截——数据分析——数据处理——放行/不放行
  • 重点在过滤

示例

字符集过滤器

web.xml 配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!-- 配置字符集过滤器 -->
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<!-- 配置项目的编码mapping -->
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

拦截器

概述

  • 依赖于 web 框架,在 SpringMVC 中就是依赖于 SpringMVC 框架
  • 拦截器类继承HandlerInterceptorAdapter类,需要在 spring-mvc.xml 中配置拦截器
  • 在实现上,基于 Java 的反射机制, 属于面向切面编程(AOP)的一种运用
  • 只能对 controller 请求进行拦截
  • 如果修改 request 信息,不会传递给后面
  • 场景:更细粒度的操作
  • 拦截作用:拦截——数据分析——放行/不放行
  • 重点在拦截

自定义拦截器

  1. 拦截器类 MyInterceptor.java

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    import org.apache.commons.lang3.StringUtils;
    import org.springframework.web.servlet.ModelAndView;
    import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.util.Map;

    public class MyInterceptor extends HandlerInterceptorAdapter {
    // 在请求处理的方法之前执行
    // 如果返回 true 执行下一个拦截器
    // 如果返回 false 就不执行下一个拦截器
    @Override
    public boolean preHandle (HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    System.out.println ("------------ 处理前 ------------");

    //放行
    return true;
    }

    // 在请求处理方法执行之后执行
    @Override
    public void postHandle (HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
    System.out.println ("------------ 处理后 ------------");
    }

    // 在 dispatcherServlet 处理后执行,做清理工作.
    @Override
    public void afterCompletion (HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
    System.out.println ("------------ 清理 ------------");
    }
    }
  2. spring-mvc.xml

    1
    2
    3
    4
    5
    6
    7
    8
     <mvc:interceptors>
    <mvc:interceptor>
    <mvc:mapping path="/*.jspx"/>
    <mvc:mapping path="/*.do"/>
    <!--bean 配置的就是拦截器 -->
    <bean class="com.bjtcrj.gms.common.MyInterceptor"/>
    </mvc:interceptor>
    </mvc:interceptors>

自定义过滤器

示例一:新增/修改请求参数「非 form-data

  1. ParameterFilter.java

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    import java.io.IOException;

    import javax.servlet.Filter;
    import javax.servlet.FilterChain;
    import javax.servlet.FilterConfig;
    import javax.servlet.ServletException;
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    import javax.servlet.http.HttpServletRequest;

    public class ParameterFilter implements Filter {

    @Override
    public void destroy() {
    // TODO Auto-generated method stub

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse reponse, FilterChain filterChain)
    throws IOException, ServletException {
    // TODO Auto-generated method stub
    Map<String, String[]> newParameter = new HashMap<String, String[]>(request.getParameterMap());
    newParameter.put("test", new String[]{"hello, world"});
    filterChain.doFilter(new ParameterRequestWrapper(request, newParameter), reponse);
    }

    @Override
    public void init(FilterConfig arg0) throws ServletException {
    // TODO Auto-generated method stub
    }
    }
  2. ParameterRequestWrapper.java

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    class ParameterRequestWrapper extends HttpServletRequestWrapper {

    private Map<String, String[]> params;

    public ParameterRequestWrapper(HttpServletRequest request, Map<String, String[]> newParams) {
    super(request);
    this.params = newParams;
    renewParameterMap(request);
    }

    @Override
    public String getParameter(String name) {
    String result = "";

    Object v = params.get(name);
    if (v == null) {
    result = null;
    } else if (v instanceof String[]) {
    String[] strArr = (String[]) v;
    if (strArr.length > 0) {
    result = strArr[0];
    } else {
    result = null;
    }
    } else if (v instanceof String) {
    result = (String) v;
    } else {
    result = v.toString();
    }

    return result;
    }

    @Override
    public Map<String, String[]> getParameterMap() {
    return params;
    }

    @Override
    public Enumeration<String> getParameterNames() {
    return new Vector<String>(params.keySet()).elements();
    }

    @Override
    public String[] getParameterValues(String name) {
    String[] result = null;

    Object v = params.get(name);
    if (v == null) {
    result = null;
    } else if (v instanceof String[]) {
    result = (String[]) v;
    } else if (v instanceof String) {
    result = new String[] { (String) v };
    } else {
    result = new String[] { v.toString() };
    }

    return result;
    }

    private void renewParameterMap(HttpServletRequest req) {

    String queryString = req.getQueryString();

    if (queryString != null && queryString.trim().length() > 0) {
    String[] params = queryString.split("&");

    for (int i = 0; i < params.length; i++) {
    int splitIndex = params[i].indexOf("=");
    if (splitIndex == -1) {
    continue;
    }

    String key = params[i].substring(0, splitIndex);

    if (!this.params.containsKey(key)) {
    if (splitIndex < params[i].length()) {
    String value = params[i].substring(splitIndex + 1);
    this.params.put(key, new String[] { value });
    }
    }
    }
    }
    }
    }
  3. web.xml 中配置过滤器

    1
    2
    3
    4
    5
    6
    7
    8
    9
    <filter>
    <filter-name>ParameterFilter</filter-name>
    <filter-class>com.bjtcrj.common.filter.ParameterFilter</filter-class>
    </filter>
    <filter-mapping>
    <filter-name>ParameterFilter</filter-name>
    <url-pattern>/*.jspx</url-pattern>
    <url-pattern>/*.do</url-pattern>
    </filter-mapping>

示例二:防止 XSS 攻击,过滤特殊字符「非 form-data

  1. XSSFilter.java

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    import java.io.IOException;

    import javax.servlet.Filter;
    import javax.servlet.FilterChain;
    import javax.servlet.FilterConfig;
    import javax.servlet.ServletException;
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    import javax.servlet.http.HttpServletRequest;

    public class XSSFilter implements Filter {

    @Override
    public void destroy() {
    // TODO Auto-generated method stub

    }

    @Override
    public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain arg2)
    throws IOException, ServletException {
    // TODO Auto-generated method stub
    arg2.doFilter(new XSSRequestWrapper((HttpServletRequest) arg0), arg1);
    }

    @Override
    public void init(FilterConfig arg0) throws ServletException {
    // TODO Auto-generated method stub

    }
    }
  2. XSSRequestWrapper.java

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    import java.util.regex.Pattern;

    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletRequestWrapper;


    public class XSSRequestWrapper extends HttpServletRequestWrapper {

    public XSSRequestWrapper(HttpServletRequest request) {
    super(request);
    }

    @Override
    public String[] getParameterValues(String parameter) {
    String[] values = super.getParameterValues(parameter);
    if (values == null) {
    return null;
    }
    int count = values.length;
    String[] encodedValues = new String[count];
    for (int i = 0; i < count; i++) {
    encodedValues[i] = stripXSS(values[i]);
    }
    return encodedValues;
    }

    @Override
    public String getParameter(String parameter) {
    String value = super.getParameter(parameter);
    return stripXSS(value);
    }

    @Override
    public String getHeader(String name) {
    String value = super.getHeader(name);
    //return stripXSS(value);
    return value;
    }

    public String getQueryString() {
    String value = super.getQueryString();
    if (value != null) {
    value = stripXSS(value);
    }
    return value;
    }

    private String stripXSS(String value) {
    if (value != null) {
    // Avoid anything between script tags
    Pattern scriptPattern = Pattern.compile("<script>(.*?)</script>", Pattern.CASE_INSENSITIVE);
    value = scriptPattern.matcher(value).replaceAll("");
    // Avoid anything in a
    // e­xpression
    scriptPattern = Pattern.compile("src[\r\n]*=[\r\n]*\\\'(.*?)\\\'",
    Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
    value = scriptPattern.matcher(value).replaceAll("");
    scriptPattern = Pattern.compile("src[\r\n]*=[\r\n]*\\\"(.*?)\\\"",
    Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
    value = scriptPattern.matcher(value).replaceAll("");
    // Remove any lonesome </script> tag
    scriptPattern = Pattern.compile("</script>", Pattern.CASE_INSENSITIVE);
    value = scriptPattern.matcher(value).replaceAll("");
    // Remove any lonesome <script ...> tag
    scriptPattern = Pattern.compile("<script(.*?)>",
    Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
    value = scriptPattern.matcher(value).replaceAll("");
    // Avoid eval(...) e­xpressions
    scriptPattern = Pattern.compile("eval\\((.*?)\\)",
    Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
    value = scriptPattern.matcher(value).replaceAll("");
    // Avoid e­xpression(...) e­xpressions
    scriptPattern = Pattern.compile("e­xpression\\((.*?)\\)",
    Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
    value = scriptPattern.matcher(value).replaceAll("");
    // Avoid javascript:... e­xpressions
    scriptPattern = Pattern.compile("javascript:", Pattern.CASE_INSENSITIVE);
    value = scriptPattern.matcher(value).replaceAll("");
    // Avoid vbscript:... e­xpressions
    scriptPattern = Pattern.compile("vbscript:", Pattern.CASE_INSENSITIVE);
    value = scriptPattern.matcher(value).replaceAll("");
    // Avoid οnlοad= e­xpressions
    scriptPattern = Pattern.compile("onload(.*?)=",
    Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
    value = scriptPattern.matcher(value).replaceAll("");

    String regEx = "[`~!@#$%^&*()+=|{}':;',\\[\\].<>/?~!@#¥%……&*()——+|{}【】‘;:”“’。,、?]";
    value = compile(regEx).matcher(value).replaceAll("").trim();
    }
    return value;
    }
    }
  3. web.xml 配置过滤器

    1
    2
    3
    4
    5
    6
    7
    8
    9
    <filter>
    <filter-name>XSSFilter</filter-name>
    <filter-class>com.bjtcrj.common.filter.XSSFilter</filter-class>
    </filter>
    <filter-mapping>
    <filter-name>XSSFilter</filter-name>
    <url-pattern>/*.jspx</url-pattern>
    <url-pattern>/*.do</url-pattern>
    </filter-mapping>

示例三:过滤 form-data 表单域中特殊字符

  1. 新建 CustomMultipartResolver类,实现 CommonsMultipartResolver

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    public class CustomMultipartResolver extends CommonsMultipartResolver {
    private HttpServletRequest request;

    @Override
    protected FileUpload newFileUpload(FileItemFactory fileItemFactory) {
    ServletFileUpload upload = new ServletFileUpload(fileItemFactory);
    upload.setSizeMax(-1);
    if (request != null) {
    HttpSession session = request.getSession();
    FileUploadProgressListener progressListener = new FileUploadProgressListener(session);
    upload.setProgressListener(progressListener);
    }
    return upload;
    }

    @Override
    public MultipartHttpServletRequest resolveMultipart(HttpServletRequest request) throws MultipartException {
    // 获取到request,要用到session
    this.request = request;
    return super.resolveMultipart(request);
    }

    @Override
    public MultipartParsingResult parseRequest(HttpServletRequest request) throws MultipartException {
    HttpSession session = request.getSession();
    String encoding = determineEncoding(request);
    FileUpload fileUpload = prepareFileUpload(encoding);
    FileUploadProgressListener progressListener = new FileUploadProgressListener(session);
    fileUpload.setProgressListener(progressListener);
    try {
    List<FileItem> fileItems = ((ServletFileUpload) fileUpload).parseRequest(request);
    return parseFileItems2(fileItems, encoding);
    } catch (FileUploadBase.SizeLimitExceededException ex) {
    throw new MaxUploadSizeExceededException(fileUpload.getSizeMax(), ex);
    } catch (FileUploadException ex) {
    throw new MultipartException("Could not parse multipart servlet request", ex);
    }
    }

    private MultipartParsingResult parseFileItems2(List<FileItem> fileItems, String encoding) {
    MultiValueMap<String, MultipartFile> multipartFiles = new LinkedMultiValueMap<String, MultipartFile>();
    Map<String, String[]> multipartParameters = new HashMap<String, String[]>();
    Map<String, String> multipartParameterContentTypes = new HashMap<String, String>();

    // Extract multipart files and multipart parameters.
    for (FileItem fileItem : fileItems) {
    if (fileItem.isFormField()) {
    String value;
    String partEncoding = determineEncoding(fileItem.getContentType(), encoding);
    if (partEncoding != null) {
    try {
    value = fileItem.getString(partEncoding);
    }
    catch (UnsupportedEncodingException ex) {
    if (logger.isWarnEnabled()) {
    logger.warn("Could not decode multipart item '" + fileItem.getFieldName() +
    "' with encoding '" + partEncoding + "': using platform default");
    }
    value = fileItem.getString();
    }
    }
    else {
    value = fileItem.getString();
    }

    //处理特殊字符
    value = StringEx.filterSpecialCharacters(value);
    String[] curParam = multipartParameters.get(fileItem.getFieldName());
    if (curParam == null) {
    // simple form field
    multipartParameters.put(fileItem.getFieldName(), new String[] {value});
    }
    else {
    // array of simple form fields
    String[] newParam = StringUtils.addStringToArray(curParam, value);
    multipartParameters.put(fileItem.getFieldName(), newParam);
    }
    multipartParameterContentTypes.put(fileItem.getFieldName(), fileItem.getContentType());
    }
    else {
    // multipart file field
    CommonsMultipartFile file = new CommonsMultipartFile(fileItem);
    multipartFiles.add(file.getName(), file);
    if (logger.isDebugEnabled()) {
    logger.debug("Found multipart file [" + file.getName() + "] of size " + file.getSize() +
    " bytes with original filename [" + file.getOriginalFilename() + "], stored " +
    file.getStorageDescription());
    }
    }
    }
    return new MultipartParsingResult(multipartFiles, multipartParameters, multipartParameterContentTypes);
    }

    private String determineEncoding(String contentTypeHeader, String defaultEncoding) {
    if (!StringUtils.hasText(contentTypeHeader)) {
    return defaultEncoding;
    }
    MediaType contentType = MediaType.parseMediaType(contentTypeHeader);
    Charset charset = contentType.getCharSet();
    return (charset != null ? charset.name() : defaultEncoding);
    }
    }
  2. spring-mvc.xml 配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <bean id="multipartResolver" class="com.bjtcrj.scm.common.controller.CustomMultipartResolver">
    <property name="defaultEncoding">
    <value>UTF-8</value>
    </property>
    <property name="maxUploadSize">
    <value>32505856</value><!-- 上传文件大小限制为31M,31*1024*1024 -->
    </property>
    <property name="maxInMemorySize">
    <value>4096</value>
    </property>
    </bean>

请求参数接收

GET请求

  1. 请求数据示例

    1
    GET http://localhost:8000/houseaddress/page?page=1&rows=10&fulltext=%E5%95%86%E5%9C%BA
  2. 接收

    1
    2
    3
    4
    @RequestMapping("/page")
    public Map<String, Object> page(@RequestParam("page") int page, @RequestParam("rows") int size, Houseaddress houseaddress) {

    }

POST 请求

根据 Content-Type区分

application/x-www-form-urlencoded

  1. 请求数据示例

    1
    2
    3
    4
    POST http://localhost:8080/ssm_maven_demo/account/add
    Content-Type: application/x-www-form-urlencoded

    name=张三&money=1000
  2. 接收

    1
    2
    3
    4
    @PostMapping("add")
    public Account add(Account account) {
    return this.accountService.insert(account);
    }

application/json

  1. 请求数据示例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    POST http://localhost:8080/ssm_maven_demo/account/add
    Content-Type: application/json

    {
    "name": "hello",
    "money": 190,
    "stu":
    {
    "name": "ddddd",
    "age": 222
    }
    }
  2. controller 接口处理

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    @PostMapping("add")
    public Account add(@RequestBody Account account) {
    return this.accountService.insert(account);
    }

    @Data
    public class Account implements Serializable {
    private static final long serialVersionUID = 618038893780400829L;

    private Integer id;

    private String name;

    private Double money;

    private Stu stu;
    }

    @Data
    class Stu {
    String name;
    Integer age;
    }

multipart/form-data

  1. 请求数据示例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    POST http://localhost:8080/ssm_maven_demo/account/addFile
    Content-Type: multipart/form-data; boundary=WebAppBoundary

    --WebAppBoundary
    Content-Disposition: form-data; name="name"
    Content-Type: text/plain

    zhagnsan
    --WebAppBoundary
    Content-Disposition: form-data; name="money"
    Content-Type: text/plain

    2999
    --WebAppBoundary
    Content-Disposition: form-data; name="file"; filename="README.md"
    < /Users/mac126/study/Study_Projects/ssm_maven_demo/README.md
    --WebAppBoundary--
  2. 接收

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    @PostMapping("addFile")
    public void addFile(@RequestParam MultipartFile file, Account account) {
    System.out.println(file.getOriginalFilename());
    System.out.println(account.toString());
    }

    @RequestMapping(value = "/addHouseHolderBasic_App", method = RequestMethod.POST)
    public void addHouseHolderBasic_App(HttpServletRequest request, HttpServletResponse response, PersonDto personDto) {
    // personDto 对象接收表单数据;文本对象通过 request 强制转换为 MultipartHttpServletRequest 类型,再从中获取
    }