jsonp跨域访问+AES,RSA加密

1.jsonp介绍:

Jsonp(JSON with Padding) 是 json 的一种"使用模式",可以让网页从别的域名(网站)那获取资料,即跨域读取数据。简单说是利用<script>标签没有跨域限制的“漏洞”来达到与第三方通讯的目的。

当项目的前后端分离,部署在不同的服务器上就要面临跨域问题,这时可以用jsonp来实现跨域数据请求。

2.jsonp使用:

前端jquery ajax请求方式:

var data={
        param1:param1 //参数1
    }
$.ajax({
   type : "get",
   async:false,
   url : "****/test.json",//后端请求地址
   dataType : "jsonp",
   data:data,
   jsonp: "callbackparam",//传递给请求处理程序或页面的,用以获得jsonp回调函数名的参数名(默认为:callback)
   success : function(json){
       alert(json.result[0].personName);
   }
});

后端代码(springMvc框架):

    @ResponseBody
    @RequestMapping(value="test.json")
    public JSONPObject test(String param1,String callbackparam) throws Exception {
        ModelMap model = new ModelMap();
        List<PersonInfo> personList = new ArrayList<PersonInfo>();
        PersonInfo person = new PersonInfo();
        person.setPersonName("李四");
        person.setBornLocation("火星");
        personList.add(person);
        person = new PersonInfo();
        person.setPersonName("张三");
        person.setBornLocation("南极");
        model.addAttribute("result", personList);
        JSONPObject resultFinal = new JSONPObject(callbackparam, model);
        return resultFinal;
    }

3.数据传输加密:

由于jsonp只支持get方式发送请求,即使在ajax中设置为post,jquery也会自动转为get。所以数据传输缺少安全性,上网了解了一下前辈们的经验,总结了以下AES+RSA加密实现方式:

(1) AES 加密

需要引入相关jar包,(maven)pom配置如下

        <dependency>
            <groupId>org.bouncycastle</groupId>
            <artifactId>bcprov-jdk15on</artifactId>
            <version>1.57</version>
        </dependency>

(2)前端工具类代码(发布时最好做压缩混淆,注:代码非原创来源网上)

/**
 * 加密解密
 */
 
/** RSA加密用 获取RSQ公钥 */
function bodyRSA(){
    /** 1024位的key参数写130,2014位的key参数写260 */
    setMaxDigits(130);
    /** ajax 调用后台方法,取回公钥 */
    var keyR ;
    $.ajax({
        url: "Key/pk",//请求后台的url,本例是springMVC框架
        type: "post",
        cache: false,
        async : false,
        dataType: "text",
        success: function (data)
        {
            keyR = data;
        },
        error:function (XMLHttpRequest, textStatus, errorThrown) {      
            alert("与服务器连接失败!");
        }
     });
    /** RSAKeyPair 函数三个参数:加密指数、解密指数、系数 */
    return new RSAKeyPair("10001","",keyR);
}
 
/** AES加密用 随机生成key和iv */
function randomString() {
    var chars='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
    var length= chars.length;
    var pwd='';
    for(var i = 0; i < 16 ;i++){
        pwd += chars.charAt(Math.floor(Math.random() * length));
    }
    return pwd;
}
/**
 * AES加密
 * @param data
 * @param key
 * @param iv
 * @returns
 */
function getAesString(data,key,iv){
    var key  = CryptoJS.enc.Utf8.parse(key);
    var iv   = CryptoJS.enc.Utf8.parse(iv);
    var encrypted = CryptoJS.AES.encrypt(data,key,
            {
                iv:iv,
                mode:CryptoJS.mode.CBC,
                padding:CryptoJS.pad.Pkcs7
            });
    return encrypted.toString();
}
/**
 * AES解密
 * @param encrypted
 * @param key
 * @param iv
 * @returns
 */
function getDAesString(encrypted,key,iv){
    var key  = CryptoJS.enc.Utf8.parse(key);
    var iv   = CryptoJS.enc.Utf8.parse(iv);
    var decrypted = CryptoJS.AES.decrypt(encrypted,key,
            {
                iv:iv,
                mode:CryptoJS.mode.CBC,
                padding:CryptoJS.pad.Pkcs7
            });
    return decodeURIComponent(decrypted.toString(CryptoJS.enc.Utf8)).replace("+", " ");
}

后端获取RSA公钥代码(注:来源网上)

@Controller
@RequestMapping("Key")
public class PublicKeyController {
     
