Spring boot 过滤返回对象属性遇到的各种坑

Spring boot 过滤返回对象属性遇到的各种坑

在很多的应用场景中,我们从后台查询出来的对象数据并不想把所有的字段返回到前台,特别是一些敏感的字段,如密码,解决这种问题有一下几种方式

  1. 可以查询数据库的时候不要查询出来,这里我不想讨论这个这种情况了
  2. 还有一种情况就是在实体中,如果某个字段不要显示,则在其get方法前加上注解@JsonIgnore
    以上两种都不灵活,如果有的地方要显示这个字段,有的地方不要显示这个字段,这样就不好办了
    那能不能通过自定义注解的方式,在要返回页面前对返回的实体字段进行过滤呢?这当然是可以的了
    首先定义注解,如下所示
package com.sf.ams.json;

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

/**
 * 描述:
 * 
 * <pre>HISTORY
 * ****************************************************************************
 *  ID   DATE           PERSON          REASON
 *  1    2017年10月20日      Simba.Hua         Create
 * ****************************************************************************
 * </pre>
 * @author Simba.Hua
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface JsonFieldFilter {
    Class<?> type();//对哪个类的属性进行过滤
    String include() default  "";//包含哪些字段,即哪些字段可以显示
    String exclude() default "";//不包含哪些字段,即哪些字段不可以显示
}

在spring boot中用把Object转换成json用的是jackson,那么jackson是怎么过滤Object的字段的呢,是通过ObjectMapper,例子如下

package com.sf.ams.json;

import com.fasterxml.jackson.annotation.JsonFilter;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter;
import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;

/**
 * 描述:
 * 
 * <pre>HISTORY
 * ****************************************************************************
 *  ID   DATE           PERSON          REASON
 *  1    2017年10月20日      Simba.Hua         Create
 * ****************************************************************************
 * </pre>
 * @author Simba.Hua
 */
public class JsonFilterSerializer {
    private static final String DYNC_INCLUDE = "DYNC_INCLUDE";//包含的标识
    private static final String DYNC_EXCLUDE = "DYNC_EXCLUDE";//过滤的标识
    private ObjectMapper mapper = new ObjectMapper();
    @JsonFilter(DYNC_EXCLUDE)
    interface DynamicExclude{

    }
    @JsonFilter(DYNC_INCLUDE)
    interface DynamicInclude{

    }
    public void filter(Class<?> clazz , String include , String exclude) {
        if (clazz == null) return;
        if (include != null && include.length() > 0) {//包含的操作
            mapper.setFilterProvider(new SimpleFilterProvider()
            .addFilter(DYNC_INCLUDE, SimpleBeanPropertyFilter.filterOutAllExcept(include.split(","))));//多个字段用,分割开
            mapper.addMixIn(clazz, DynamicInclude.class);
        } else if (exclude != null && exclude.length() > 0) {
            mapper.setFilterProvider(new SimpleFilterProvider()
            .addFilter(DYNC_EXCLUDE, SimpleBeanPropertyFilter.serializeAllExcept(exclude.split(","))));
            mapper.addMixIn(clazz, DynamicExclude.class);
        }
    }
    public String toJson(Object object) throws JsonProcessingException{
        return mapper.writeValueAsString(object);
    }
    @Test
    public void testFilterJson() throws JsonProcessingException{
        JsonFilterSerializer jsonFilter = new JsonFilterSerializer();
        jsonFilter.filter(Response.class, null, "status");//Response中有status、failReason、currentIndex三个属性
        System.out.println(jsonFilter.toJson(new Response("fail","失败了",-1)));
    }
}

测试结果如下,status字段没有显示,测试成功
这里写图片描述

在springMVC中返回到页面前可以通过实现HandlerMethodReturnValueHandler接口,在方法handleReturnValue中可以通过MethodParameter类扫描是否含有@JsonFieldFilter 注解,如果有则过滤返回对象指定的字段,代码如下

package com.sf.ams.json;

import javax.servlet.http.HttpServletResponse;

