Spring 接口参数加密传输

加密方式 AES

spring jar 包 pom.xml配置(注意版本)

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>3.2.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>3.2.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-orm</artifactId>
            <version>3.2.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>3.2.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
            <version>3.2.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>3.2.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>3.2.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>3.2.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>3.2.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>3.2.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>3.2.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc-portlet</artifactId>
            <version>3.2.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-oxm</artifactId>
            <version>3.2.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jms</artifactId>
            <version>3.2.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>3.2.5.RELEASE</version>
            <scope>test</scope>
        </dependency>

这个是原理图

在spring做如下 配置。

    <!-- 启动Spring MVC的注解功能,完成请求和注解POJO的映射 -->
    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
        <property name="order" value="0" />
    </bean>
    <bean class="com.sifude.youlife.spring.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
        
        <property name="messageConverters">
            <list>
                <ref bean="jsonHttpMessageConverter" />
                <ref bean="stringHttpMessageConverter" />
                <!-- <ref bean="marshallingHttpMessageConverter" /> -->
            </list>
        </property>
    </bean>
    <bean id="stringHttpMessageConverter"
        class="org.springframework.http.converter.StringHttpMessageConverter">
        <constructor-arg value="UTF-8" />
        <property name="supportedMediaTypes">
            <value>text/html;charset=UTF-8</value>
        </property>
    </bean>
    <bean id="jsonHttpMessageConverter"
        class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
        <property name="supportedMediaTypes">
            <list>
                <value>text/html;charset=UTF-8</value>
                <value>application/json;charset=UTF-8</value>
            </list>
        </property>
    </bean>

可以发现com.sifude.youlife.spring.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter这个类就是我们自己写的。从spring里面拷贝出如下几个类

RequestParamMethodArgumentResolver对每个参数进行了拦截,然后在resolveName方法进行处理,因此我们只要

重写RequestParamMethodArgumentResolver中的resolveName方法即可

    @Override
    protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest webRequest) throws Exception {

        Object arg;

        HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
        MultipartHttpServletRequest multipartRequest =
            WebUtils.getNativeRequest(servletRequest, MultipartHttpServletRequest.class);

        if (MultipartFile.class.equals(parameter.getParameterType())) {
            assertIsMultipartRequest(servletRequest);
            Assert.notNull(multipartRequest, "Expected MultipartHttpServletRequest: is a MultipartResolver configured?");
            arg = multipartRequest.getFile(name);
        }
        else if (isMultipartFileCollection(parameter)) {
            assertIsMultipartRequest(servletRequest);
            Assert.notNull(multipartRequest, "Expected MultipartHttpServletRequest: is a MultipartResolver configured?");
            arg = multipartRequest.getFiles(name);
        }
        else if ("javax.servlet.http.Part".equals(parameter.getParameterType().getName())) {
            assertIsMultipartRequest(servletRequest);
            arg = servletRequest.getParameter(name);
        }
        else {
            arg = null;
            if (multipartRequest != null) {
                List<MultipartFile> files = multipartRequest.getFiles(name);
                if (!files.isEmpty()) {
                    arg = (files.size() == 1 ? files.get(0) : files);
                }
            }
            if (arg == null) {
                boolean isEnc = false;
                if (null != parameter.getMethod().getAnnotation(EncRequest.class)) {
                    isEnc = true;
                }
                if (isEnc) {// 数据需要加密的情况
                    String content = servletRequest.getParameter("content");
                    if (null != content) {
                        content = AESUtil.decrypt(content);
                        ObjectMapper mapper = new ObjectMapper(); // can reuse, share
                        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);// 忽略未知元素
                        Object o = mapper.readValue(content, HashMap.class).get(name);
                        if(o instanceof String[]) {
                            String[] paramValues = (String[]) o;
                            if (paramValues != null) {
                                arg = paramValues.length == 1 ? paramValues[0] : paramValues;
                            }
                        } else {
                            arg = o;
                        }
                    }
                } else {
                    String[] paramValues = webRequest.getParameterValues(name);
                    if (paramValues != null) {
                        arg = paramValues.length == 1 ? paramValues[0] : paramValues;
                    }
                }
            }
        }

        return arg;
    }

