You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
192 lines
8.2 KiB
192 lines
8.2 KiB
package com.company.project.configurer;
|
|
|
|
|
|
import com.alibaba.fastjson.JSON;
|
|
import com.alibaba.fastjson.serializer.SerializerFeature;
|
|
import com.alibaba.fastjson.support.config.FastJsonConfig;
|
|
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter4;
|
|
import com.company.project.core.Result;
|
|
import com.company.project.core.ResultCode;
|
|
import com.company.project.core.ServiceException;
|
|
import org.apache.commons.codec.digest.DigestUtils;
|
|
import org.apache.commons.lang3.StringUtils;
|
|
import org.slf4j.Logger;
|
|
import org.slf4j.LoggerFactory;
|
|
import org.springframework.beans.factory.annotation.Value;
|
|
import org.springframework.context.annotation.Configuration;
|
|
import org.springframework.http.converter.HttpMessageConverter;
|
|
import org.springframework.web.method.HandlerMethod;
|
|
import org.springframework.web.servlet.HandlerExceptionResolver;
|
|
import org.springframework.web.servlet.ModelAndView;
|
|
import org.springframework.web.servlet.NoHandlerFoundException;
|
|
import org.springframework.web.servlet.config.annotation.CorsRegistry;
|
|
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
|
|
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
|
|
|
|
import javax.servlet.http.HttpServletRequest;
|
|
import javax.servlet.http.HttpServletResponse;
|
|
import java.io.IOException;
|
|
import java.nio.charset.Charset;
|
|
import java.util.ArrayList;
|
|
import java.util.Collections;
|
|
import java.util.List;
|
|
|
|
/**
|
|
* Spring MVC 配置
|
|
*/
|
|
@Configuration
|
|
public class WebMvcConfigurer extends WebMvcConfigurerAdapter {
|
|
|
|
private final Logger logger = LoggerFactory.getLogger(WebMvcConfigurer.class);
|
|
@Value("${spring.profiles.active}")
|
|
private String env;//当前激活的配置文件
|
|
|
|
//使用阿里 FastJson 作为JSON MessageConverter
|
|
@Override
|
|
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
|
|
FastJsonHttpMessageConverter4 converter = new FastJsonHttpMessageConverter4();
|
|
FastJsonConfig config = new FastJsonConfig();
|
|
config.setSerializerFeatures(SerializerFeature.WriteMapNullValue,//保留空的字段
|
|
SerializerFeature.WriteNullStringAsEmpty,//String null -> ""
|
|
SerializerFeature.WriteNullNumberAsZero);//Number null -> 0
|
|
converter.setFastJsonConfig(config);
|
|
converter.setDefaultCharset(Charset.forName("UTF-8"));
|
|
converters.add(converter);
|
|
}
|
|
|
|
|
|
//统一异常处理
|
|
@Override
|
|
public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
|
|
exceptionResolvers.add(new HandlerExceptionResolver() {
|
|
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception e) {
|
|
Result result = new Result();
|
|
if (handler instanceof HandlerMethod) {
|
|
HandlerMethod handlerMethod = (HandlerMethod) handler;
|
|
|
|
if (e instanceof ServiceException) {//业务失败的异常,如“账号或密码错误”
|
|
result.setCode(ResultCode.FAIL).setMessage(e.getMessage());
|
|
logger.info(e.getMessage());
|
|
} else {
|
|
result.setCode(ResultCode.INTERNAL_SERVER_ERROR).setMessage("接口 [" + request.getRequestURI() + "] 内部错误,请联系管理员");
|
|
String message = String.format("接口 [%s] 出现异常,方法:%s.%s,异常摘要:%s",
|
|
request.getRequestURI(),
|
|
handlerMethod.getBean().getClass().getName(),
|
|
handlerMethod.getMethod().getName(),
|
|
e.getMessage());
|
|
logger.error(message, e);
|
|
}
|
|
} else {
|
|
if (e instanceof NoHandlerFoundException) {
|
|
result.setCode(ResultCode.NOT_FOUND).setMessage("接口 [" + request.getRequestURI() + "] 不存在");
|
|
} else {
|
|
result.setCode(ResultCode.INTERNAL_SERVER_ERROR).setMessage(e.getMessage());
|
|
logger.error(e.getMessage(), e);
|
|
}
|
|
}
|
|
responseResult(response, result);
|
|
return new ModelAndView();
|
|
}
|
|
|
|
});
|
|
}
|
|
|
|
//解决跨域问题
|
|
@Override
|
|
public void addCorsMappings(CorsRegistry registry) {
|
|
//registry.addMapping("/**");
|
|
}
|
|
|
|
//添加拦截器
|
|
@Override
|
|
public void addInterceptors(InterceptorRegistry registry) {
|
|
//接口签名认证拦截器,该签名认证比较简单,实际项目中建议使用Json Web Token代替。
|
|
if (!StringUtils.contains(env, "dev")) { //开发环境忽略签名认证
|
|
registry.addInterceptor(new HandlerInterceptorAdapter() {
|
|
|
|
@Override
|
|
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
|
|
String sign = request.getParameter("sign");
|
|
//验证签名
|
|
if (StringUtils.isNotEmpty(sign) && validateSign(request, sign)) {
|
|
return true;
|
|
} else {
|
|
logger.warn("签名认证失败,请求接口:{},请求IP:{},请求参数:{}",
|
|
request.getRequestURI(), getIpAddress(request), JSON.toJSONString(request.getParameterMap()));
|
|
|
|
Result result = new Result();
|
|
result.setCode(ResultCode.UNAUTHORIZED).setMessage("签名认证失败");
|
|
responseResult(response, result);
|
|
return false;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
private void responseResult(HttpServletResponse response, Result result) {
|
|
response.setCharacterEncoding("UTF-8");
|
|
response.setHeader("Content-type", "application/json;charset=UTF-8");
|
|
response.setStatus(200);
|
|
try {
|
|
response.getWriter().write(JSON.toJSONString(result));
|
|
} catch (IOException ex) {
|
|
logger.error(ex.getMessage());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 一个简单的签名认证,规则:请求参数按ASCII码排序后,拼接为a=value&b=value...这样的字符串后进行MD5
|
|
*
|
|
* @param request
|
|
* @param requestSign
|
|
* @return
|
|
*/
|
|
private boolean validateSign(HttpServletRequest request, String requestSign) {
|
|
List<String> keys = new ArrayList<String>(request.getParameterMap().keySet());
|
|
Collections.sort(keys);
|
|
|
|
String linkString = "";
|
|
|
|
for (String key : keys) {
|
|
if (!"sign".equals(key)) {
|
|
linkString += key + "=" + request.getParameter(key) + "&";
|
|
}
|
|
}
|
|
if (StringUtils.isEmpty(linkString))
|
|
return false;
|
|
|
|
linkString = linkString.substring(0, linkString.length() - 1);
|
|
String key = "Potato";//自己修改
|
|
String sign = DigestUtils.md5Hex(linkString + key);
|
|
|
|
return StringUtils.equals(sign, requestSign);
|
|
|
|
}
|
|
|
|
private String getIpAddress(HttpServletRequest request) {
|
|
String ip = request.getHeader("x-forwarded-for");
|
|
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
|
ip = request.getHeader("Proxy-Client-IP");
|
|
}
|
|
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
|
ip = request.getHeader("WL-Proxy-Client-IP");
|
|
}
|
|
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
|
ip = request.getHeader("HTTP_CLIENT_IP");
|
|
}
|
|
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
|
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
|
|
}
|
|
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
|
ip = request.getRemoteAddr();
|
|
}
|
|
// 如果是多级代理,那么取第一个ip为客户ip
|
|
if (ip != null && ip.indexOf(",") != -1) {
|
|
ip = ip.substring(0, ip.indexOf(",")).trim();
|
|
}
|
|
|
|
return ip;
|
|
}
|
|
}
|
|
|