    /**
     * 获取RSA密钥文件中的公钥
     * @return String类型
     * @throws Exception 
     */
    @RequestMapping("/pk")
    @ResponseBody 
    public String getPublicKey() throws Exception{
        /** 实例化加密解密工具类*/
        EncryptionDecryption ed = new EncryptionDecryption();
//        ed.generateKeyPair(); //首次运行,需要放开此段代码,来生成公钥及私钥
        return ed.getPublicKey();
    }
}

(3)后端工具类代码(注:来源网上):

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.SecureRandom;
 
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
 
import org.apache.commons.codec.binary.Base64;
import org.apache.log4j.Logger;
 
/**
 * rsa aes 加密解密工具类
 * Title: EncryptionDecryption
 * Company: djzh
 * @author hanlin
 * @date 2017年1月17日 上午11:02:50
 */
 
 
public class EncryptionDecryption {
 
    /** 密钥文件存储位置 */
    private static String RSAKeyStore = "C:/RSAKey.txt";//在这个位置放这个文件
     
    /**
     * 日志记录器
     */
    public static Logger logger = Logger.getLogger(EncryptionDecryption.class);
     
    /**
     * AES加密
     * @param content  明文
     * @param keyBytes 秘钥
     * @param iv      偏移量
     * @return   
     */   
    public static String AES_CBC_Encrypt(String content, byte[] keyBytes, byte[] iv){  
           
        try{ 
            SecretKeySpec key = new SecretKeySpec(keyBytes, "AES");
            Cipher cipher=Cipher.getInstance("AES/CBC/PKCS5Padding");  
            cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv));  
            content = URLEncoder.encode(content,"UTF-8");   //用url编码
            byte[] result=cipher.doFinal(content.getBytes()); //加密
            return new String(Base64.encodeBase64(result),"UTF-8");
        }catch (NoSuchPaddingException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        } catch (BadPaddingException e) {
            e.printStackTrace();
        } catch (InvalidAlgorithmParameterException e) {
            e.printStackTrace();
        } 
        return null;
    }
     
    /**
     * AES解密
     * @param content   密文
     * @param keyBytes  秘钥
     * @param iv        偏移量
     * @return          
     */
    public static String AES_CBC_Decrypt(String content, byte[] keyBytes, byte[] iv){  
           
        try{  
            content = content.replaceAll(" ", "+");
            byte[] decryptBaseData=Base64.decodeBase64(content.getBytes("utf-8"));
            SecretKeySpec key = new SecretKeySpec(keyBytes, "AES");
            Cipher cipher=Cipher.getInstance("AES/CBC/PKCS5Padding");  
            cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));  
            byte[] result=cipher.doFinal(decryptBaseData);  
            return URLDecoder.decode(new String(result),"utf-8");  
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        } catch (BadPaddingException e) {
            e.printStackTrace();
        } catch (InvalidAlgorithmParameterException e) {
            e.printStackTrace();
        }   
        return null;  
    }
     
    /**
     * 字符串转为 byte[]
     * @param hexString
     * @return
     */
    public static byte[] hexStringToBytes(String hexString) {
        if (hexString == null || hexString.equals("")) {
            return null;
        }
        hexString = hexString.toUpperCase();
        int length = hexString.length() / 2;
        char[] hexChars = hexString.toCharArray();
        byte[] d = new byte[length];
        for (int i = 0; i < length; i++) {
            int pos = i * 2;
            d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));
        }
        return d;
    }
     
    /**
     * Convert char to byte
     * @param c char
     * @return byte
     */
    private static byte charToByte(char c) {
        return (byte) "0123456789ABCDEF".indexOf(c);
    }
     
    /**
     * 解密由RSA加密的AES的key 和 iv
     * @param para
     * @return
     * @throws Exception
     */
    public static byte[] getValue(String param) throws Exception{
        byte[] trueValue = null;
        try {
            if(!param.equals("") && param != null){
                byte[] KeyB = hexStringToBytes(param);
                KeyB = decrypt(getKeyPair().getPrivate(),KeyB);
                StringBuffer sbKey = new StringBuffer();
                sbKey.append(new String(KeyB));
                param = sbKey.reverse().toString();
                trueValue = URLDecoder.decode(param,"UTF-8").getBytes(); 
            }          
        } catch (Exception e) {
            //重要参数值
            logger.error("传入参数:" + "param: " + param);
            //异常说明
            logger.error("解密由RSA加密的AES的key 和 iv 失败,可能前台传入的aKey或者aIv为空");
            e.printStackTrace();
        }
        return trueValue; 
    }
     
    /**
     * 获取密钥文件中的公钥
     * @return
     */
    public String getPublicKey(){
        Object publicKey = null;
        String publicKEY = null;
        try {
            publicKey = getKeyPair().getPublic();
            publicKEY = (String) publicKey.toString().subSequence(37, 293);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return publicKEY;     
    }
     
    /**
     * RSA 生成密钥对
     * @return
     * @throws Exception
     */
    public static KeyPair generateKeyPair() throws Exception {
        try {
            KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA",
                    new org.bouncycastle.jce.provider.BouncyCastleProvider());
            final int KEY_SIZE = 1024;
            keyPairGen.initialize(KEY_SIZE, new SecureRandom());
            KeyPair keyPair = keyPairGen.generateKeyPair();
            FileOutputStream fos = new FileOutputStream(RSAKeyStore);
            ObjectOutputStream oos = new ObjectOutputStream(fos);
            oos.writeObject(keyPair);
            oos.close();
            fos.close();
            return keyPair;
        } catch (Exception e) {
            throw new Exception(e.getMessage());
        }
    }
 
    /**
     * 获取密钥对
     * @return
     * @throws Exception
     */
    public static KeyPair getKeyPair() throws Exception {
        FileInputStream fis = new FileInputStream(RSAKeyStore);
        ObjectInputStream oos = new ObjectInputStream(fis);
        KeyPair kp = (KeyPair) oos.readObject();
        oos.close();
        fis.close();
        return kp;
    }
 
    /**
     * RSA解密,获取AES的key和iv时调用
     * @param pk
     * @param raw
     * @return
     * @throws Exception
     */
    @SuppressWarnings("static-access")
    private static byte[] decrypt(PrivateKey pk, byte[] raw) throws Exception {
        try {
            Cipher cipher = Cipher.getInstance("RSA",
                    new org.bouncycastle.jce.provider.BouncyCastleProvider());
            cipher.init(cipher.DECRYPT_MODE, pk);
            int blockSize = cipher.getBlockSize();
            ByteArrayOutputStream bout = new ByteArrayOutputStream(64);
            int j = 0;
 
            while (raw.length - j * blockSize > 0) {
                bout.write(cipher.doFinal(raw, j * blockSize, blockSize));
                j++;
            }
            return bout.toByteArray();
        } catch (Exception e) {
            throw new Exception(e.getMessage());
        }
    }
}

