字符串时间转Date类型 ,@ControllerAdvice注解定义的controller类@InitBinder注解功能失效原因

字符串时间转Date类型 ,@ControllerAdvice注解定义的controller类@InitBinder注解功能失效原因

渡星河
2023-04-14 / 0 评论 / 14 阅读 / 正在检测是否收录...
温馨提示:
本文最后更新于2023年04月14日,已超过523天没有更新,若内容或图片失效,请留言反馈。

起因

我们先来看一下我的全局配置类

package cn.yyx.common;

import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.InitBinder;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * 给全局添加@InitBinder注解
 * 不用给每个controller添加此方法
 */
@ControllerAdvice
public class GlobalBindingInitializer {
    @InitBinder
    public void initBinder(WebDataBinder binder) {
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
        binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, true));
    }
}
首先这个类是把客户端传过来的字符串时间转换成为Date类型,并且是全局的,不用每个controller类都去配置,可以这么说,很好用啊

失效场景

我今天做项目用到的技术栈如下
springboot,mybatis-plus,springsecurity等

失效原因

就是因为使用了security做权限验证出的冲突问题

因为 Spring Security 默认情况下也会定义一个全局的 WebMvcConfigurer 实现类,并注册到应用上下文中。这个全局类会覆盖掉我们自己定义的 GlobalBindingInitializer 类,导致我们自己定义的日期格式化失效

解决方案

  1. 解决这个问题的方法是,在我们自己定义的 GlobalBindingInitializer 类上添加 @ConditionalOnMissingBean 注解,表示当 Spring 容器中不存在 WebMvcConfigurer 类型的 Bean 时才扫描并注册这个类。具体代码如下:
@ControllerAdvice
@ConditionalOnMissingBean(WebMvcConfigurer.class)
public class GlobalBindingInitializer {
    @InitBinder
    public void initBinder(WebDataBinder binder) {
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
        binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, true));
    }
}

2.解决方法二,直接在security的配置类写时间转换例如下方代码,最=最后一个bean注解

package cn.yyx.security.config;

import cn.yyx.security.filter.TokenAuthenticationFilter;
import cn.yyx.security.filter.TokenLoginFilter;
import cn.yyx.security.security.TokenLogoutHandler;
import cn.yyx.security.security.TokenManager;
import cn.yyx.security.security.UnauthorizedEntryPoint;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * 该类是用了进行认证配置的
 */
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
@ControllerAdvice
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private PasswordEncoder passwordEncoder;
    @Autowired
    private UserDetailsService userDetailsService;
    @Autowired
    private TokenManager tokenManager;
    @Autowired
    private RedisTemplate redisTemplate;
    /**
     * 密码处理
     * @param auth
     * @throws Exception
     */
    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder);
    }
    /**
     * 绑定权限和对应的路径
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.exceptionHandling()
                .authenticationEntryPoint(new UnauthorizedEntryPoint())
                .and().csrf().disable()
                .cors().configurationSource(corsConfigurationSource())
                .and()
                .authorizeRequests()
                .anyRequest().authenticated()
                .and().logout().logoutUrl("/admin/acl/index/logout")
                .addLogoutHandler(new TokenLogoutHandler(tokenManager,redisTemplate)).and()
                //添加认证过滤器  authenticationManager()这是调用了父类的认证管理器
                // tokenManager 对token管理的对象(生成和解析token)
                .addFilter(new TokenLoginFilter(authenticationManager(), tokenManager, redisTemplate))
                //添加授权过滤器
                .addFilter(new TokenAuthenticationFilter(authenticationManager(), tokenManager, redisTemplate)).httpBasic();
        http.cors();
    }
    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.addAllowedOrigin("*");    //同源配置,*表示任何请求都视为同源,若需指定ip和端口可以改为如“localhost:8080”,多个以“,”分隔;
        corsConfiguration.addAllowedHeader("*");//header,允许哪些header,本案中使用的是token,此处可将*替换为token;
        corsConfiguration.addAllowedMethod("*");    //允许的请求方法,PSOT、GET等
        ((UrlBasedCorsConfigurationSource) source).registerCorsConfiguration("/**", corsConfiguration); //配置允许跨域访问的url
        return source;
    }
    //使用PasswordEncoder 接口的实现类进行加密
    @Bean
    public  PasswordEncoder  passwordEncoder(){
        return new BCryptPasswordEncoder();
    }
    /**
     * 配置哪些请求不拦截
     * @param web
     * @throws Exception
     */
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/api/**","/swagger-resources/**",
                "/webjars/**", "/v2/**", "/swagger-ui/**","/customer/add","/order/save");
    }

    @InitBinder
    public void initBinder(WebDataBinder binder) {
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
        binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, true));
    }
    @Bean
    public ObjectMapper objectMapper() {
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd"));
        return objectMapper;
    }

}

3.解决方法三就是直接在实体类使用注解解决,例如

@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
    private Date hiredate;

2

评论 (0)

取消