起因
我们先来看一下我的全局配置类
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 类,导致我们自己定义的日期格式化失效
解决方案
- 解决这个问题的方法是,在我们自己定义的 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;
评论 (0)