最近碰到一个需求是要模仿支付宝、微信等平台的API接口。在APP登录时候产生token,后用token等参数设计
安全接口验证,但实际过程中发现如果表单和非表单方式是有区别,因为非表单可以用
HttpServletRequest.getParameterNames获取post和get的请求参数(queryparam),如果使用
multi-part那就没办法使用上述方式,但可以用HttpServletRequest.getParts()。在使用这个方法处理
调试时候出现了如果下几个错误
1、Unable to process parts as no multi-part configuration
这个原因是因为web.xml没有配置multi-part配置,在serverlet节点里面
<multipart-config>
<max-file-size>52428800</max-file-size>
<max-request-size>52428800</max-request-size>
<file-size-threshold>0</file-size-threshold>
</multipart-config>
2、the request was rejected because no multipart boundary was found
这个原因是因为我用postman模拟请求时候加了header,这个地方不用手动加header的媒体类型
具体代码实现内容如下
@Provider
public class VerifyFilter implements ContainerRequestFilter{
@Context
private HttpServletRequest servletRequest;
@Context
private HttpServletResponse servletResponse;
private Logger filterLog=LoggerFactory.getLogger(VerifyFilter.class);
private String substringBetween(String str, String open, String close) {
if (str == null || open == null || close == null)
return null;
int start = str.indexOf(open);
if (start != -1) {
int end = str.indexOf(close, start + open.length());
if (end != -1)
return str.substring(start + open.length(), end);
}
return null;
}
@SuppressWarnings("rawtypes")
private void ProcessMultiPart(ContainerRequestContext requestContext) throws IOException{
/*HashMap<String,String> map=new HashMap<String, String>();
HashMap<String,Object> resultMap=new HashMap<String,Object>();
String signStr="";
String timespan="0";
String deviceid="";
InputStream in = requestContext.getEntityStream();
int fileLen=1024;//URL参数部分长度
byte[] fileContent = new byte[fileLen];
String encoding = "UTF-8";
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int len;
while ((len = in.read(fileContent)) > -1 ) {
baos.write(fileContent, 0, len);
}
baos.flush();
InputStream stream1 = new ByteArrayInputStream(baos.toByteArray());
stream1.reset();
requestContext.setEntityStream(stream1);
InputStream stream2 = new ByteArrayInputStream(baos.toByteArray());
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(stream2));
String lineStr = null;
int index;
while((lineStr = bufferedReader.readLine()) != null){
//分析form-data
lineStr=lineStr.trim();
if(lineStr.contains("Content-Disposition")&&!lineStr.contains("filename")){
index=lineStr.indexOf("name=");
String name=lineStr.substring(index+6, lineStr.length());
index=name.indexOf("\"");
name=name.substring(0,name.length()-1);
bufferedReader.readLine();//读取空格
lineStr=bufferedReader.readLine();
lineStr=URLDecoder.decode(lineStr,encoding).trim();
//存入值
if(name.equals("signStr")){
signStr=lineStr;
}
else if(name.equals("timespan")){
timespan=lineStr;
map.put(name, lineStr);
}
else if(name.equals("deviceID")){
deviceid=lineStr;
map.put(name, lineStr);
}
else{
map.put(name, lineStr);
}
}
} */
//in.close();
//bufferedReader.close();
//baos.close();
//stream2.close();
/* //是否有这三个参数
String token= RedisService.getInstance().getAppImeiToken(deviceid);
if(TextUtil.isNullEmptyOrWhitespace(token)){
resultMap.put(RESULT, INVAILDEDTOKEN.getResultCode());
Response response = Response.ok(resultMap).status(200).type(MediaType.APPLICATION_JSON).build();
throw new WebApplicationException(response); // Throw new UnAuthorized
}
map.put("token", token);
long lCurTime=System.currentTimeMillis();
long lTimeSpan=Long.parseLong(timespan);
if(lCurTime-lTimeSpan>60000){
filterLog.info(ErrorMessage.VERIFY_TIMESTAP,servletRequest.getRequestURI());
}
SortedMap<String,String> sort=new TreeMap<String,String>(map);
Set es = sort.entrySet();
Iterator it = es.iterator();
StringBuilder sb=new StringBuilder();
while(it.hasNext()){
Map.Entry entry = (Map.Entry)it.next();
sb.append(entry.getValue());
//System.out.println("排序之后:"+entry.getKey()+"值:"+entry.getValue());
}
String okSignStr=MD5Util.getMd5String(sb.toString());
if(!okSignStr.equals(signStr)){
resultMap.put(RESULT, SIGNATUREINCONSISTENT.getResultCode());
Response response = Response.ok(resultMap).status(200).type(MediaType.APPLICATION_JSON).build();
throw new WebApplicationException(response); // Throw new UnAuthorized
}*/
}
@SuppressWarnings("rawtypes")
private void ProcessNoMultiPartPost(ContainerRequestContext requestContext) throws IOException{
String signStr="";
String timespan="0";
String deviceid="";
HashMap<String,String> map=new HashMap<String, String>();
String encoding = "UTF-8";
short fileLen=1024;//URL参数部分长度
byte[] fileContent = new byte[fileLen];
InputStream in = requestContext.getEntityStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int len;
while ((len = in.read(fileContent)) > -1 ) {
baos.write(fileContent, 0, len);
}
baos.flush();
InputStream stream1 = new ByteArrayInputStream(baos.toByteArray());
requestContext.setEntityStream(stream1);
String value=new String(baos.toByteArray(),encoding);
baos.close();
String[] strArray = value.split("&");
for(int i=0;i<strArray.length;++i){
if(strArray[i].contains("Content-Type=")) continue;
if(!strArray[i].contains("=")) continue;
String[] strArray1=strArray[i].split("=");
if(strArray1[0].equals("signStr")){
signStr=strArray1[1];
}
else if(strArray1[0].equals("timespan")){
timespan=strArray1[1];
map.put(strArray1[0], strArray1[1]);
}
else if(strArray1[0].equals("deviceID")){
deviceid=strArray1[1];
map.put(strArray1[0], strArray1[1]);
}
else{
map.put(strArray1[0], strArray1[1]);
}
}
//是否有这三个参数
String token= RedisService.getInstance().getAppImeiToken(deviceid);
if(TextUtil.isNullEmptyOrWhitespace(token)){
HashMap<String,Object> resultMap=new HashMap<String,Object>();
resultMap.put(RESULT, INVAILDEDTOKEN.getResultCode());
Response response = Response.ok(resultMap).status(200).type(MediaType.APPLICATION_JSON).build();
throw new WebApplicationException(response); // Throw new UnAuthorized
}
map.put("token", token);
long lCurTime=System.currentTimeMillis();
long lTimeSpan=Long.parseLong(timespan);
if(lCurTime-lTimeSpan>60000){
filterLog.info(ErrorMessage.VERIFY_TIMESTAP,servletRequest.getRequestURI());
}
SortedMap<String,String> sort=new TreeMap<String,String>(map);
Set es = sort.entrySet();
Iterator it = es.iterator();
StringBuilder sb=new StringBuilder();
while(it.hasNext()){
Map.Entry entry = (Map.Entry)it.next();
sb.append(entry.getValue());
}
System.out.println(sb.toString());
String okSignStr=MD5Util.getMd5String(sb.toString());
if(!okSignStr.equals(signStr)){
HashMap<String,Object> resultMap=new HashMap<String,Object>();
resultMap.put(RESULT, SIGNATUREINCONSISTENT.getResultCode());
Response response = Response.ok(resultMap).status(200).type(MediaType.APPLICATION_JSON).build();
throw new WebApplicationException(response); // Throw new UnAuthorized
}
}
@SuppressWarnings("rawtypes")
private void ProcessNoMultiPart(ContainerRequestContext requestContext){
Enumeration<String> listParam=servletRequest.getParameterNames();
HashMap<String,String> map=new HashMap<String, String>();
String signStr="";
String timespan="0";
String deviceid="";
//System.out.println(servletRequest.getRequestURI());
if(!listParam.hasMoreElements()){
HashMap<String,Object> resultMap=new HashMap<String,Object>();
resultMap.put(RESULT, ARGUMENTSISNULLORNOTFIND.getResultCode());
Response response = Response.ok(resultMap).status(200).type(MediaType.APPLICATION_JSON).build();
throw new WebApplicationException(response); // Throw new UnAuthorized
}
while(listParam.hasMoreElements()){
String name=(String)listParam.nextElement();
String value=servletRequest.getParameter(name);
if(name.equals("signStr")){
signStr=value;
}
else if(name.equals("timespan")){
timespan=value;
map.put(name, value);
}
else if(name.equals("deviceID")){
deviceid=value;
map.put(name, value);
}
else{
map.put(name, value);
}
}
//是否有这三个参数
String token= RedisService.getInstance().getAppImeiToken(deviceid);
if(TextUtil.isNullEmptyOrWhitespace(token)){
HashMap<String,Object> resultMap=new HashMap<String,Object>();
resultMap.put(RESULT, INVAILDEDTOKEN.getResultCode());
Response response = Response.ok(resultMap).status(200).type(MediaType.APPLICATION_JSON).build();
throw new WebApplicationException(response); // Throw new UnAuthorized
}
map.put("token", token);
long lCurTime=System.currentTimeMillis();
long lTimeSpan=Long.parseLong(timespan);
if(lCurTime-lTimeSpan>60000){
filterLog.info(ErrorMessage.VERIFY_TIMESTAP,servletRequest.getRequestURI());
}
SortedMap<String,String> sort=new TreeMap<String,String>(map);
Set es = sort.entrySet();
Iterator it = es.iterator();
StringBuilder sb=new StringBuilder();
while(it.hasNext()){
Map.Entry entry = (Map.Entry)it.next();
sb.append(entry.getValue());
//System.out.println("排序之后:"+entry.getKey()+"值:"+entry.getValue());
}
String okSignStr=MD5Util.getMd5String(sb.toString());
if(!okSignStr.equals(signStr)){
HashMap<String,Object> resultMap=new HashMap<String,Object>();
resultMap.put(RESULT, SIGNATUREINCONSISTENT.getResultCode());
Response response = Response.ok(resultMap).status(200).type(MediaType.APPLICATION_JSON).build();
throw new WebApplicationException(response); // Throw new UnAuthorized
}
}
@Override
public void filter(ContainerRequestContext requestContext) throws IOException {
// TODO Auto-generated method stub
String url=servletRequest.getRequestURI();
//媒体类型如果是mult/part-data
if(url.contains("/v1/api")){
if(requestContext.getMethod().equals("GET")){
ProcessNoMultiPart(requestContext);
}
else{
MediaType mt=requestContext.getMediaType();
if(mt==null||mt.getType()==null||mt.getSubtype()==null){
ProcessNoMultiPartPost(requestContext);
}
else if(MediaType.MULTIPART_FORM_DATA_TYPE.getType().equals(mt.getType())
&&MediaType.MULTIPART_FORM_DATA_TYPE.getSubtype().equals(mt.getSubtype())){
ProcessMultiPart(requestContext);
}
else{
ProcessNoMultiPartPost(requestContext);
}
}
}
}
}
注意点,在传multi-part数据时候,如果用上述方法将导致数据流无法读取,因为业务层获取文件数据代码
如果这个写法
FileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
List<FileItem> items = upload.parseRequest(request);
查看源代码看到InputSream是用HttpServelRequest获取,而不是在jersey层的流,所以业务层不能读取
底层数据,要用jersey方式获取文件。但是会碰到一个注解错误,在web.xml里面添加就可以了。
<init-param>
<param-name>jersey.config.server.provider.classnames</param-name>
<param-value>org.glassfish.jersey.filter.LoggingFilter;org.glassfish.jersey.media.multipart.MultiPartFeature</param-value>
</init-param>
业务demo参考
@POST
@Path("/update")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.MULTIPART_FORM_DATA)
public String update(@FormDataParam ("token") String deviceid,
@FormDataParam("nickname") String nickName,
@FormDataParam("sex") String sex,
@FormDataParam("birthday") String birthday,
@FormDataParam("file") InputStream saveFile,
@FormDataParam("file") FormDataContentDisposition fileItem)