web开发过程中对post请求过来的整个请求流数据,怎样保证post在传输过程中被截取后无法获取到用户提交请求实际数据,保证请求安全,在实践过程中我们采用过滤器(Filter)来实现流截取完成这个代码post请求流数据及返回数据加解密
一、请求数据流解密
1 请求数据提交filter进行数据过滤,过滤器类主要用于创建HttpServletRequest,实现解密,将解密后的request返回到下层
public class CharacterFilter implements Filter{
@Override
public void doFilter(ServletRequest req, ServletResponse res,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest)req;
TsRequest wrapRequest= new TsRequest(request,request.getParameterMap());
chain.doFilter(wrapRequest, res);
}
}
2、提交数据解密:前端或者app端对提交数据流数据解密,继承HttpServletRequestWrapper方法实现自己的HttpServletRequest,拷贝原始数据流,getParameterValues写入解密方法,Spring框架进行自动解包是会通过getParameterValues读取数据流,进行自动解包,将数据set到对象,完成数据解密过程(copy数据流主要解决流只能读取一次问题)
public class TsRequest extends HttpServletRequestWrapper{
private Map params;
public static final int BUFFER_SIZE = 4096;
private byte[] requestBody = null;
public TsRequest(HttpServletRequest request, Map newParams) {
super(request);
this.params = newParams;
// 缓存请求body 读取Post数据
String method = request.getMethod();
if (("POST").equals(request.getMethod())) {
try {
ByteArrayOutputStream out = copy(request.getInputStream());
requestBody = out.toByteArray();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static ByteArrayOutputStream copy(InputStream in) throws IOException {
String data = convertStreamToString(in);
if (data != null && data.indexOf("time") == -1) {
try {
data = AESDataUtils.decrypt(data);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
byte bytes[] = data.getBytes("UTF-8");
ByteArrayOutputStream out = new ByteArrayOutputStream(bytes.length);
out.write(bytes, 0, bytes.length);
out.flush();
return out;
}
public static String convertStreamToString(InputStream is)
throws UnsupportedEncodingException {
BufferedReader reader = new BufferedReader(new InputStreamReader(is,
"UTF-8"));
StringBuilder sb = new StringBuilder();
String line = null;
try {
while ((line = reader.readLine()) != null) {
sb.append(line);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return sb.toString();
}
/**
* 重写 getInputStream()
*/
@Override
public ServletInputStream getInputStream() throws IOException {
if (requestBody == null) {
requestBody = new byte[0];
}
final ByteArrayInputStream bais = new ByteArrayInputStream(requestBody);
return new ServletInputStream() {
@Override
public int read() throws IOException {
return bais.read();
}
};
}
/**
* 重写 getReader()
*/
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(getInputStream()));
}
public Map getParameterMap() {
return params;
}
public Enumeration getParameterNames() {
Vector l = new Vector(params.keySet());
return l.elements();
}
public String[] getParameterValues(String name) {
Object v = params.get(name);
if (v == null) {
return null;
} else if (v instanceof String[]) {
String[] value = (String[]) v;
for (int i = 0; i < value.length; i++) {
// AES解密数据
value[i]=AESDataUtils.decrypt(value[i]);
if (StringUtils.isNoneEmpty(value[i])) {
value[i] = value[i].replaceAll("<", "<");
value[i] = value[i].replaceAll(">", ">");
}
}
return (String[]) value;
} else if (v instanceof String) {
String value = (String) v;
value=AESDataUtils.decrypt(value);
// AES解密数据
if (StringUtils.isNoneBlank(value)) {
value = value.replaceAll("<", "<");
value = value.replaceAll(">", ">");
}
return new String[] { (String) value };
} else {
return new String[] { v.toString() };
}
}
public String getParameter(String name) {
Object v = params.get(name);
if (v == null) {
return null;
} else if (v instanceof String) {
String value = (String) v;
value = AESDataUtils.decrypt(value);
// AES解密数据
if (StringUtils.isNoneBlank(value)) {
value = value.replaceAll("<", "<");
value = value.replaceAll(">", ">");
}
return (String) value;
} else if (v instanceof String[]) {
String[] strArr = (String[]) v;
if (strArr.length > 0) {
String value = strArr[0];
String aesdata=AESDataUtils.decrypt(value);
if(StringUtils.isNoneEmpty(aesdata)){
value =aesdata ;
}
// AES解密数据
if (StringUtils.isNoneBlank(value)) {
value = value.replaceAll("<", "<");
value = value.replaceAll(">", ">");
}
return value;
} else {
return null;
}
} else {
return v.toString();
}
}
}
二、返回数据流加密
1、继承过滤器接口实现返回数据过滤拦截,将返回数据转换为代理类,通过代理类改变处理返回数据流进行加密并将加密后数据重新写入到流中返回(使用代理类主要是为了解决流只能读取一次问题)
public class ResponseFilter implements Filter{
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain filterChain) throws IOException, ServletException {
filterChain.doFilter(request, response);
ResponseWrapper wrapperResponse = new ResponseWrapper(
(HttpServletResponse) response);// 转换成代理类
// 这里只拦截返回,直接让请求过去,如果在请求前有处理,可以在这里处理
filterChain.doFilter(request, wrapperResponse);
byte[] content = wrapperResponse.getContent();// 获取返回值
if (content.length > 0) {
String str = new String(content, "UTF-8");
String ciphertext = null;
try {
ciphertext = AESDataUtils.encrypt(str);
// ......根据需要处理返回值
} catch (Exception e) {
e.printStackTrace();
}
//将加密后数据重新写如后刷新数据流
ServletOutputStream out = response.getOutputStream();
out.write(ciphertext.getBytes());
out.flush();
}
}
}
2.返回值输出代理,通过代理获取返回值信息内容
public class ResponseWrapper extends HttpServletResponseWrapper
{
private ByteArrayOutputStream buffer;
private ServletOutputStream out;
public ResponseWrapper(HttpServletResponse httpServletResponse)
{
super(httpServletResponse);
buffer = new ByteArrayOutputStream();
out = new WrapperOutputStream(buffer);
}
@Override
public ServletOutputStream getOutputStream()
throws IOException
{
return out;
}
@Override
public void flushBuffer()
throws IOException
{
if (out != null)
{
out.flush();
}
}
public byte[] getContent()
throws IOException
{
flushBuffer();
return buffer.toByteArray();
}
class WrapperOutputStream extends ServletOutputStream
{
private ByteArrayOutputStream bos;
public WrapperOutputStream(ByteArrayOutputStream bos)
{
this.bos = bos;
}
@Override
public void write(int b)
throws IOException
{
bos.write(b);
}
}
}
三、使用工具类代码
AES加密工具类加解密代码模块,主要http请求加密过程会进行URL编码传入,需要进行URLDecoder.decode进行解码后才能进行解密操作
public class AESDataUtils {
/**
* 解密
* @param value
* @return
*/
public static String decrypt(String value){
if(StringUtils.isNoneEmpty(value)){
if(value.indexOf("%")>-1){
value=URLDecoder.decode(value);
}
}
try {
return AESUtils.decrypt(value);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
/**
* 加密
* @param value
* @return
*/
public static String encrypt(String value){
value=AESUtils.encrypt(value);
value=URLEncoder.encode(value);
return value;
}
}
实际AES加解密代码块,本文档采用aes进行加密,也可以自行可以换其它加密方式进行加密
public class AESUtils {
private static String secretKey = "秘钥";
private static String ivParameter = "";
/**
* aes 解密
*
* @param sSrc
* @return
* @throws Exception
*/
public static String decrypt(String sSrc) throws Exception {
try {
byte[] raw = secretKey.getBytes("ASCII");
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES / CBC / PKCS5Padding");
IvParameterSpec iv = new IvParameterSpec(ivParameter.getBytes());
cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
byte[] encrypted1 = new BASE64Decoder().decodeBuffer(sSrc);// 先用base64解密
byte[] original = cipher.doFinal(encrypted1);
String originalString = new String(original, "UTF-8");
return originalString;
} catch (Exception ex) {
return null;
}
}
//加密
public static String encrypt(String sSrc) {
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
byte[] raw = secretKey.getBytes();
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
//使用CBC模式,需要一个向量iv,可增加加密算法的强度
IvParameterSpec iv = new IvParameterSpec(ivParameter.getBytes());
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
byte[] encrypted = cipher.doFinal(sSrc.getBytes("utf-8"));
//此处使用BASE64做转码。
return new BASE64Encoder().encode(encrypted);
} catch (Exception ex) {
return null;
}
}
}
以上为加解密在实际开发过程中代码,代码提交是对于>和<等符号进行过滤,防止sql注入,在开发过程中可以参考此代码进行适当修改进行使用