jeecgboot访问签名修改

1、增加过滤器, copy一个requet流(附件上传不做签名认证,仍然用之前的流)


import org.springframework.stereotype.Component;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

@Component
public class MyFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {}

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
        throws IOException, ServletException, IOException {
        ServletRequest requestWrapper = null;
        String header = ((HttpServletRequest)servletRequest).getHeader("Content-Type");
        if (header != null && "multipart/form-data".equals(header.split(";")[0])) {
            System.out.println("附件类型");
            filterChain.doFilter(servletRequest, servletResponse);
        } else {
            requestWrapper = new MyHttpServletRequestWrapper((HttpServletRequest)servletRequest);
            filterChain.doFilter(requestWrapper, servletResponse);
        }


    }

    @Override
    public void destroy() {}
}

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;

public class MyHttpServletRequestWrapper extends HttpServletRequestWrapper {

    private final String body;

    public MyHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        InputStream is = null;
        StringBuilder sb = null;
        try {
            is = request.getInputStream();
            sb = new StringBuilder();
            String content="";
            BufferedReader bufferedReader =new BufferedReader(new InputStreamReader(is, "utf-8"));
            while(( content = bufferedReader.readLine()) !=null){
                sb.append(content);
            }
        } finally {
            if (is != null) {
                is.close();
            }
        }
        body = sb.toString();
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        final ByteArrayInputStream bais = new ByteArrayInputStream(body.getBytes());
        return new ServletInputStream() {

            @Override
            public boolean isFinished() {
                return false;
            }

            @Override
            public boolean isReady() {
                return false;
            }

            @Override
            public void setReadListener(ReadListener readListener) {

            }

            @Override
            public int read() throws IOException {

                return bais.read();

            }

            /*@Override
            public void close() throws IOException {
                System.out.println(1);
                bais.close();
            }*/
        };
    }

    public String getBody() {
        return body;
    }
}

2 签名拦截器配置
配置几个未登录前的接口不做签名认证

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; 
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * 签名 拦截器配置
 */
@Configuration
public class SignAuthConfiguration implements WebMvcConfigurer {
    public static String[] urlList = new String[] {"/sys/dict/getDictItems/*", "/sys/dict/loadDict/*",
            "/sys/dict/loadDictOrderByValue/*", "/sys/dict/loadDictItem/*", "/sys/dict/loadTreeData",
            "/sys/api/queryTableDictItemsByCode", "/sys/api/queryFilterTableDictInfo", "/sys/api/queryTableDictByKeys",
            "/sys/api/translateDictFromTable", "/sys/api/translateDictFromTableByKeys"};
   public static String[] excludeList = new String[]{"/sys/logout","/sys/phoneLogin","/sys/phoneLogin","/sys/login",
           "/account/sms","/auth/forge-password","/auth/register","/sys/common/upload","/sys/common/static/**","/sys/mLogin"};
    @Bean
    public SignAuthInterceptor signAuthInterceptor() {
        return new SignAuthInterceptor();
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {

        //registry.addInterceptor(signAuthInterceptor()).addPathPatterns(urlList);
        registry.addInterceptor(signAuthInterceptor()).addPathPatterns("/**").excludePathPatterns(excludeList);
    }
}

附件上传、调用远程文件链接等不做签名认证


import java.io.PrintWriter;
import java.util.SortedMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;

import org.jeecg.common.api.vo.Result;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.util.DateUtils;
import org.jeecg.common.util.oConvertUtils; 
import org.jeecg.config.sign.util.HttpUtils;
import org.jeecg.config.sign.util.SignUtil;
import org.springframework.web.servlet.HandlerInterceptor;

import com.alibaba.fastjson.JSON;

import lombok.extern.slf4j.Slf4j;

/**
 * 签名拦截器
 * @author qinfeng
 */
@Slf4j
public class SignAuthInterceptor implements HandlerInterceptor {
    /**
     * 5分钟有效期
     */
    private final static long MAX_EXPIRE = 5 * 60;

    
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
          String method = request.getMethod();
          log.error("request URI = " + request.getRequestURI());
          String header = ((HttpServletRequest)request).getHeader("Content-Type");
          if (header != null && "multipart/form-data".equals(header.split(";")[0])) {
            //文件上传不做签名认证
              return true;
          }
          //直接链接,如打开pdf文件等
          String uri = request.getRequestURI();
          if(uri.lastIndexOf("/")>-1) {
              String fileName = uri.substring(uri.lastIndexOf("/")+1) ;
              if(fileName.indexOf(".")>-1){
                  Pattern pattern = Pattern.compile("^[\\w\\s\\.\\-]+(?<!\\.\\.)$");
                  Matcher matcher = pattern.matcher(fileName);
                  if (matcher.matches()) {
                      return true;
                  }
              }
          }
          //图片
          if(uri.indexOf("data:image")>-1){
              return true ;
          }