至此准备工作完成,如下为加密方式调用的代码

(4)加密方式调用

前端ajax 加密方式请求代码:

    var keyRSA = bodyRSA(); //生成RSA加密用的key
    var key  = randomString();//随机生成AES的key 和 iv      
    var iv   = randomString();
    var aKey = encryptedString(keyRSA, encodeURIComponent(key)); //RSA加密AES的key
    var aIv = encryptedString(keyRSA, encodeURIComponent(iv)); //RSA加密AES的iv     
    var param1="张三";//参数1
    var param1_ = getAesString(encodeURIComponent(param1),key,iv); //AES加密参数内容1   
    //筛选条件的参数
    var data={
        param1:param1_, //参数1
        aKey:aKey, 
        aIv:aIv
    }
    $.ajax({
        type : "GET",
        async: false, 
        url:"***/test.json",//请求的url,本例为springMVC框架
        dataType:'JSONP',
        data:data,
        jsonp:'callbackparam',
        success:function(json){
            alert(json.result);
            var decryptedStr = getDAesString(json.result,key,iv);//解密
            var resultObj = JSON.parse(decryptedStr);
            alert(resultObj[0].personName);
        }              
   });

后端代码:

    @ResponseBody
    @RequestMapping(value="test.json")
    public JSONPObject testRes(String param1,String aKey,String aIv,String callbackparam) throws Exception {
        ModelMap model = new ModelMap();
        EncryptionDecryption encryptionDecryption = new EncryptionDecryption();
        String key = encryptionDecryption.getValue(aKey).toString();
        String iv = encryptionDecryption.getValue(aIv).toString();
        byte[] keyBytes = encryptionDecryption.getValue(aKey);
        byte[] ivBytes = encryptionDecryption.getValue(aIv);
        param1 = encryptionDecryption.AES_CBC_Decrypt(param1, keyBytes, ivBytes);
        System.out.println("key:"+key+"\n");
        System.out.println("iv:"+iv+"\n");
        System.out.println("param1:"+param1+"\n");
        List<PersonInfo> personList = new ArrayList<PersonInfo>();
        PersonInfo person = new PersonInfo();
        person.setPersonName("李四");
        person.setBornLocation("火星");
        personList.add(person);
        person = new PersonInfo();
        person.setPersonName("张三");
        person.setBornLocation("南极");
        String result = JsonUtils.toJson(personList);//把list转为json格式字符串
        System.out.println("&&&&&&&&&&&&&&"+result+"\n");
        result = encryptionDecryption.AES_CBC_Encrypt(result, keyBytes, ivBytes);
        model.addAttribute("result", result);
        JSONPObject resultFinal = new JSONPObject(callbackparam, model);
        return resultFinal;
    }

