智慧保安后台管理-外网
Administrator
2022-03-11 064573a5c5117b6cbe200a33b062a783189019d1
拦截时做签名校验,校验通过则放行,签名采用 md5 方式加密
6 files modified
4 files added
533 ■■■■■ changed files
src/main/java/org/springblade/common/utils/HttpClientUtils.java 4 ●●●● patch | view | raw | blame | history
src/main/java/org/springblade/common/utils/Md5SignUtil.java 44 ●●●●● patch | view | raw | blame | history
src/main/java/org/springblade/common/utils/RSAUtils.java 215 ●●●●● patch | view | raw | blame | history
src/main/java/org/springblade/modules/attendance/controller/AttendanceController.java 21 ●●●●● patch | view | raw | blame | history
src/main/java/org/springblade/modules/exam/service/impl/ExamScoreServiceImpl.java 5 ●●●●● patch | view | raw | blame | history
src/main/java/org/springblade/modules/signature/entity/BaseSign.java 1 ●●●● patch | view | raw | blame | history
src/main/java/org/springblade/modules/signature/interceptor/ExternalAdapter.java 40 ●●●●● patch | view | raw | blame | history
src/main/java/org/springblade/modules/signature/interceptor/ExternalInterceptor.java 50 ●●●●● patch | view | raw | blame | history
src/main/java/org/springblade/modules/signature/util/SignUtil.java 144 ●●●●● patch | view | raw | blame | history
src/main/java/org/springblade/modules/signature/util/SignUtils.java 9 ●●●●● patch | view | raw | blame | history
src/main/java/org/springblade/common/utils/HttpClientUtils.java
@@ -391,7 +391,7 @@
    }
    //适用于post请求并传送form-data数据(同样适用于post的Raw类型的application-json格式)
    public static String postParams(String url, String appKey, String appKeyValue, Map<String, String> map) {
    public static String postParams(String url, String appKey, Map<String, String> map) {
        SSLContext sslcontext = null;
        try {
            sslcontext = createIgnoreVerifySSL();
@@ -412,7 +412,7 @@
//创建自定义的httpclient对象
        CloseableHttpClient client = HttpClients.custom().setConnectionManager(connManager).build();
        HttpPost post = new HttpPost(url);
        post.setHeader(appKey, appKeyValue);
        post.setHeader("appKey", appKey);
        CloseableHttpResponse res = null;
        try {
            List<NameValuePair> nvps = new ArrayList<NameValuePair>();
src/main/java/org/springblade/common/utils/Md5SignUtil.java
@@ -5,6 +5,7 @@
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.security.MessageDigest;
import java.util.Arrays;
import java.util.Map;
@@ -51,4 +52,47 @@
        return sign.toString();
    }
    /***
     * MD5加码 生成32位md5码
     */
    public static String string2MD5(String inStr) {
        MessageDigest md5 = null;
        try {
            md5 = MessageDigest.getInstance("MD5");
        } catch (Exception e) {
            e.printStackTrace();
            return "";
        }
        char[] charArray = inStr.toCharArray();
        byte[] byteArray = new byte[charArray.length];
        for (int i = 0; i < charArray.length; i++) {
            byteArray[i] = (byte) charArray[i];
        }
        byte[] md5Bytes = md5.digest(byteArray);
        StringBuffer hexValue = new StringBuffer();
        for (int i = 0; i < md5Bytes.length; i++) {
            int val = ((int) md5Bytes[i]) & 0xff;
            if (val < 16) {
                hexValue.append("0");
            }
            hexValue.append(Integer.toHexString(val));
        }
        return hexValue.toString();
    }
    /**
     * 加密解密算法 执行一次加密,两次解密
     */
    public static String convertMD5(String inStr) {
        char[] a = inStr.toCharArray();
        for (int i = 0; i < a.length; i++) {
            a[i] = (char) (a[i] ^ 't');
        }
        String s = new String(a);
        return s;
    }
}
src/main/java/org/springblade/common/utils/RSAUtils.java
New file
@@ -0,0 +1,215 @@
package org.springblade.common.utils;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.security.InvalidParameterException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.SecureRandom;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import javax.crypto.Cipher;
import org.apache.commons.lang3.StringUtils;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.encoders.Hex;
/**
 * RSA算法加密/解密工具类。
 * 以下代码可以使用,唯一需要注意的是: org.bouncycastle...这个jar包需要找一到放到项目中,RSA所需jar包在java中已经自带了.
 *
 * @author liuyan
 */
public class RSAUtils {
    /**
     * 算法名称
     */
    private static final String ALGORITHOM = "RSA";
    /**
     * 密钥大小
     */
    private static final int KEY_SIZE = 1024;
    /**
     * 默认的安全服务提供者
     */
    private static final Provider DEFAULT_PROVIDER = new BouncyCastleProvider();
    private static KeyPairGenerator keyPairGen = null;
    private static KeyFactory keyFactory = null;
    /**
     * 缓存的密钥对。
     */
    private static KeyPair oneKeyPair = null;
    //密文种子, 当想更换RSA钥匙的时候,只需要修改密文种子,即可更换
    private static final String radamKey = "你自己随便写上数字或者英文即可";
    //类加载后进行初始化数据
    static {
        try {
            keyPairGen = KeyPairGenerator.getInstance(ALGORITHOM,
                    DEFAULT_PROVIDER);
            keyFactory = KeyFactory.getInstance(ALGORITHOM, DEFAULT_PROVIDER);
        } catch (NoSuchAlgorithmException ex) {
            ex.printStackTrace();
        }
    }
    /**
     * 根据指定的密文种子,生成并返回RSA密钥对。
     */
    private static synchronized KeyPair generateKeyPair() {
        try {
            keyPairGen.initialize(KEY_SIZE,
                    new SecureRandom(radamKey.getBytes()));
            oneKeyPair = keyPairGen.generateKeyPair();
            return oneKeyPair;
        } catch (InvalidParameterException ex) {
            ex.printStackTrace();
        } catch (NullPointerException ex) {
            ex.printStackTrace();
        }
        return null;
    }
    /**
     * 返回初始化时默认的公钥。
     */
    public static RSAPublicKey getDefaultPublicKey() {
        KeyPair keyPair = generateKeyPair();
        if (keyPair != null) {
            return (RSAPublicKey) keyPair.getPublic();
        }
        return null;
    }
    /**
     * 使用指定的私钥解密数据。
     *
     * @param privateKey 给定的私钥。
     * @param data       要解密的数据。
     * @return 原数据。
     */
    public static byte[] decrypt(PrivateKey privateKey, byte[] data) throws Exception {
        Cipher ci = Cipher.getInstance(ALGORITHOM, DEFAULT_PROVIDER);
        ci.init(Cipher.DECRYPT_MODE, privateKey);
        return ci.doFinal(data);
    }
    /**
     * 使用默认的私钥解密给定的字符串。
     * @param encryptText 密文。
     * @return 原文字符串。
     */
    public static String decryptString(String encryptText) {
        if (StringUtils.isBlank(encryptText)) {
            return null;
        }
        KeyPair keyPair = generateKeyPair();
        try {
            byte[] en_data = Hex.decode(encryptText);
            byte[] data = decrypt((RSAPrivateKey) keyPair.getPrivate(), en_data);
            return new String(data);
        } catch (NullPointerException ex) {
            ex.printStackTrace();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return null;
    }
    /**
     * 使用秘钥 - 对js端传递过来密文进行解密
     *
     * @param encryptText 密文。
     * @return {@code encryptText} 的原文字符串。
     */
    public static String decryptStringByJs(String encryptText) {
        String text = decryptString(encryptText);
        if (text == null) {
            return null;
        }
        String reverse = StringUtils.reverse(text);
        String decode = null;
        try {
            //这里需要进行编码转换.注:在前端js对明文加密前需要先进行转码-可自行百度"编码转换"
            decode = URLDecoder.decode(reverse, "UTF-8");
            System.out.println("解密后文字:" + decode);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return decode;
    }
    //java端 - 使用公钥进行加密
    public static byte[] encrypt(String plaintext) throws Exception {
        // 获取公钥及参数e,n
        RSAPublicKey publicKey = RSAUtils.getDefaultPublicKey();
        //获取公钥指数 e
        BigInteger e = publicKey.getPublicExponent();
        //获取公钥系数 n
        BigInteger n = publicKey.getModulus();
        //先将明文进行编码
        String encode = URLEncoder.encode(plaintext);
        // 获取明文字节数组 m
        BigInteger m = new BigInteger(encode.getBytes());
        // 进行明文加密 c
        BigInteger c = m.modPow(e, n);
        //返回密文字节数组
        return c.toByteArray();
    }
    //java端 - 使用私钥进行解密
    public static String decrypt(byte[] cipherText) throws Exception {
        // 读取私钥
        KeyPair keyPair = generateKeyPair();
        RSAPrivateKey prk = (RSAPrivateKey) keyPair.getPrivate();
        // 获取私钥参数-指数/系数
        BigInteger d = prk.getPrivateExponent();
        BigInteger n = prk.getModulus();
        // 读取密文
        BigInteger c = new BigInteger(cipherText);
        // 进行解密
        BigInteger m = c.modPow(d, n);
        // 解密结果-字节数组
        byte[] mt = m.toByteArray();
        //转成String,此时是乱码
        String en = new String(mt);
        //再进行编码
        String result = java.net.URLDecoder.decode(en, "UTF-8");
        //最后返回解密后得到的明文
        return result;
    }
    public static void main(String[] args) {
        /*解密js端传递过来的密文*/
        //获取公钥对象--注意:前端那边需要用到公钥系数和指数
        RSAPublicKey publicKey = RSAUtils.getDefaultPublicKey();
        //公钥-系数(n)
        System.out.println("public key modulus:" + new String(Hex.encode(publicKey.getModulus().toByteArray())));
        //公钥-指数(e1)
        System.out.println("public key exponent:" + new String(Hex.encode(publicKey.getPublicExponent().toByteArray())));
        //JS加密后的字符串
        String param = "8c49dfb92a0c8d15b0187b1eabc343a80f340962ebecc497205f83f6a4792141d4e711b6d439388ecf691df4eda2cae1e6431573b61e3f564ffe1c757e32f3e846b5983e5939ddeb28c9570001d7f208ffbaa069677d363cc73e4c78c0d508e8b0b9f6205473269bbfbc22e7fb9413be4c449520eb6cfb4fbbff4e7e189a005a";
        //解密后的字符串
        String param1 = RSAUtils.decryptStringByJs(param);
        /*后端的加密和解密-在加密的方法中已经使用了公钥指数和系数*/
        try {
            //加密
            byte[] param3 = encrypt("你好> @# !!# #");
            //解密
            String decrypt = RSAUtils.decrypt(param3);
            System.out.println(decrypt);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
src/main/java/org/springblade/modules/attendance/controller/AttendanceController.java
@@ -9,6 +9,7 @@
import lombok.AllArgsConstructor;
import org.springblade.core.boot.ctrl.BladeController;
import org.springblade.core.excel.util.ExcelUtil;
import org.springblade.core.log.exception.ServiceException;
import org.springblade.core.mp.support.Condition;
import org.springblade.core.mp.support.Query;
import org.springblade.core.tool.api.R;
@@ -44,8 +45,6 @@
public class AttendanceController extends BladeController {
    private final IAttendanceService attendanceService;
//    private final IEnclosureService iEnclosureService;
    private final IDictBizService iDictBizService;
@@ -83,24 +82,6 @@
        IPage<AttendanceVO> pages = attendanceService.selectAttendancePage(Condition.getPage(query), attendance);
        return R.data(pages);
    }
//    /**
//     * 保安位置比对
//     */
//    @GetMapping("/positionThan")
//    public R positionThan(@Valid Attendance attendance, HttpServletResponse response) {
//        response.setHeader("Access-Control-Allow-Origin", "*");
//        response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
//        response.setHeader("Access-Control-Allow-Credentials", "true");
//        Enclosure enclosure = new Enclosure();
//        enclosure.setAnumber(attendance.getNumber());
//        //查询当前处警人员的电子围栏信息
//        Boolean onArea = iEnclosureService.isOnArea(attendance.getJd(),attendance.getWd(),enclosure);
//        if (onArea){
//            return R.data(true);
//        }
//        return R.data(false);
//    }
    /**
src/main/java/org/springblade/modules/exam/service/impl/ExamScoreServiceImpl.java
@@ -187,7 +187,7 @@
                myAsyncService.FTP(s);
            }
            //模拟考试
            //培训考试
            if (paper.getExamType() == 2) {
                TrainingRegistration trainingRegistration = new TrainingRegistration();
                trainingRegistration.setCandidateNo(examScore.getCandidateNo());
@@ -210,7 +210,8 @@
                //FtpUtil.sqlFileUpload(s);
                myAsyncService.FTP(s);
            }
            //设置提交时间
            examScore.setExamEndTime(new Date());
            //设置考试ID
            examScore.setExamId(examScore.getPapersId().toString());
            //设置理论得分
src/main/java/org/springblade/modules/signature/entity/BaseSign.java
@@ -8,7 +8,6 @@
/**
 * 第三方接口对接密钥
 */
@SuppressWarnings("deprecation")
@Data
public class BaseSign implements Serializable{
src/main/java/org/springblade/modules/signature/interceptor/ExternalAdapter.java
New file
@@ -0,0 +1,40 @@
package org.springblade.modules.signature.interceptor;
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.WebMvcConfigurationSupport;
/**
 * 拦截器
 * @author zhongrj
 * @time 2022-03-10
 */
@Configuration
public class ExternalAdapter extends WebMvcConfigurationSupport {
    /**
     * 新增拦截
     * @param registry
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new ExternalInterceptor()).
            addPathPatterns("/simulateExamRecord/getSimulateExamRecordInfo",
                            "/simulateExamRecord/pauseExam",
                            "/simulateExamRecord/getSimulateExamRefreshInfo",
                            "/simulateExamRecord/beginExam",
                            "/simulateExamRecord/saveSimulateExam",
                            "/examSubjectChoices/getSubjectResultInfoBySimulate",
                            "/blade-user/getNotUpdatePwdInfo",
                            "/electronRail/**",
                            "/attendance/AppSave"
                );
        super.addInterceptors(registry);
    }
    @Bean
    public ExternalInterceptor getExternalInterceptor(){
        return new ExternalInterceptor();
    }
}
src/main/java/org/springblade/modules/signature/interceptor/ExternalInterceptor.java
New file
@@ -0,0 +1,50 @@
package org.springblade.modules.signature.interceptor;
import org.springblade.modules.signature.util.SignUtil;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
 * 拦截器
 * @author zhongrj
 * @time 2022-3-10
 */
@Component
public class ExternalInterceptor implements HandlerInterceptor {
    /**
     * 拦截所有的设备查询操作,在此之前做更新设备状态的操作
     * @param request 请求域
     * @param response 响应域
     * @param handler 处理器
     * @return
     * @throws Exception 异常
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//        String acc = "454sd5fa45dsa5fdaf54";
//        //校验签名
//        Boolean aBoolean = SignUtil.checkSign(request, acc);
//        if (aBoolean){
//            System.out.println("签名校验通过,放行");
//            //放行
//            return true;
//        }
//        //拒绝访问
//        return false;
        return true;
    }
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    }
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    }
}
src/main/java/org/springblade/modules/signature/util/SignUtil.java
New file
@@ -0,0 +1,144 @@
package org.springblade.modules.signature.util;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springblade.common.utils.HttpClientUtils;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.GeneralSecurityException;
import java.security.MessageDigest;
import java.util.*;
/**
 * sign 生成工具类
 * @author zhongrj
 * @since 2022-03-11
 */
public class SignUtil {
    private static Logger LOGGER = LoggerFactory.getLogger(SignUtil.class);
    /**
     * 获取签名sign
     * @param params 参数集合不含appSecret
     * @param secret 验证接口的appSecret
     * @return
     */
    public static String getSign(Map params,String secret)
    {
        String sign="";
        StringBuilder sb = new StringBuilder();
        //1. 先对请求参数排序
        Set<String> keyset=params.keySet();
        TreeSet<String> sortSet=new TreeSet<>();
        sortSet.addAll(keyset);
        Iterator<String> it=sortSet.iterator();
        //2. 把参数的key value链接起来 ,再把appSecret拼接参数字符串的最后面,得到要加密的字符串
        while(it.hasNext())
        {
            String key=it.next();
            String value=params.get(key).toString();
            sb.append(key).append(value);
        }
        sb.append(secret);
        byte[] md5Digest;
        try {
            //3. Md5加密
            md5Digest = getMD5Digest(sb.toString());
            //4.得到sign
            sign = byte2hex(md5Digest);
        } catch (IOException e) {
            LOGGER.error("生成签名错误",e);
        }
        // 5. 返回 sign
        return sign;
    }
    /**
     * 回调接口验证签名方法
     * @param request
     * @return
     */
    public static Boolean checkSign(HttpServletRequest request,String appSecret){
        Boolean flag= false;
        String sign = request.getParameter("sign");//签名
        if(StringUtils.isEmpty(sign)){
            return false;
        }
        //检查sign是否过期
        Enumeration<?> pNames =  request.getParameterNames();
        Map<String, String> params = new HashMap<>();
        while (pNames.hasMoreElements()) {
            String pName = (String) pNames.nextElement();
            if("sign".equals(pName)) {
                continue;
            }
            String pValue = request.getParameter(pName);
            params.put(pName, pValue);
        }
        if(sign.equals(getSign(params, appSecret))){
            flag = true;
        }
        return flag;
    }
    /**
     * MD5加密数据方法
     */
    private static byte[] getMD5Digest(String data) throws IOException {
        byte[] bytes = null;
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            bytes = md.digest(data.getBytes("UTF-8"));
        } catch (GeneralSecurityException gse) {
            throw new IOException(gse);
        }
        return bytes;
    }
    /**
     * MD5加密数据后的数据处理方法
     */
    private static String byte2hex(byte[] bytes) {
        StringBuilder sign = new StringBuilder();
        for (int i = 0; i < bytes.length; i++) {
            String hex = Integer.toHexString(bytes[i] & 0xFF);
            if (hex.length() == 1) {
                sign.append("0");
            }
            sign.append(hex.toUpperCase());
        }
        return sign.toString();
    }
    public static String utf8Encoding(String value, String sourceCharsetName) {
        try {
            return new String(value.getBytes(sourceCharsetName), "UTF-8");
        } catch (UnsupportedEncodingException e) {
            throw new IllegalArgumentException(e);
        }
    }
    /**
     测试方法
     */
    public static void main(String[] args)
    {
        //参数签名算法测试例子
        HashMap<String, String> signMap = new HashMap<String, String>();
        String appkey = "584561146";
        signMap.put("appKey",appkey);
//        System.out.println("得到签名sign1:"+getSign(signMap,"454sd5fa45dsa5fdaf54"));
        String sign = getSign(signMap, "454sd5fa45dsa5fdaf54");
        signMap.put("sign",sign);
        HttpClientUtils.postParams("http://localhost:81/attendance/AppSave", appkey, signMap);
    }
}
src/main/java/org/springblade/modules/signature/util/SignUtils.java
@@ -5,6 +5,9 @@
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import com.qcloud.cos.utils.Md5Utils;
import liquibase.util.MD5Util;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springblade.common.utils.Md5SignUtil;
@@ -14,8 +17,8 @@
/**
 * 签名验证工具类
 * @author Administrator
 * @since 2022-02-15
 * @author arsn
 * @since 2022-03-10
 */
@Slf4j
public class SignUtils {
@@ -24,7 +27,7 @@
        Map<String, Object> map = new HashMap<>();
        map.put("sign",sign.getSign());
        map.put("accessKeyId",sign.getAccessKeyId());
//        map.put("accessDate",sign.getAccessKeyId());
        map.put("accessDate",sign.getAccessDate());
        return Md5SignUtil.signRequest(map,accessKeySecret);
    }