import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.method.support.ModelAndViewContainer;

/**
 * 描述:对加了JsonFieldFilter注解的方法进行字段过滤处理
 * 
 * <pre>HISTORY
 * ****************************************************************************
 *  ID   DATE           PERSON          REASON
 *  1    2017年10月20日      Simba.Hua         Create
 * ****************************************************************************
 * </pre>
 * @author Simba.Hua
 */
public class JsonReturnHandler implements HandlerMethodReturnValueHandler{

    @Override
    public void handleReturnValue(Object returnObject, MethodParameter paramter,
            ModelAndViewContainer container, NativeWebRequest request) throws Exception {
        container.setRequestHandled(true);
        JsonFilterSerializer serializer = new JsonFilterSerializer();
        if(paramter.hasMethodAnnotation(JsonFieldFilter.class)) {//如果有JsonFieldFilter注解,则过滤返回的对象returnObject
            JsonFieldFilter jsonFilter = paramter.getMethodAnnotation(JsonFieldFilter.class);
            serializer.filter(jsonFilter.type() == null ?returnObject.getClass() : jsonFilter.type(), jsonFilter.include(), jsonFilter.exclude());//调用过滤方法
        }
        HttpServletResponse response = request.getNativeResponse(HttpServletResponse.class);
        response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
        response.getWriter().write(serializer.toJson(returnObject));
    }

    @Override
    public boolean supportsReturnType(MethodParameter methodParameter) {
        return methodParameter.hasMethodAnnotation(JsonFieldFilter.class);
    }

}

到这似乎只要写一个Controller加上注解就可以了,如下

package com.sf.ams.controller;

import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

import com.sf.ams.biz.IDataStoreXmlAnalysisBiz;
import com.sf.ams.entity.SystemInfo;
import com.sf.ams.entity.TreeNode;
import com.sf.ams.json.JsonFieldFilter;
import com.sf.ams.utils.TreeNodeUtil;
/**
 * 描述:
 * 
 * <pre>HISTORY
 * ****************************************************************************
 *  ID   DATE           PERSON          REASON
 *  1    2017-09-26      Simba.Hua         Create
 * ****************************************************************************
 * </pre>
 * @author Simba.Hua
 */
@Controller
@RequestMapping("/dataStoreAnalysis")
public class DataStoreAnalysisController {
    @Autowired
    private IDataStoreXmlAnalysisBiz dataStoreXmlAnalysisBiz;
    @RequestMapping("/getSystemDetail")
    @ResponseBody
    @JsonFieldFilter(type = SystemInfo.class,exclude = "gitPassword")//把gitPassword字段过滤掉
    public SystemInfo getSystemDetail(String systemCode){
        return dataStoreXmlAnalysisBiz.getSystemBySystemCode(systemCode);
    }
}

然而发现这样写并没有过滤掉字段gitPassword,debug发现代码根本没有进入类JsonReturnHandler的 handleReturnValue方法,问题出在哪?是不是没有告诉spring boot有JsonReturnHandler这个类,而我发现,如果在springmvc中只要在配置文件中加入以下配置就可以了,但spring boot只能通过注解。

<mvc:annotation-driven >
        <mvc:return-value-handlers>
            <bean class="com.sf.ams.json.JsonReturnHandler"/>
        </mvc:return-value-handlers>
 </mvc:annotation-driven>

后来发现在spring boot中需要加入配置告诉spring boot有自定义的HandlerMethodReturnValueHandler,代码如下

package com.sf.springboot;

import java.util.List;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.ViewResolverRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

import com.sf.ams.json.JsonReturnHandler;

/**
 * 描述:
 * 
 * <pre>HISTORY
 * ****************************************************************************
 *  ID   DATE           PERSON          REASON
 *  1    2017年10月20日      Simba.Hua         Create
 * ****************************************************************************
 * </pre>
 * @author Simba.Hua
 */