        HttpServletRequest requestWrapper = new HttpServletRequestWrapper(request);
        //获取全部参数(包括URL和body上的)
        SortedMap<String, String> allParams = HttpUtils.getAllParams(requestWrapper);
        //对参数进行签名验证
        String headerSign = request.getHeader(CommonConstant.X_SIGN);
        String timesTamp = request.getHeader(CommonConstant.X_TIMESTAMP);

        if (oConvertUtils.isEmpty(headerSign)) {
            headerSign = request.getParameter(CommonConstant.X_SIGN);
        }
        if (oConvertUtils.isEmpty(timesTamp)) {
            timesTamp = request.getParameter(CommonConstant.X_TIMESTAMP);
        }

        //1.校验时间有消息
        try {
            DateUtils.parseDate(timesTamp, "yyyyMMddHHmmss");
        } catch (Exception e) {
            throw new IllegalArgumentException("签名验证失败:X-TIMESTAMP格式必须为:yyyyMMddHHmmss");
        }
        Long clientTimestamp = Long.parseLong(timesTamp);
        //判断时间戳 timestamp=201808091113
        if ((DateUtils.getCurrentTimestamp() - clientTimestamp) > MAX_EXPIRE) {
            throw new IllegalArgumentException("签名验证失败:X-TIMESTAMP已过期");
        }

        //2.校验签名
        boolean isSigned =   SignUtil.verifySign(allParams,headerSign);

        if (isSigned) {
            log.debug("Sign 签名通过!Header Sign : {}",headerSign);
            return true;
        } else {
            log.error("request URI = " + request.getRequestURI());
            log.error("Sign 签名校验失败!Header Sign : {}",headerSign);
            //校验失败返回前端
            response.setCharacterEncoding("UTF-8");
            response.setContentType("application/json; charset=utf-8");
            PrintWriter out = response.getWriter();
            Result<?> result = Result.error("Sign签名校验失败!");
            out.print(JSON.toJSON(result));
            return false;
        }
    }

}

支持提交null数据

import com.alibaba.fastjson.JSONObject;

import com.alibaba.fastjson.serializer.SerializerFeature;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.exception.JeecgBootException;
import org.jeecg.common.util.SpringContextUtils;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.config.StaticConfig;
import org.springframework.util.DigestUtils;
import org.springframework.util.StringUtils;


import java.util.*;


/**
 * 签名工具类
 *
 * @author jeecg
 * @date 20210621
 */
@Slf4j
public class SignUtil {
    public static final String xPathVariable = "x-path-variable";

    /**
     * @param params
     *            所有的请求参数都会在这里进行排序加密
     * @return 验证签名结果
     */
    public static boolean verifySign(SortedMap<String, String> params,String headerSign) {
        if (params == null || StringUtils.isEmpty(headerSign)) {
            return false;
        }
        // 把参数加密
        String paramsSign = getParamsSign(params);
        log.error("Param Sign : {}", paramsSign);
        boolean result =  !StringUtils.isEmpty(paramsSign) && headerSign.equals(paramsSign);
        if(!result){
            log.error("参数签名:"+paramsSign+ " 传递的签名:"+headerSign);
        }
        return result ;
    }

    /**
     * @param params
     *            所有的请求参数都会在这里进行排序加密
     * @return 得到签名
     */
    public static String getParamsSign(SortedMap<String, String> params) {
        //去掉 Url 里的时间戳
        params.remove("_t");
        String paramsJsonStr = JSONObject.toJSONString(params, SerializerFeature.WriteMapNullValue);
        log.error("Param paramsJsonStr : {}", paramsJsonStr);
        StaticConfig staticConfig = SpringContextUtils.getBean(StaticConfig.class);
        String signatureSecret = staticConfig.getSignatureSecret();
        if(oConvertUtils.isEmpty(signatureSecret) || signatureSecret.contains("${")){
            throw new JeecgBootException("签名密钥 ${jeecg.signatureSecret} 缺少配置 !!");
        }
        return DigestUtils.md5DigestAsHex((paramsJsonStr + signatureSecret).getBytes()).toUpperCase();
    }
}

