MyWebMvcConfigurer.java 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. package com.txz.operating.configurer;
  2. import com.alibaba.fastjson.JSON;
  3. import com.alibaba.fastjson.serializer.SerializeConfig;
  4. import com.alibaba.fastjson.serializer.SerializerFeature;
  5. import com.alibaba.fastjson.serializer.ToStringSerializer;
  6. import com.alibaba.fastjson.support.config.FastJsonConfig;
  7. import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
  8. import com.txz.operating.core.ProjectConstant;
  9. import com.txz.operating.core.ResultCode;
  10. import com.txz.operating.core.ServiceException;
  11. import com.txz.operating.core.cache.CacheKey;
  12. import com.txz.operating.core.cache.CacheType;
  13. import com.txz.operating.result.Result;
  14. import org.apache.commons.codec.digest.DigestUtils;
  15. import org.apache.commons.lang3.StringUtils;
  16. import org.slf4j.Logger;
  17. import org.slf4j.LoggerFactory;
  18. import org.springframework.context.annotation.Configuration;
  19. import org.springframework.data.redis.core.RedisTemplate;
  20. import org.springframework.http.MediaType;
  21. import org.springframework.http.converter.HttpMessageConverter;
  22. import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
  23. import org.springframework.web.method.HandlerMethod;
  24. import org.springframework.web.servlet.HandlerExceptionResolver;
  25. import org.springframework.web.servlet.ModelAndView;
  26. import org.springframework.web.servlet.NoHandlerFoundException;
  27. import org.springframework.web.servlet.config.annotation.CorsRegistry;
  28. import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
  29. import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
  30. import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
  31. import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
  32. import javax.annotation.Resource;
  33. import javax.servlet.ServletException;
  34. import javax.servlet.http.HttpServletRequest;
  35. import javax.servlet.http.HttpServletResponse;
  36. import java.io.IOException;
  37. import java.math.BigInteger;
  38. import java.util.ArrayList;
  39. import java.util.Collections;
  40. import java.util.List;
  41. /**
  42. * Spring MVC 配置
  43. */
  44. @Configuration
  45. public class MyWebMvcConfigurer implements WebMvcConfigurer {
  46. private final Logger logger = LoggerFactory.getLogger(MyWebMvcConfigurer.class);
  47. @Resource
  48. private Parameters parameters;
  49. @Resource
  50. private RedisTemplate redisTemplate;
  51. @Override
  52. public void addResourceHandlers(ResourceHandlerRegistry registry) {
  53. registry.addResourceHandler("swagger-ui.html")
  54. .addResourceLocations("classpath:/META-INF/resources/");
  55. registry.addResourceHandler("docs.html")
  56. .addResourceLocations("classpath:/META-INF/resources/");
  57. registry.addResourceHandler("doc.html")
  58. .addResourceLocations("classpath:/META-INF/resources/");
  59. registry.addResourceHandler("/webjars/**")
  60. .addResourceLocations("classpath:/META-INF/resources/webjars/");
  61. }
  62. // 使用阿里 FastJson 作为JSON MessageConverter
  63. @Override
  64. public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
  65. for (int i = converters.size() - 1; i >= 0; i--) {
  66. if (converters.get(i) instanceof MappingJackson2HttpMessageConverter) {
  67. converters.remove(i);
  68. }
  69. }
  70. FastJsonHttpMessageConverter fastJsonHttpMessageConverter = new FastJsonHttpMessageConverter();
  71. //自定义fastjson配置
  72. FastJsonConfig config = new FastJsonConfig();
  73. config.setDateFormat("yyyy-MM-dd HH:mm:ss");
  74. config.setSerializerFeatures(
  75. SerializerFeature.WriteMapNullValue, // 是否输出值为null的字段,默认为false,我们将它打开
  76. SerializerFeature.WriteNullListAsEmpty, // 将Collection类型字段的字段空值输出为[]
  77. SerializerFeature.WriteNullStringAsEmpty, // 将字符串类型字段的空值输出为空字符串
  78. SerializerFeature.WriteNullNumberAsZero, // 将数值类型字段的空值输出为0
  79. SerializerFeature.WriteDateUseDateFormat,
  80. SerializerFeature.DisableCircularReferenceDetect // 禁用循环引用
  81. );
  82. SerializeConfig serializeConfig = SerializeConfig.globalInstance;
  83. serializeConfig.put(BigInteger.class, ToStringSerializer.instance);
  84. serializeConfig.put(Long.class, ToStringSerializer.instance);
  85. serializeConfig.put(Long.TYPE, ToStringSerializer.instance);
  86. config.setSerializeConfig(serializeConfig);
  87. fastJsonHttpMessageConverter.setFastJsonConfig(config);
  88. // 添加支持的MediaTypes;不添加时默认为*/*,也就是默认支持全部
  89. // 但是MappingJackson2HttpMessageConverter里面支持的MediaTypes为application/json
  90. // 参考它的做法, fastjson也只添加application/json的MediaType
  91. List<MediaType> fastMediaTypes = new ArrayList<>();
  92. fastMediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
  93. fastJsonHttpMessageConverter.setSupportedMediaTypes(fastMediaTypes);
  94. converters.add(fastJsonHttpMessageConverter);
  95. // 设置项目名称
  96. ProjectConstant.application = parameters.getApplication();
  97. }
  98. // 统一异常处理
  99. @Override
  100. public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
  101. exceptionResolvers.add((request, response, handler, e) -> {
  102. Result result = new Result();
  103. if (e instanceof ServiceException) {// 业务失败的异常,如“账号或密码错误”
  104. result.setCode(ResultCode.FAIL.getCode()).setMessage(e.getMessage());
  105. logger.info(e.getMessage());
  106. } else if (e instanceof NoHandlerFoundException) {
  107. result.setCode(ResultCode.NOT_FOUND.getCode()).setMessage(
  108. "接口 [" + request.getRequestURI() + "] 不存在");
  109. } else if (e instanceof ServletException) {
  110. result.setCode(ResultCode.FAIL.getCode()).setMessage(e.getMessage());
  111. } else {
  112. result.setCode(ResultCode.INTERNAL_SERVER_ERROR.getCode()).setMessage(
  113. "接口 [" + request.getRequestURI() + "] 内部错误,请联系管理员");
  114. String message;
  115. if (handler instanceof HandlerMethod) {
  116. HandlerMethod handlerMethod = (HandlerMethod) handler;
  117. message = String.format("接口 [%s] 出现异常,方法:%s.%s,异常摘要:%s",
  118. request.getRequestURI(), handlerMethod.getBean().getClass().getName(),
  119. handlerMethod.getMethod().getName(), e.getMessage());
  120. } else {
  121. message = e.getMessage();
  122. }
  123. logger.error(message, e);
  124. }
  125. responseResult(response, result);
  126. return new ModelAndView();
  127. });
  128. }
  129. // 解决跨域问题
  130. @Override
  131. public void addCorsMappings(CorsRegistry registry) {
  132. registry.addMapping("/**");
  133. }
  134. // 添加拦截器
  135. @Override
  136. public void addInterceptors(InterceptorRegistry registry) {
  137. // 接口签名认证拦截器,该签名认证比较简单,实际项目中可以使用Json Web Token或其他更好的方式替代。
  138. if ("true".equals(parameters.getSignature())) { // 开发环境忽略签名认证
  139. registry.addInterceptor(new HandlerInterceptorAdapter() {
  140. @Override
  141. public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
  142. Object handler) throws Exception {
  143. // 验证签名
  144. boolean pass = validateSign(request);
  145. if (pass) {
  146. return true;
  147. } else {
  148. logger.warn("签名认证失败,请求接口:{},请求IP:{},请求参数:{}", request.getRequestURI(),
  149. getIpAddress(request), JSON.toJSONString(request.getParameterMap()));
  150. Result result = new Result();
  151. result.setCode(ResultCode.UNAUTHORIZED.getCode()).setMessage("签名认证失败");
  152. responseResult(response, result);
  153. return false;
  154. }
  155. }
  156. });
  157. }
  158. }
  159. private void responseResult(HttpServletResponse response, Result result) {
  160. response.setCharacterEncoding("UTF-8");
  161. response.setHeader("Content-type", "application/json;charset=UTF-8");
  162. response.setStatus(200);
  163. try {
  164. response.getWriter().write(JSON.toJSONString(result));
  165. } catch (IOException ex) {
  166. logger.error(ex.getMessage());
  167. }
  168. }
  169. /**
  170. * 一个简单的登录认证
  171. */
  172. private boolean validateLogin(HttpServletRequest request) {
  173. String userId = request.getParameter("userId");
  174. if (StringUtils.isBlank(userId)) {
  175. return false;
  176. }
  177. CacheKey key = CacheKey.generateKey(CacheType.AppSecretKey, userId);
  178. boolean exists = redisTemplate.hasKey(key.toString());
  179. if (exists) {
  180. return true;
  181. } else {
  182. return false;
  183. }
  184. }
  185. /**
  186. * 一个简单的签名认证,规则: 1. 将请求参数按ascii码排序 2. 拼接为a=value&b=value...这样的字符串(不包含sign)
  187. * 3. 混合密钥(secret)进行md5获得签名,与请求的签名进行比较
  188. */
  189. private boolean validateSign(HttpServletRequest request) {
  190. String requestSign = request.getParameter("sign");// 获得请求签名,如sign=19e907700db7ad91318424a97c54ed57
  191. if (StringUtils.isEmpty(requestSign)) {
  192. return false;
  193. }
  194. List<String> keys = new ArrayList<String>(request.getParameterMap().keySet());
  195. keys.remove("sign");// 排除sign参数
  196. Collections.sort(keys);// 排序
  197. StringBuilder sb = new StringBuilder();
  198. for (String key : keys) {
  199. sb.append(key).append("=").append(request.getParameter(key)).append("&");// 拼接字符串
  200. }
  201. String linkString = sb.toString();
  202. linkString = StringUtils.substring(linkString, 0, linkString.length() - 1);// 去除最后一个'&'
  203. String secret = "Potato";// 密钥,自己修改
  204. String sign = DigestUtils.md5Hex(linkString + secret);// 混合密钥md5
  205. return StringUtils.equals(sign, requestSign);// 比较
  206. }
  207. private String getIpAddress(HttpServletRequest request) {
  208. String ip = request.getHeader("x-forwarded-for");
  209. if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
  210. ip = request.getHeader("Proxy-Client-IP");
  211. }
  212. if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
  213. ip = request.getHeader("WL-Proxy-Client-IP");
  214. }
  215. if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
  216. ip = request.getHeader("HTTP_CLIENT_IP");
  217. }
  218. if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
  219. ip = request.getHeader("HTTP_X_FORWARDED_FOR");
  220. }
  221. if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
  222. ip = request.getRemoteAddr();
  223. }
  224. // 如果是多级代理,那么取第一个ip为客户端ip
  225. if (ip != null && ip.indexOf(",") != -1) {
  226. ip = ip.substring(0, ip.indexOf(",")).trim();
  227. }
  228. return ip;
  229. }
  230. }