@Configuration
@ComponentScan(basePackages = {"com.sf.ams"},useDefaultFilters = true)
@EnableWebMvc
public class ApplicationConfig extends WebMvcConfigurerAdapter{
    @Bean
    public JsonReturnHandler jsonReturnHandler(){
        return new JsonReturnHandler();//初始化json过滤器
    }
    @Override
    public void addReturnValueHandlers(
            List<HandlerMethodReturnValueHandler> returnValueHandlers) {
        returnValueHandlers.add(jsonReturnHandler());
    }
}

@SpringBootApplication
@Import(value = {ApplicationConfig.class})
public class AutomaticSqlApplication {

    public static void main(String[] args) {
        SpringApplication.run(AutomaticSqlApplication.class, args);
    }
}

配置需继承WebMvcConfigurerAdapter,重写addReturnValueHandlers方法,把JsonReturnHandler加入进去,继承的作用我这里就不详细写了,加入这个后重启spring boot,访问页面的时候直接报如下错

javax.servlet.ServletException: Could not resolve view with name 'index' in servlet with name 'dispatcherServlet'
    at org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1305) ~[spring-webmvc-5.0.1.BUILD-SNAPSHOT.jar:5.0.1.BUILD-SNAPSHOT]
    at org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1069) ~[spring-webmvc-5.0.1.BUILD-SNAPSHOT.jar:5.0.1.BUILD-SNAPSHOT]
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1008) ~[spring-webmvc-5.0.1.BUILD-SNAPSHOT.jar:5.0.1.BUILD-SNAPSHOT]
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:925) ~[spring-webmvc-5.0.1.BUILD-SNAPSHOT.jar:5.0.1.BUILD-SNAPSHOT]
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:978) ~[spring-webmvc-5.0.1.BUILD-SNAPSHOT.jar:5.0.1.BUILD-SNAPSHOT]
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:870) ~[spring-webmvc-5.0.1.BUILD-SNAPSHOT.jar:5.0.1.BUILD-SNAPSHOT]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:687) ~[javax.servlet-api-3.1.0.jar:3.1.0]

index.jsp页面怎么就会找不到了呢?就是因为加了这个配置项?
后来我把@EnableWebMvc去了,又可以正常访问了,但是注解@JsonFieldFilter并没有起作用,没有把属性过滤掉,后来查各种资料,发现@EnableWebMvc会改变Spring boot 对静态资源的配置,我的Spring boot的application.properties文件有如下配置
spring.mvc.view.prefix=/WEB-INF/jsp/
spring.mvc.view.suffix=.jsp
加入@EnableWebMvc后肯定改变了这两个值,既然跟这个有关系那么肯定跟WebMvcConfigurerAdapter也有关系,后来发现WebMvcConfigurerAdapter确实有静态资源有关的方法,addResourceHandlers和configureViewResolvers,重写这两个方法,把jsp文件的路径和文件的结尾告诉spring boot,加上以下代码

package com.sf.springboot;

import java.util.List;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.ViewResolverRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

import com.sf.ams.json.JsonReturnHandler;

/**
 * 描述:
 * 
 * <pre>HISTORY
 * ****************************************************************************
 *  ID   DATE           PERSON          REASON
 *  1    2017年10月20日      Simba.Hua         Create
 * ****************************************************************************
 * </pre>
 * @author Simba.Hua
 */
@Configuration
@ComponentScan(basePackages = {"com.sf.ams"},useDefaultFilters = true)
@EnableWebMvc
public class ApplicationConfig extends WebMvcConfigurerAdapter{
    @Bean
    public JsonReturnHandler jsonReturnHandler(){
        return new JsonReturnHandler();
    }

