123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252 |
- package com.txz.operating.configurer;
- import com.alibaba.fastjson.JSON;
- import com.alibaba.fastjson.serializer.SerializeConfig;
- import com.alibaba.fastjson.serializer.SerializerFeature;
- import com.alibaba.fastjson.serializer.ToStringSerializer;
- import com.alibaba.fastjson.support.config.FastJsonConfig;
- import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
- import com.txz.operating.core.ProjectConstant;
- import com.txz.operating.core.ResultCode;
- import com.txz.operating.core.ServiceException;
- import com.txz.operating.core.cache.CacheKey;
- import com.txz.operating.core.cache.CacheType;
- import com.txz.operating.result.Result;
- import org.apache.commons.codec.digest.DigestUtils;
- import org.apache.commons.lang3.StringUtils;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import org.springframework.context.annotation.Configuration;
- import org.springframework.data.redis.core.RedisTemplate;
- import org.springframework.http.MediaType;
- import org.springframework.http.converter.HttpMessageConverter;
- import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
- 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.ResourceHandlerRegistry;
- import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
- import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
- import javax.annotation.Resource;
- import javax.servlet.ServletException;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import java.io.IOException;
- import java.math.BigInteger;
- import java.util.ArrayList;
- import java.util.Collections;
- import java.util.List;
- /**
- * Spring MVC 配置
- */
- @Configuration
- public class MyWebMvcConfigurer implements WebMvcConfigurer {
- private final Logger logger = LoggerFactory.getLogger(MyWebMvcConfigurer.class);
- @Resource
- private Parameters parameters;
- @Resource
- private RedisTemplate redisTemplate;
- @Override
- public void addResourceHandlers(ResourceHandlerRegistry registry) {
- registry.addResourceHandler("swagger-ui.html")
- .addResourceLocations("classpath:/META-INF/resources/");
- registry.addResourceHandler("docs.html")
- .addResourceLocations("classpath:/META-INF/resources/");
- registry.addResourceHandler("doc.html")
- .addResourceLocations("classpath:/META-INF/resources/");
- registry.addResourceHandler("/webjars/**")
- .addResourceLocations("classpath:/META-INF/resources/webjars/");
- }
- // 使用阿里 FastJson 作为JSON MessageConverter
- @Override
- public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
- for (int i = converters.size() - 1; i >= 0; i--) {
- if (converters.get(i) instanceof MappingJackson2HttpMessageConverter) {
- converters.remove(i);
- }
- }
- FastJsonHttpMessageConverter fastJsonHttpMessageConverter = new FastJsonHttpMessageConverter();
- //自定义fastjson配置
- FastJsonConfig config = new FastJsonConfig();
- config.setDateFormat("yyyy-MM-dd HH:mm:ss");
- config.setSerializerFeatures(
- SerializerFeature.WriteMapNullValue, // 是否输出值为null的字段,默认为false,我们将它打开
- SerializerFeature.WriteNullListAsEmpty, // 将Collection类型字段的字段空值输出为[]
- SerializerFeature.WriteNullStringAsEmpty, // 将字符串类型字段的空值输出为空字符串
- SerializerFeature.WriteNullNumberAsZero, // 将数值类型字段的空值输出为0
- SerializerFeature.WriteDateUseDateFormat,
- SerializerFeature.DisableCircularReferenceDetect // 禁用循环引用
- );
- SerializeConfig serializeConfig = SerializeConfig.globalInstance;
- serializeConfig.put(BigInteger.class, ToStringSerializer.instance);
- serializeConfig.put(Long.class, ToStringSerializer.instance);
- serializeConfig.put(Long.TYPE, ToStringSerializer.instance);
- config.setSerializeConfig(serializeConfig);
- fastJsonHttpMessageConverter.setFastJsonConfig(config);
- // 添加支持的MediaTypes;不添加时默认为*/*,也就是默认支持全部
- // 但是MappingJackson2HttpMessageConverter里面支持的MediaTypes为application/json
- // 参考它的做法, fastjson也只添加application/json的MediaType
- List<MediaType> fastMediaTypes = new ArrayList<>();
- fastMediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
- fastJsonHttpMessageConverter.setSupportedMediaTypes(fastMediaTypes);
- converters.add(fastJsonHttpMessageConverter);
- // 设置项目名称
- ProjectConstant.application = parameters.getApplication();
- }
- // 统一异常处理
- @Override
- public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
- exceptionResolvers.add((request, response, handler, e) -> {
- Result result = new Result();
- if (e instanceof ServiceException) {// 业务失败的异常,如“账号或密码错误”
- result.setCode(ResultCode.FAIL.getCode()).setMessage(e.getMessage());
- logger.info(e.getMessage());
- } else if (e instanceof NoHandlerFoundException) {
- result.setCode(ResultCode.NOT_FOUND.getCode()).setMessage(
- "接口 [" + request.getRequestURI() + "] 不存在");
- } else if (e instanceof ServletException) {
- result.setCode(ResultCode.FAIL.getCode()).setMessage(e.getMessage());
- } else {
- result.setCode(ResultCode.INTERNAL_SERVER_ERROR.getCode()).setMessage(
- "接口 [" + request.getRequestURI() + "] 内部错误,请联系管理员");
- String message;
- if (handler instanceof HandlerMethod) {
- HandlerMethod handlerMethod = (HandlerMethod) handler;
- message = String.format("接口 [%s] 出现异常,方法:%s.%s,异常摘要:%s",
- request.getRequestURI(), handlerMethod.getBean().getClass().getName(),
- handlerMethod.getMethod().getName(), e.getMessage());
- } else {
- message = e.getMessage();
- }
- logger.error(message, 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 ("true".equals(parameters.getSignature())) { // 开发环境忽略签名认证
- registry.addInterceptor(new HandlerInterceptorAdapter() {
- @Override
- public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
- Object handler) throws Exception {
- // 验证签名
- boolean pass = validateSign(request);
- if (pass) {
- return true;
- } else {
- logger.warn("签名认证失败,请求接口:{},请求IP:{},请求参数:{}", request.getRequestURI(),
- getIpAddress(request), JSON.toJSONString(request.getParameterMap()));
- Result result = new Result();
- result.setCode(ResultCode.UNAUTHORIZED.getCode()).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());
- }
- }
- /**
- * 一个简单的登录认证
- */
- private boolean validateLogin(HttpServletRequest request) {
- String userId = request.getParameter("userId");
- if (StringUtils.isBlank(userId)) {
- return false;
- }
- CacheKey key = CacheKey.generateKey(CacheType.AppSecretKey, userId);
- boolean exists = redisTemplate.hasKey(key.toString());
- if (exists) {
- return true;
- } else {
- return false;
- }
- }
- /**
- * 一个简单的签名认证,规则: 1. 将请求参数按ascii码排序 2. 拼接为a=value&b=value...这样的字符串(不包含sign)
- * 3. 混合密钥(secret)进行md5获得签名,与请求的签名进行比较
- */
- private boolean validateSign(HttpServletRequest request) {
- String requestSign = request.getParameter("sign");// 获得请求签名,如sign=19e907700db7ad91318424a97c54ed57
- if (StringUtils.isEmpty(requestSign)) {
- return false;
- }
- List<String> keys = new ArrayList<String>(request.getParameterMap().keySet());
- keys.remove("sign");// 排除sign参数
- Collections.sort(keys);// 排序
- StringBuilder sb = new StringBuilder();
- for (String key : keys) {
- sb.append(key).append("=").append(request.getParameter(key)).append("&");// 拼接字符串
- }
- String linkString = sb.toString();
- linkString = StringUtils.substring(linkString, 0, linkString.length() - 1);// 去除最后一个'&'
- String secret = "Potato";// 密钥,自己修改
- String sign = DigestUtils.md5Hex(linkString + secret);// 混合密钥md5
- 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;
- }
- }
|