字符转换null转换’null’ bug问题

public class HttpUtils {

 public static SortedMap<String, String> getAllParams(HttpServletRequest request) throws IOException {

        SortedMap<String, String> result = new TreeMap<>();
        // 获取URL上最后带逗号的参数变量 sys/dict/getDictItems/sys_user,realname,username
        String pathVariable = request.getRequestURI().substring(request.getRequestURI().lastIndexOf("/") + 1);
        if (pathVariable.contains(",")) {
            log.info(" pathVariable: {}",pathVariable);
            String deString = URLDecoder.decode(pathVariable, "UTF-8");
            log.info(" pathVariable decode: {}",deString);
            result.put(SignUtil.xPathVariable, deString);
        }
        // 获取URL上的参数
        Map<String, String> urlParams = getUrlParams(request);
        for (Map.Entry entry : urlParams.entrySet()) {
            result.put((String)entry.getKey(), (String)entry.getValue());
        }
        Map<String, String> allRequestParam = new HashMap<>(16);
        // get请求不需要拿body参数
        if (!HttpMethod.GET.name().equals(request.getMethod())) {
            allRequestParam = getAllRequestParam(request);
        }
        // 将URL的参数和body参数进行合并
        if (allRequestParam != null) {
            for (Map.Entry entry : allRequestParam.entrySet()) {
            //空数据不做字符转换
                result.put((String)entry.getKey(), entry.getValue()==null?null:String.valueOf(entry.getValue()));
            }
        }
        return result;
    }
    ...
}

前端修改
1、增删修查 增加签名
2、查询时去掉null值(因为get方法提交url自动会去掉为空的参数),再签名。解决带null签名与后台签名不一致问题。

//post
export function postAction(url,parameter) {
  let sign = signMd5Utils.getSign(url, parameter);
  //将签名和时间戳,添加在请求接口 Header
  let signHeader = {"X-Sign": sign,"X-TIMESTAMP": signMd5Utils.getDateTimeToString()};

  return axios({
    url: url,
    method:'post' ,
    data: parameter,
    headers: signHeader
  })
}

//post method= {post | put}
export function httpAction(url,parameter,method) {
  let sign = signMd5Utils.getSign(url, parameter);
  //将签名和时间戳,添加在请求接口 Header
  let signHeader = {"X-Sign": sign,"X-TIMESTAMP": signMd5Utils.getDateTimeToString()};

  return axios({
    url: url,
    method:method ,
    data: parameter,
    headers: signHeader
  })
}

//put
export function putAction(url,parameter) {
  let sign = signMd5Utils.getSign(url, parameter);
  //将签名和时间戳,添加在请求接口 Header
  let signHeader = {"X-Sign": sign,"X-TIMESTAMP": signMd5Utils.getDateTimeToString()};
  return axios({
    url: url,
    method:'put',
    data: parameter,
    headers: signHeader
  })
}

//get
export function getAction(url,parameter) {
  //此方法get请求去掉了null,但签名用了null数据
  //解决此办法,在签名前去掉null数据
   let newParameter = {}
  for (let key in parameter) {
    if (parameter[key] !== null) {
      newParameter[key] = parameter[key]
    }
  }
  let sign = signMd5Utils.getSign(url, newParameter);
  //将签名和时间戳,添加在请求接口 Header
  let signHeader = {"X-Sign": sign,"X-TIMESTAMP": signMd5Utils.getDateTimeToString()};

  return axios({
    url: url,
    method: 'get',
    params: newParameter,
    headers: signHeader
  })
}

//deleteAction
export function deleteAction(url,parameter) {
  let sign = signMd5Utils.getSign(url, parameter);
  //将签名和时间戳,添加在请求接口 Header
  let signHeader = {"X-Sign": sign,"X-TIMESTAMP": signMd5Utils.getDateTimeToString()};
  return axios({
    url: url,
    method: 'delete',
    params: parameter,
    headers: signHeader
  })
}
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值