    @Override
    public void addReturnValueHandlers(
            List<HandlerMethodReturnValueHandler> returnValueHandlers) {
        returnValueHandlers.add(jsonReturnHandler());
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/**").addResourceLocations("/");//所有
    }

    @Override
    public void configureViewResolvers(ViewResolverRegistry registry) {
        registry.jsp("/WEB-INF/jsp/", ".jsp");
    }

}

系统文件路径如下
这里写图片描述
重新启动,不会在报错了,但是发现也还没有进入JsonReturnHandler类的handleReturnValue方法,当然就没有过滤掉gitPassword字段了,问题还是没有解决,是什么原因呢?发现配置项的
@Override
public void addReturnValueHandlers(
List returnValueHandlers) {
returnValueHandlers.add(jsonReturnHandler());
}
方法是把JsonReturnHandler加入到了返回值过滤list中,但是他们是有顺序的,最后加进来的是JsonReturnHandler,我们知道把对象转换成json的是通过注解@ResponseBody,而ResponseBody也应该是HandlerMethodReturnValueHandler中的一员,并且先处理ResponseBody,然后就直接返回了,就不处理JsonReturnHandler了,如果我们把@ResponseBody去了是不是不返回@ResponseBody了呢,最后Controller改成了如下代码,即把@ResponseBody去了

package com.sf.ams.controller;

import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

import com.sf.ams.biz.IDataStoreXmlAnalysisBiz;
import com.sf.ams.entity.SystemInfo;
import com.sf.ams.entity.TreeNode;
import com.sf.ams.json.JsonFieldFilter;
import com.sf.ams.utils.TreeNodeUtil;
/**
 * 描述:
 * 
 * <pre>HISTORY
 * ****************************************************************************
 *  ID   DATE           PERSON          REASON
 *  1    2017-09-26      Simba.Hua         Create
 * ****************************************************************************
 * </pre>
 * @author Simba.Hua
 */
@Controller
@RequestMapping("/dataStoreAnalysis")
public class DataStoreAnalysisController {
    @Autowired
    private IDataStoreXmlAnalysisBiz dataStoreXmlAnalysisBiz;
    @RequestMapping("/getSystemDetail")
    @JsonFieldFilter(type = SystemInfo.class,exclude = "gitPassword")
    public SystemInfo getSystemDetail(String systemCode){
        return dataStoreXmlAnalysisBiz.getSystemBySystemCode(systemCode);
    }
}

最后发现终于不显示gitPassword字段了
这里写图片描述

这里还是发现对spring boot和springmvc不够熟悉。以后要多练习

  • 7
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 11
    评论
Spring Boot允许你使用过滤器来拦截和处理HTTP请求和响应。过滤器是一种在请求进入servlet容器之前或响应离开容器之前执行操作的组件。 在Spring Boot中,你可以通过实现`javax.servlet.Filter`接口来创建一个过滤器。以下是一个简单的示例: ```java import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; public class MyFilter implements Filter { @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) servletRequest; HttpServletResponse response = (HttpServletResponse) servletResponse; // 执行你的过滤操作,例如检查请求头或参数等 // 将请求传递给下一个过滤器或目标资源 filterChain.doFilter(request, response); } // 其他方法如init()和destroy()可以根据需要实现 } ``` 要将过滤器应用到Spring Boot应用程序中,你可以将其声明为一个Bean,并使用`@WebFilter`注解指定该过滤器的URL模式。例如: ```java import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class MyFilterConfig { @Bean public FilterRegistrationBean<MyFilter> myFilterRegistrationBean() { FilterRegistrationBean<MyFilter> registrationBean = new FilterRegistrationBean<>(); registrationBean.setFilter(new MyFilter()); registrationBean.addUrlPatterns("/api/*"); // 设置URL模式 return registrationBean; } } ``` 在上面的示例中,我们创建了一个名为`MyFilter`的过滤器,并使用`FilterRegistrationBean`将其注册到应用程序中。`addUrlPatterns`方法指定了要拦截的URL模式,例如`/api/*`表示所有以`/api/`开头的URL都会被该过滤器拦截处理。 请注意,这只是一个简单的示例,你可以根据自己的需求进行更复杂的过滤操作。同时,你也可以使用其他方式来注册过滤器,如使用`@ServletComponentScan`注解或通过`FilterRegistrationBean`的其他方法来设置过滤器的顺序等。
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值