其他1个注解和加密算法

package com.sifude.annotations;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface EncRequest {

}
package com.sifude.tool.util;

import java.io.UnsupportedEncodingException;
import java.util.Random;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.sifude.tool.util.entity.Constant;

/**
 * AES加解密算法
 * key:每次登陆动态随机生成(大小写字母和数字组成),并保存在session中
 * 此处使用AES-128-CBC加密模式,key需要为16位
 */
public class AESUtil {
    private static Logger log = LoggerFactory.getLogger(FileUtil.class);
    
    public static boolean isAES = Constant.AES.ISAES;
    public static String sKey = Constant.AES.SKEY;
    
    // 加密
    public static String encrypt(String sSrc) throws Exception {
        
        if(!isAES) {
            return sSrc;
        }
        if (sKey == null) {
            //System.out.print("Key为空null");
            return null;
        }
        // 判断Key是否为16位
        if (sKey.length() != 16) {
            //System.out.print("Key长度不是16位");
            return null;
        }
        byte[] raw = sKey.getBytes();
        SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");// "算法/模式/补码方式"
        IvParameterSpec iv = new IvParameterSpec("0102030405060708".getBytes());// 使用CBC模式,需要一个向量iv,可增加加密算法的强度
        cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
        //加密前要进行编码,否则js无法解码
        byte[] encrypted = cipher.doFinal(sSrc.getBytes("UTF-8"));

        return Base64.encodeBase64String(encrypted);// 此处使用BAES64做转码功能,同时能起到2次加密的作用。
    }

    // 解密
    public static String decrypt(String sSrc) throws Exception {
        if(!isAES) {
            return sSrc;
        }
        // 判断Key是否正确
        if (sKey == null) {
            //System.out.print("Key为空null");
            return null;
        }
        // 判断Key是否为16位
        if (sKey.length() != 16) {
            //System.out.print("Key长度不是16位");
            return null;
        }
        byte[] raw = sKey.getBytes("ASCII");
        SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        IvParameterSpec iv = new IvParameterSpec("0102030405060708"
                .getBytes());
        cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
        byte[] encrypted1 = Base64.decodeBase64(sSrc);// 先用bAES64解密
        //System.out.println(encrypted1.length);
        byte[] original = cipher.doFinal(encrypted1);
        String originalString = new String(original);
        return originalString;
    }

    // 生成随机密锁
    public static String getKey(int length) {
        StringBuffer sb = new StringBuffer();
        Random random = new Random();
        // 参数length,表示生成几位随机数
        for (int i = 0; i < length; i++) {
            String charOrNum = random.nextInt(2) % 2 == 0 ? "char" : "num";
            // 输出字母还是数字
            if ("char".equalsIgnoreCase(charOrNum)) {
                // 输出是大写字母还是小写字母
                int temp = random.nextInt(2) % 2 == 0 ? 65 : 97;
                sb.append((char) (random.nextInt(26) + temp));
            } else if ("num".equalsIgnoreCase(charOrNum)) {
                sb.append(String.valueOf(random.nextInt(10)));
            }
        }

        try {
            return new String(sb.toString().getBytes(), "UTF-8");
        } catch (UnsupportedEncodingException e) {
            log.error(e.getMessage(), e);
        }
        return "mapabc2014214yxj";
    }

    public static void main(String[] args) {
        //AES.sKey = getKey(16);
        AESUtil.isAES = true;
        try {
            //String str = AES.encrypt("你好1.2#3:4//5_6,1 2&3?4a/bc5=6");
            //String str = AES.encrypt("{\"account\":\"ez\",\"password\":\"123456\"}");
            String str = AESUtil.encrypt("{\"cityId\":\"110000\",\"cityType\":\"1\"}");
            System.out.println(str);
            String str1 = AESUtil.decrypt(str);
            System.out.println(str1);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

}

这样功能就实现了,欢迎大家一期交流。

posted @ 2017-06-11 09:58  BarryW  阅读(1138)  评论(0编辑  收藏  举报