JsonUtils代码:

import org.apache.commons.lang.StringUtils;
import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.map.ObjectMapper;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import java.io.StringWriter;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public abstract class JsonUtils {

    private static ObjectMapper objectMapper = new ObjectMapper();

    public static String toJson(Object bean) {
        StringWriter stringWriter = new StringWriter();
        try {
            JsonGenerator jsonGenerator = objectMapper.getJsonFactory()
                    .createJsonGenerator(stringWriter);
            objectMapper.writeValue(jsonGenerator, bean);
            return stringWriter.toString();
        } catch (IOException e) {
        }
        return "{}";
    }

}

调用结束,前端正常获取加密数据并进行解密显示。

(5)总结说明:

参数,及返回结果使用AES方式加密

AES中用到的key和iv在传输过程中用RSA加密

后端服务器需要存储一份RSA生成的公钥和私钥,生成的代码在工具类中,公钥私钥生成一次即可,如果需要更新先清空密钥文件内容,然后执行生成代码。

需要下载引入的js文件(rsa及aes相关):无法上传附件,就把文件名贴出来吧

<script language="javascript" type="text/javascript" src="statics/js/res/core-min.js"></script>
<script language="javascript" type="text/javascript" src="statics/js/res/lib-typedarrays-min.js"></script>
<script language="javascript" type="text/javascript" src="statics/js/res/aes.js"></script>
<script language="javascript" type="text/javascript" src="statics/js/res/RSA.js"></script>
<script language="javascript" type="text/javascript" src="statics/js/res/BigInt.js"></script>
<script language="javascript" type="text/javascript" src="statics/js/res/Barrett.js"></script>
<script language="javascript" type="text/javascript" src="statics/js/json2.js"></script>

理解说明不对的地方欢迎批评指正

 

 

转载于:https://my.oschina.net/u/2609444/blog/1510542

CH2.Ajax 框架 支持简单加密(Base64/DES)和跨域(Jsonp) 如何以最快捷的方式直接调用服务器方法? 场景: 比如,服务器已有如下方法: namespace CH2 { class BLL{ User Login(string name,string password){ //balabala... } } } 如何写最少的 js 调用以上方法?也许你只需要写五行 js 代码: CH2.BLL.Login( {name: "caihaihua", password: "123456"} ,function(user){ alert('登录成功!'); } ,function(errormessage){ alert('登录失败!' + errormessage);} ); 步骤: 1.网站引用两个dll:CH2.Ajax.dll 和 Newtonsoft.Json.dll 2.把服务器方法添加属性 AjaxMethod。(例一) 3.添加一个一般处理程序,继承于 AjaxHandler<T> (T 为服务器方法的类名) 。(例二) 4.页面中引用两个js。(例三) 5.准备工作完成了。ajax 调用服务器方法,只需要在 js 中直接写: [namespace].[classname].[methodname](arguments,onsuccess,onfailed) 其中, arguments 为参数集合 onsuccess 为调用成功的回调函数 function(returndata) onfailed 为调用失败的回调函数(可选) function(errormessage) (例四) 例一: namespace CH2 { class BLL{ [AjaxMethod] // ←原有方法中添加这一属性 User Login(string name,string password){ } } } 例二: <%@ WebHandler Language="C#" Class="Handler" %> using CH2; public class Handler : AjaxHandler<BLL> // ←一般处理程序继承于AjaxHandler<BLL> { //此处不需要任何代码 } 例三: <script type="text/javascript" src="Ajax/Ajax.js"></script> // 配套的 js <script type="text/javascript" src="Ajax/Handler.ashx"></script> // 第 3 步中的一般处理程序(注意 url 中不带任何传参) 例四: <script type="text/javascript"> btn.onclick = function(){ // 你这样直接调用服务器方法: // CH2.BLL.Login(arguments,onsuccess,onfailed) CH2.BLL.Login( { //服务器方法需要的参数。key 为参数名,value 为对应的 json 对象 name: "caihaihua" ,password: "123456" } , function(data){ //onsuccess 方法。data 为服务器方法返回的结果(User) } , function(errmsg){ //onfailed 方法。 }); } </script>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值