package com.vci.starter.web.autoconfigure;
|
|
import com.alibaba.fastjson.serializer.SerializerFeature;
|
import com.alibaba.fastjson.support.config.FastJsonConfig;
|
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
|
import com.vci.starter.web.interceptor.VciLocaleInterceptor;
|
import com.vci.starter.web.interceptor.VciLogAfterInterceptor;
|
import com.vci.starter.web.interceptor.VciLogBeforeInterceptor;
|
import com.vci.starter.web.interceptor.VciSecurityInterceptor;
|
import com.vci.starter.web.properties.CorsProperties;
|
import com.vci.starter.web.toolmodel.String2DateConverterForSpringMvc;
|
import com.vci.starter.web.util.LocalFileUtil;
|
import com.vci.starter.web.util.VciDateUtil;
|
import org.slf4j.Logger;
|
import org.slf4j.LoggerFactory;
|
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Configuration;
|
import org.springframework.format.FormatterRegistry;
|
import org.springframework.http.CacheControl;
|
import org.springframework.http.MediaType;
|
import org.springframework.http.converter.HttpMessageConverter;
|
import org.springframework.http.converter.StringHttpMessageConverter;
|
import org.springframework.web.filter.FormContentFilter;
|
import org.springframework.web.servlet.LocaleResolver;
|
import org.springframework.web.servlet.config.annotation.*;
|
import org.springframework.web.servlet.i18n.SessionLocaleResolver;
|
|
import java.io.File;
|
import java.nio.charset.StandardCharsets;
|
import java.util.ArrayList;
|
import java.util.List;
|
import java.util.Locale;
|
import java.util.Map;
|
|
/**
|
* spring mvc的相关配置
|
*
|
* @author weidy
|
*/
|
@Configuration
|
@EnableWebMvc
|
@ConditionalOnProperty(prefix = "vcispringmvc",name="enabled",havingValue = "true",matchIfMissing = true)
|
@ConfigurationProperties(prefix = "vcispringmvc")
|
public class SpringMVCConfig implements WebMvcConfigurer {
|
|
/**
|
* 日志对象
|
*/
|
private Logger log = LoggerFactory.getLogger(SpringMVCConfig.class);
|
|
private boolean enabled;
|
|
/**
|
* 外部文件夹的映射地址
|
*/
|
private Map<String,String> resourceFolderMap ;
|
|
/**
|
* 不校验安全的链接地址
|
*/
|
private List<String> unCheckUrls;
|
|
/**
|
* 不更新请求时间的链接地址
|
*/
|
private List<String> unStorageRequestTimeUrls;
|
|
/**
|
* 跨域的配置
|
*/
|
@Autowired(required = false)
|
private CorsProperties corsProperties;
|
|
/**
|
* 默认允许的域名
|
*/
|
private String[] DEFAULT_ORIGINS = {"*"};
|
|
/**
|
* 默认允许的头
|
*/
|
private String[] DEFAULT_ALLOWED_HEADERS = {"*"};
|
|
/**
|
* 默认允许的方法
|
*/
|
private String[] DEFAULT_METHODS = {"PUT", "DELETE","GET","POST"};
|
|
/**
|
* 默认暴露的头
|
*/
|
private String[] DEFAULT_EXPOSEDHEADERS = {"access-control-allow-headers",
|
"access-control-allow-methods",
|
"access-control-allow-origin",
|
"access-control-max-age",
|
"X-Frame-Options"};
|
/**
|
* 默认是否允许证书
|
*/
|
private boolean DEFAULT_ALLOW_CREDENTIALS = true;
|
|
/**
|
* 默认的最大值
|
*/
|
private long DEFAULT_MAX_AGE = 1800;
|
|
public boolean isEnabled() {
|
return enabled;
|
}
|
|
public void setEnabled(boolean enabled) {
|
this.enabled = enabled;
|
}
|
|
public Map<String, String> getResourceFolderMap() {
|
return resourceFolderMap;
|
}
|
|
public void setResourceFolderMap(Map<String, String> resourceFolderMap) {
|
this.resourceFolderMap = resourceFolderMap;
|
}
|
|
/**
|
* 安全相关的拦截器
|
* @return 安全相关的拦截器
|
*/
|
@Bean
|
public VciSecurityInterceptor vciSecurityInterceptor(){
|
return new VciSecurityInterceptor();
|
}
|
|
/**
|
* 增加PUT和DELETE的支持
|
* @return 过滤器
|
*/
|
@Bean
|
public FormContentFilter formContentFilter() {
|
return new FormContentFilter();
|
}
|
|
/**
|
* 使用fastjson返回值
|
* @param converters 所有的消息转换器
|
*/
|
@Override
|
public void configureMessageConverters(List<HttpMessageConverter<?>> converters){
|
converters.add(new StringHttpMessageConverter(StandardCharsets.UTF_8));
|
converters.add(createFastJsonConverter());
|
}
|
|
/**
|
* 获取fastjson的转换器,即springmvc和fegin都使用fastjson来序列化
|
* @return fastjson转换器对象,日期格式都是yyyy-MM-dd HH:mm:ss.SSS
|
*/
|
public static HttpMessageConverter createFastJsonConverter(){
|
FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
|
List<MediaType> supportedMediaTypes = new ArrayList<>();
|
supportedMediaTypes.add(MediaType.APPLICATION_JSON);
|
supportedMediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
|
supportedMediaTypes.add(MediaType.APPLICATION_ATOM_XML);
|
supportedMediaTypes.add(MediaType.APPLICATION_FORM_URLENCODED);
|
supportedMediaTypes.add(MediaType.APPLICATION_OCTET_STREAM);
|
supportedMediaTypes.add(MediaType.APPLICATION_PDF);
|
supportedMediaTypes.add(MediaType.APPLICATION_RSS_XML);
|
supportedMediaTypes.add(MediaType.APPLICATION_XHTML_XML);
|
supportedMediaTypes.add(MediaType.APPLICATION_XML);
|
supportedMediaTypes.add(MediaType.IMAGE_GIF);
|
supportedMediaTypes.add(MediaType.IMAGE_JPEG);
|
supportedMediaTypes.add(MediaType.IMAGE_PNG);
|
supportedMediaTypes.add(MediaType.TEXT_EVENT_STREAM);
|
supportedMediaTypes.add(MediaType.TEXT_HTML);
|
supportedMediaTypes.add(MediaType.TEXT_MARKDOWN);
|
//supportedMediaTypes.add(MediaType.TEXT_PLAIN);
|
supportedMediaTypes.add(MediaType.TEXT_XML);
|
fastConverter.setSupportedMediaTypes(supportedMediaTypes);
|
//创建配置类
|
FastJsonConfig fastJsonConfig = new FastJsonConfig();
|
//修改配置返回内容的过滤
|
//WriteNullListAsEmpty :List字段如果为null,输出为[],而非null
|
//WriteNullStringAsEmpty : 字符类型字段如果为null,输出为"",而非null
|
//DisableCircularReferenceDetect :消除对同一对象循环引用的问题,默认为false(如果不配置有可能会进入死循环)
|
//WriteNullBooleanAsFalse:Boolean字段如果为null,输出为false,而非null
|
//WriteMapNullValue:是否输出值为null的字段,默认为false
|
fastJsonConfig.setSerializerFeatures(
|
SerializerFeature.DisableCircularReferenceDetect,
|
SerializerFeature.WriteMapNullValue
|
);
|
fastJsonConfig.setDateFormat(VciDateUtil.DateTimeMillFormat);
|
fastConverter.setFastJsonConfig(fastJsonConfig);
|
|
|
return fastConverter;
|
}
|
|
/**
|
* 设置格式转换器,在接收到参数封装到对象时使用
|
* @param registry 格式注册器
|
*/
|
@Override
|
public void addFormatters(FormatterRegistry registry) {
|
//添加日期的转换器
|
registry.addConverter(new String2DateConverterForSpringMvc());
|
//registry.addConverter(new StringEscapeEditor());
|
|
}
|
|
/**
|
* 添加需要转换的路径,常用于读取tomcat外部的路径
|
* @param registry 资源转换注册器
|
*/
|
@Override
|
public void addResourceHandlers(ResourceHandlerRegistry registry){
|
if(resourceFolderMap!=null){
|
for(String resourceUrl :resourceFolderMap.keySet() ){
|
String folder = resourceFolderMap.get(resourceUrl);
|
if(folder.contains("$[projectPath]")){
|
folder = folder.replace("$[projectPath]", LocalFileUtil.getProjectFolder(LocalFileUtil.mainClass));
|
}
|
log.info("注册了外部路径转换器, " + resourceUrl + ":" + folder);
|
registry.addResourceHandler("/" + resourceUrl + "/**").addResourceLocations("file:" + folder + File.separator);
|
}
|
}
|
String logs = "file:" + LocalFileUtil.getProjectFolder(LocalFileUtil.mainClass) + File.separator + "logs" +File.separator;
|
log.info("日志的文件夹映射为" + logs);
|
registry.addResourceHandler("/log/**").addResourceLocations( logs);
|
registry.addResourceHandler("/doc/**").addResourceLocations( "classpath:/md/");
|
registry.addResourceHandler("/**")
|
.addResourceLocations("classpath:/META-INF/resources/")
|
.addResourceLocations("classpath:/resources/")
|
.addResourceLocations("classpath:/static/")
|
.addResourceLocations("classpath:/html/")
|
.addResourceLocations("classpath:/public/").setCacheControl(CacheControl.noStore());
|
|
}
|
|
/**
|
* 配置index
|
* @param registry 注册器
|
*/
|
@Override
|
public void addViewControllers(ViewControllerRegistry registry) {
|
registry.addViewController("/").setViewName("forward:index.html");
|
}
|
/**
|
* 设置默认的语言为简体中文
|
* @return 默认语言解析器
|
*/
|
@Bean
|
public LocaleResolver localeResolver(){
|
SessionLocaleResolver localeResolver = new SessionLocaleResolver();
|
localeResolver.setDefaultLocale(Locale.SIMPLIFIED_CHINESE);
|
return localeResolver;
|
}
|
|
/**
|
* 配置拦截器
|
* @param registry 拦截器注册器
|
*/
|
@Override
|
public void addInterceptors(InterceptorRegistry registry){
|
/* preHandle按拦截器定义顺序地调用
|
postHandle按拦截器定义逆序地调用
|
afterCompletion按拦截器定义逆序地调用
|
postHandle在截器链内所有拦截器返回成功调用
|
afterCompletion只有在preHandle返回true才调用*/
|
//1. 需要配置多语的拦截器,配置多语环境
|
//2. 需要配置token的拦截器,这个必须在权限拦截器之前,里面包括用户的session信息,日志的
|
//3. 配置权限的拦截器,如果不符合要求则返回异常。需要处理系统访问的权限,接口访问的权限,数据访问的权限三种类型
|
//4. 更新用户请求的最后时间
|
|
//添加日志的MDC
|
registry.addInterceptor(vciLogBeforeInterceptor());
|
//多语言
|
registry.addInterceptor(vciLocaleInterceptor());
|
registry.addInterceptor(vciSecurityInterceptor()).excludePathPatterns("/error").excludePathPatterns("/**.*");
|
//移除日志的MDC
|
registry.addInterceptor(vciLogAfterInterceptor());
|
}
|
|
/**
|
* 多语言相关的拦截器
|
* @return 多语言拦截器
|
*/
|
@Bean
|
public VciLocaleInterceptor vciLocaleInterceptor(){
|
return new VciLocaleInterceptor();
|
}
|
|
|
|
/**
|
* 日志相关的拦截器
|
* @return 日志拦截器
|
*/
|
@Bean
|
public VciLogBeforeInterceptor vciLogBeforeInterceptor(){
|
return new VciLogBeforeInterceptor();
|
}
|
|
/**
|
* 日志相关的拦截器
|
* @return 日志拦截器
|
*/
|
@Bean
|
public VciLogAfterInterceptor vciLogAfterInterceptor(){
|
return new VciLogAfterInterceptor();
|
}
|
|
|
/**
|
* 设置跨域
|
* @param registry 跨域注册器
|
*/
|
@Override
|
public void addCorsMappings(CorsRegistry registry) {
|
log.info("进入跨域配置");
|
if(corsProperties !=null && "true".equalsIgnoreCase(corsProperties.getEnabled())) {
|
log.info("打开了跨域开关");
|
String[] allowedOrigins = corsProperties.getAllowedOrigins();
|
|
String[] allowedHeaders = corsProperties.getAllowedHeaders();
|
|
String[] allowedMethods = corsProperties.getAllowedMethods();
|
|
String[] exposedHeaders = corsProperties.getExposedHeaders();
|
|
Boolean allowCredentials = corsProperties.getAllowCredentials();
|
|
Long maxAge = corsProperties.getMaxAge();
|
log.info("注册跨域内容 = [" + allowedOrigins + "]");
|
String mappings = corsProperties.getMappings();
|
if (allowedHeaders == null || allowedHeaders.length == 0) {
|
allowedHeaders = DEFAULT_ALLOWED_HEADERS;
|
}
|
if (allowedOrigins == null || allowedOrigins.length == 0) {
|
allowedOrigins = DEFAULT_ORIGINS;
|
}
|
|
if (exposedHeaders == null || exposedHeaders.length == 0) {
|
exposedHeaders = DEFAULT_EXPOSEDHEADERS;
|
}
|
if (allowedMethods == null || allowedMethods.length == 0){
|
allowedMethods = DEFAULT_METHODS;
|
}
|
if (maxAge == null || maxAge == 0) {
|
maxAge = DEFAULT_MAX_AGE;
|
}
|
if (allowCredentials == null) {
|
allowCredentials = DEFAULT_ALLOW_CREDENTIALS;
|
}
|
if (mappings == null || mappings.trim() == "") {
|
mappings = "/**";
|
}
|
log.info("跨域映射的URL:" + mappings);
|
registry.addMapping(mappings)
|
.allowedOrigins(allowedOrigins)
|
.allowedMethods(allowedMethods)
|
.allowedHeaders(allowedHeaders)
|
.exposedHeaders(exposedHeaders)
|
.allowCredentials(allowCredentials).maxAge(maxAge);
|
}else{
|
log.info("没有打开跨域开关");
|
}
|
}
|
|
public CorsProperties getCorsProperties() {
|
return corsProperties;
|
}
|
|
public void setCorsProperties(CorsProperties corsProperties) {
|
this.corsProperties = corsProperties;
|
}
|
|
public String[] getDEFAULT_ORIGINS() {
|
return DEFAULT_ORIGINS;
|
}
|
|
public void setDEFAULT_ORIGINS(String[] DEFAULT_ORIGINS) {
|
this.DEFAULT_ORIGINS = DEFAULT_ORIGINS;
|
}
|
|
public String[] getDEFAULT_ALLOWED_HEADERS() {
|
return DEFAULT_ALLOWED_HEADERS;
|
}
|
|
public void setDEFAULT_ALLOWED_HEADERS(String[] DEFAULT_ALLOWED_HEADERS) {
|
this.DEFAULT_ALLOWED_HEADERS = DEFAULT_ALLOWED_HEADERS;
|
}
|
|
public String[] getDEFAULT_METHODS() {
|
return DEFAULT_METHODS;
|
}
|
|
public void setDEFAULT_METHODS(String[] DEFAULT_METHODS) {
|
this.DEFAULT_METHODS = DEFAULT_METHODS;
|
}
|
|
public boolean isDEFAULT_ALLOW_CREDENTIALS() {
|
return DEFAULT_ALLOW_CREDENTIALS;
|
}
|
|
public void setDEFAULT_ALLOW_CREDENTIALS(boolean DEFAULT_ALLOW_CREDENTIALS) {
|
this.DEFAULT_ALLOW_CREDENTIALS = DEFAULT_ALLOW_CREDENTIALS;
|
}
|
|
public long getDEFAULT_MAX_AGE() {
|
return DEFAULT_MAX_AGE;
|
}
|
|
public void setDEFAULT_MAX_AGE(long DEFAULT_MAX_AGE) {
|
this.DEFAULT_MAX_AGE = DEFAULT_MAX_AGE;
|
}
|
|
public List<String> getUnCheckUrls() {
|
return unCheckUrls;
|
}
|
|
public void setUnCheckUrls(List<String> unCheckUrls) {
|
this.unCheckUrls = unCheckUrls;
|
}
|
|
public List<String> getUnStorageRequestTimeUrls() {
|
return unStorageRequestTimeUrls;
|
}
|
|
public void setUnStorageRequestTimeUrls(List<String> unStorageRequestTimeUrls) {
|
this.unStorageRequestTimeUrls = unStorageRequestTimeUrls;
|
}
|
|
public String[] getDEFAULT_EXPOSEDHEADERS() {
|
return DEFAULT_EXPOSEDHEADERS;
|
}
|
|
public void setDEFAULT_EXPOSEDHEADERS(String[] DEFAULT_EXPOSEDHEADERS) {
|
this.DEFAULT_EXPOSEDHEADERS = DEFAULT_EXPOSEDHEADERS;
|
}
|
}
|