现象描述:起初写了一个jsp页面,用于接收用户输入的字符串,后来发现在servlet端接收到字符串后,在传送过程中,字符产生了转义,导致不能接收到正确的内容。
在webui页面输入的字符串:
/home/hoary/backup/gitlab/keepLatestTar.sh > /home/hoary/logs/keepLatestTar.log 2>&1
在Servlet中接收到的字符串:
/home/hoary/backup/gitlab/keepLatestTar.sh > /home/hoary/logs/keepLatestTar.log 2>
由显示内容可以看到符号">" 转义成了 ">",由此导致接收到的实际内容错误。
分享一种解决转义的思路:使用Base64编码包含特殊字符
解决方案:
在webui页面中对输入的字符串进行Base64编码,然后在Servlet端接收到Base64编码的字符串后进行解码,即可得到正确的字符串
步骤1:在jsp页面对输入的字符串进行Base64编码
感谢大佬分析的在js函数中实现Base64编码与解码的代码,附上链接JS 实现Base64编码与解码实例详解_JavaScript技巧_积木网(gimoo.net)
本次使用中的具体代码:
function add() {
var crontab = prompt("请输入要添加的指令:");
if(crontab == null || crontab == "") {
alert("请输入crontab指令!");
return;
}
// 对输入的crontab指令进行Base64编码,避免符号转义
var base = new Base64();
var b64str = base.encode(crontab);
var reqData = "method=add&crontab="+crontab;
$.ajax({
url: "modifyCrontabServlet",
type: "post",
data: reqData,
dataType: "json",
success: function (result) {
var code = result.code;
if (code > 0) {
alert("增加crontab指令成功!");
window.location.href="modifyCrontab.jsp";
}else {
alert("增加crontab指令失败! " + result.msg);
}
}
});
}
//在js中实现Base编码与解码
function Base64() {
// private property
_keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
// public method for encoding
this.encode = function (input) {
var output = "";
var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
var i = 0;
input = _utf8_encode(input);
while (i < input.length) {
chr1 = input.charCodeAt(i++);
chr2 = input.charCodeAt(i++);
chr3 = input.charCodeAt(i++);
enc1 = chr1 >> 2;
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
enc4 = chr3 & 63;
if (isNaN(chr2)) {
enc3 = enc4 = 64;
} else if (isNaN(chr3)) {
enc4 = 64;
}
output = output +
_keyStr.charAt(enc1) + _keyStr.charAt(enc2) +
_keyStr.charAt(enc3) + _keyStr.charAt(enc4);
}
return output;
}
// public method for decoding
this.decode = function (input) {
var output = "";
var chr1, chr2, chr3;
var enc1, enc2, enc3, enc4;
var i = 0;
input = input.replace(/[^A-Za-z0-9+/=]/g, "");
while (i < input.length) {
enc1 = _keyStr.indexOf(input.charAt(i++));
enc2 = _keyStr.indexOf(input.charAt(i++));
enc3 = _keyStr.indexOf(input.charAt(i++));
enc4 = _keyStr.indexOf(input.charAt(i++));
chr1 = (enc1 << 2) | (enc2 >> 4);
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
chr3 = ((enc3 & 3) << 6) | enc4;
output = output + String.fromCharCode(chr1);
if (enc3 != 64) {
output = output + String.fromCharCode(chr2);
}
if (enc4 != 64) {
output = output + String.fromCharCode(chr3);
}
}
output = _utf8_decode(output);
return output;
}
// private method for UTF-8 encoding
_utf8_encode = function (string) {
string = string.replace(/rn/g,"n");
var utftext = "";
for (var n = 0; n < string.length; n++) {
var c = string.charCodeAt(n);
if (c < 128) {
utftext += String.fromCharCode(c);
} else if((c > 127) && (c < 2048)) {
utftext += String.fromCharCode((c >> 6) | 192);
utftext += String.fromCharCode((c & 63) | 128);
} else {
utftext += String.fromCharCode((c >> 12) | 224);
utftext += String.fromCharCode(((c >> 6) & 63) | 128);
utftext += String.fromCharCode((c & 63) | 128);
}
}
return utftext;
}
// private method for UTF-8 decoding
_utf8_decode = function (utftext) {
var string = "";
var i = 0;
var c = c1 = c2 = 0;
while ( i < utftext.length ) {
c = utftext.charCodeAt(i);
if (c < 128) {
string += String.fromCharCode(c);
i++;
} else if((c > 191) && (c < 224)) {
c2 = utftext.charCodeAt(i+1);
string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
i += 2;
} else {
c2 = utftext.charCodeAt(i+1);
c3 = utftext.charCodeAt(i+2);
string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
i += 3;
}
}
return string;
}
}
步骤2:在Servlet中接收到Base64编码后进行解码,得到原字符串
@WebServlet("/modifyCrontabServlet")
public class ModifyCrontabServlet extends HttpServlet {
String crontabStr = request.getParameter("crontab").trim();
String crontabAdd = new String(Base64.decode(crontabStr));
}
此处附上使用Java实现Base64编码的代码
public class Base64 {
private static final char[] CA = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
.toCharArray();
static {
Arrays.fill( IA , -1 );
for( int i = 0 , iS = CA.length ; i < iS ; i++ )
IA[ CA[ i ] ] = i;
IA[ '=' ] = 0;
}
public final static String encode( byte[] sArr ) {
return new String( encodeToString( sArr , false ) );
}
private final static char[] encodeToChar( byte[] sArr , boolean lineSep ) {
// Check special case
int sLen = sArr != null ? sArr.length : 0;
if( sLen == 0 )
return new char[ 0 ];
int eLen = ( sLen / 3 ) * 3; // Length of even 24-bits.
int cCnt = ( ( sLen - 1 ) / 3 + 1 ) << 2; // Returned character count
int dLen = cCnt + ( lineSep ? ( cCnt - 1 ) / 76 << 1 : 0 ); // Length of
// returned
// array
char[] dArr = new char[ dLen ];
// Encode even 24-bits
for( int s = 0 , d = 0 , cc = 0 ; s < eLen ; ) {
// Copy next three bytes into lower 24 bits of int, paying attension to
// sign.
int i = ( sArr[ s++ ] & 0xff ) << 16 | ( sArr[ s++ ] & 0xff ) << 8
| ( sArr[ s++ ] & 0xff );
// Encode the int into four chars
dArr[ d++ ] = CA[ ( i >>> 18 ) & 0x3f ];
dArr[ d++ ] = CA[ ( i >>> 12 ) & 0x3f ];
dArr[ d++ ] = CA[ ( i >>> 6 ) & 0x3f ];
dArr[ d++ ] = CA[ i & 0x3f ];
// Add optional line separator
if( lineSep && ++cc == 19 && d < dLen - 2 ) {
dArr[ d++ ] = '\r';
dArr[ d++ ] = '\n';
cc = 0;
}
}
// Pad and encode last bits if source isn't even 24 bits.
int left = sLen - eLen; // 0 - 2.
if( left > 0 ) {
// Prepare the int
int i = ( ( sArr[ eLen ] & 0xff ) << 10 )
| ( left == 2 ? ( ( sArr[ sLen - 1 ] & 0xff ) << 2 ) : 0 );
// Set last four chars
dArr[ dLen - 4 ] = CA[ i >> 12 ];
dArr[ dLen - 3 ] = CA[ ( i >>> 6 ) & 0x3f ];
dArr[ dLen - 2 ] = left == 2 ? CA[ i & 0x3f ] : '=';
dArr[ dLen - 1 ] = '=';
}
return dArr;
}
public final static byte[] decode( char[] sArr ) {
char[] tmp = new char[ sArr.length ];
int t = 0;
for( int i = 0 , length = sArr.length ; i < length ; i++ ) {
if( ( ( sArr[i] >= 'A' ) && ( sArr[i] <= 'Z' ) )
|| ( ( sArr[i] >= 'a' ) && ( sArr[i] <= 'z' ) )
|| ( ( sArr[i] >= '0' ) && ( sArr[i] <= '9' ) )
|| ( sArr[i] == '+' )
|| ( sArr[i] == '/' )
|| ( sArr[i] == '=' ) ){
tmp[ t ] = sArr[ i ];
t += 1;
}else if( sArr[i] == '-' ){
tmp[t] = '+' ;
t += 1;
}else if( sArr[i] == '_' ){
tmp[t] = '/' ;
t += 1;
}
}
char[] tmp2 = new char[ t ];
System.arraycopy( tmp , 0 , tmp2 , 0 , t );
tmp = null;
// System.out.println( new String( tmp2 ) ) ;
return decodeFast( tmp2 );
}
private final static byte[] decodeFast( char[] sArr ) {
// Check special case
int sLen = sArr.length;
if( sLen == 0 )
return new byte[ 0 ];
int sIx = 0 , eIx = sLen - 1; // Start and end index after trimming.
// Trim illegal chars from start
while ( sIx < eIx && IA[ sArr[ sIx ] ] < 0 )
sIx++ ;
// Trim illegal chars from end
while ( eIx > 0 && IA[ sArr[ eIx ] ] < 0 )
eIx-- ;
// get the padding count (=) (0, 1 or 2)
int pad = sArr[ eIx ] == '=' ? ( sArr[ eIx - 1 ] == '=' ? 2 : 1 ) : 0; // Count
// '='
// at
// end.
int cCnt = eIx - sIx + 1; // Content count including possible separators
int sepCnt = sLen > 76 ? ( sArr[ 76 ] == '\r' ? cCnt / 78 : 0 ) << 1 : 0;
int len = ( ( cCnt - sepCnt ) * 6 >> 3 ) - pad; // The number of decoded
// bytes
byte[] dArr = new byte[ len ]; // Preallocate byte[] of exact length
// Decode all but the last 0 - 2 bytes.
int d = 0;
for( int cc = 0 , eLen = ( len / 3 ) * 3 ; d < eLen ; ) {
// Assemble three bytes into an int from four "valid" characters.
int i = IA[ sArr[ sIx++ ] ] << 18 | IA[ sArr[ sIx++ ] ] << 12
| IA[ sArr[ sIx++ ] ] << 6 | IA[ sArr[ sIx++ ] ];
// Add the bytes
dArr[ d++ ] = ( byte ) ( i >> 16 );
dArr[ d++ ] = ( byte ) ( i >> 8 );
dArr[ d++ ] = ( byte ) i;
// If line separator, jump over it.
if( sepCnt > 0 && ++cc == 19 ) {
sIx += 2;
cc = 0;
}
}
if( d < len ) {
// Decode last 1-3 bytes (incl '=') into 1-3 bytes
int i = 0;
for( int j = 0 ; sIx <= eIx - pad ; j++ )
i |= IA[ sArr[ sIx++ ] ] << ( 18 - j * 6 );
for( int r = 16 ; d < len ; r -= 8 )
dArr[ d++ ] = ( byte ) ( i >> r );
}
return dArr;
}
private final static byte[] encodeToByte( byte[] sArr , boolean lineSep ) {
// Check special case
int sLen = sArr != null ? sArr.length : 0;
if( sLen == 0 )
return new byte[ 0 ];
int eLen = ( sLen / 3 ) * 3; // Length of even 24-bits.
int cCnt = ( ( sLen - 1 ) / 3 + 1 ) << 2; // Returned character count
int dLen = cCnt + ( lineSep ? ( cCnt - 1 ) / 76 << 1 : 0 ); // Length of
// returned
// array
byte[] dArr = new byte[ dLen ];
// Encode even 24-bits
for( int s = 0 , d = 0 , cc = 0 ; s < eLen ; ) {
// Copy next three bytes into lower 24 bits of int, paying attension to
// sign.
int i = ( sArr[ s++ ] & 0xff ) << 16 | ( sArr[ s++ ] & 0xff ) << 8
| ( sArr[ s++ ] & 0xff );
// Encode the int into four chars
dArr[ d++ ] = ( byte ) CA[ ( i >>> 18 ) & 0x3f ];
dArr[ d++ ] = ( byte ) CA[ ( i >>> 12 ) & 0x3f ];
dArr[ d++ ] = ( byte ) CA[ ( i >>> 6 ) & 0x3f ];
dArr[ d++ ] = ( byte ) CA[ i & 0x3f ];
// Add optional line separator
if( lineSep && ++cc == 19 && d < dLen - 2 ) {
dArr[ d++ ] = '\r';
dArr[ d++ ] = '\n';
cc = 0;
}
}
// Pad and encode last bits if source isn't an even 24 bits.
int left = sLen - eLen; // 0 - 2.
if( left > 0 ) {
// Prepare the int
int i = ( ( sArr[ eLen ] & 0xff ) << 10 )
| ( left == 2 ? ( ( sArr[ sLen - 1 ] & 0xff ) << 2 ) : 0 );
// Set last four chars
dArr[ dLen - 4 ] = ( byte ) CA[ i >> 12 ];
dArr[ dLen - 3 ] = ( byte ) CA[ ( i >>> 6 ) & 0x3f ];
dArr[ dLen - 2 ] = left == 2 ? ( byte ) CA[ i & 0x3f ] : ( byte ) '=';
dArr[ dLen - 1 ] = '=';
}
return dArr;
}
public final static byte[] decode( byte[] sArr ) {
byte[] tmp = new byte[ sArr.length ];
int t = 0;
for( int i = 0 , length = sArr.length ; i < length ; i++ ) {
if( ( ( sArr[i] >=65 ) && ( sArr[i] <= 90 ) )
|| ( ( sArr[i] >=97 ) && ( sArr[i] <= 122 ) )
|| ( ( sArr[i] >= 48) && ( sArr[i] <= 57 ) )
|| ( sArr[i] == 43 )
|| ( sArr[i] == 47 )
|| ( sArr[i] == 61 ) )
{
tmp[ t ] = sArr[ i ];
t += 1;
}
}
byte[] tmp2 = new byte[ t ];
System.arraycopy( tmp , 0 , tmp2 , 0 , t );
tmp = null;
return decodeFast( tmp2 );
}
private final static byte[] decodeFast( byte[] sArr ) {
// Check special case
int sLen = sArr.length;
if( sLen == 0 )
return new byte[ 0 ];
int sIx = 0 , eIx = sLen - 1; // Start and end index after trimming.
// Trim illegal chars from start
while ( sIx < eIx && IA[ sArr[ sIx ] & 0xff ] < 0 )
sIx++ ;
// Trim illegal chars from end
while ( eIx > 0 && IA[ sArr[ eIx ] & 0xff ] < 0 )
eIx-- ;
// get the padding count (=) (0, 1 or 2)
int pad = sArr[ eIx ] == '=' ? ( sArr[ eIx - 1 ] == '=' ? 2 : 1 ) : 0; // Count
// '='
// at
// end.
int cCnt = eIx - sIx + 1; // Content count including possible separators
int sepCnt = sLen > 76 ? ( sArr[ 76 ] == '\r' ? cCnt / 78 : 0 ) << 1 : 0;
int len = ( ( cCnt - sepCnt ) * 6 >> 3 ) - pad; // The number of decoded
// bytes
byte[] dArr = new byte[ len ]; // Preallocate byte[] of exact length
// Decode all but the last 0 - 2 bytes.
int d = 0;
for( int cc = 0 , eLen = ( len / 3 ) * 3 ; d < eLen ; ) {
// Assemble three bytes into an int from four "valid" characters.
int i = IA[ sArr[ sIx++ ] ] << 18 | IA[ sArr[ sIx++ ] ] << 12
| IA[ sArr[ sIx++ ] ] << 6 | IA[ sArr[ sIx++ ] ];
// Add the bytes
dArr[ d++ ] = ( byte ) ( i >> 16 );
dArr[ d++ ] = ( byte ) ( i >> 8 );
dArr[ d++ ] = ( byte ) i;
// If line separator, jump over it.
if( sepCnt > 0 && ++cc == 19 ) {
sIx += 2;
cc = 0;
}
}
if( d < len ) {
// Decode last 1-3 bytes (incl '=') into 1-3 bytes
int i = 0;
for( int j = 0 ; sIx <= eIx - pad ; j++ )
i |= IA[ sArr[ sIx++ ] ] << ( 18 - j * 6 );
for( int r = 16 ; d < len ; r -= 8 )
dArr[ d++ ] = ( byte ) ( i >> r );
}
return dArr;
}
private final static String encodeToString( byte[] sArr , boolean lineSep ) {
// Reuse char[] since we can't create a String incrementally anyway and
// StringBuffer/Builder would be slower.
return new String( encodeToChar( sArr , lineSep ) );
}
public final static byte[] decode( String sArr ) {
return decode( sArr.getBytes() );
}
private final static byte[] decodeFast( String s ) {
// Check special case
int sLen = s.length();
if( sLen == 0 )
return new byte[ 0 ];
int sIx = 0 , eIx = sLen - 1; // Start and end index after trimming.
// Trim illegal chars from start
while ( sIx < eIx && IA[ s.charAt( sIx ) & 0xff ] < 0 )
sIx++ ;
// Trim illegal chars from end
while ( eIx > 0 && IA[ s.charAt( eIx ) & 0xff ] < 0 )
eIx-- ;
// get the padding count (=) (0, 1 or 2)
int pad = s.charAt( eIx ) == '=' ? ( s.charAt( eIx - 1 ) == '=' ? 2 : 1 )
: 0; // Count '=' at end.
int cCnt = eIx - sIx + 1; // Content count including possible separators
int sepCnt = sLen > 76 ? ( s.charAt( 76 ) == '\r' ? cCnt / 78 : 0 ) << 1
: 0;
int len = ( ( cCnt - sepCnt ) * 6 >> 3 ) - pad; // The number of decoded
// bytes
byte[] dArr = new byte[ len ]; // Preallocate byte[] of exact length
// Decode all but the last 0 - 2 bytes.
int d = 0;
for( int cc = 0 , eLen = ( len / 3 ) * 3 ; d < eLen ; ) {
// Assemble three bytes into an int from four "valid" characters.
int i = IA[ s.charAt( sIx++ ) ] << 18 | IA[ s.charAt( sIx++ ) ] << 12
| IA[ s.charAt( sIx++ ) ] << 6 | IA[ s.charAt( sIx++ ) ];
// Add the bytes
dArr[ d++ ] = ( byte ) ( i >> 16 );
dArr[ d++ ] = ( byte ) ( i >> 8 );
dArr[ d++ ] = ( byte ) i;
// If line separator, jump over it.
if( sepCnt > 0 && ++cc == 19 ) {
sIx += 2;
cc = 0;
}
}
if( d < len ) {
// Decode last 1-3 bytes (incl '=') into 1-3 bytes
int i = 0;
for( int j = 0 ; sIx <= eIx - pad ; j++ )
i |= IA[ s.charAt( sIx++ ) ] << ( 18 - j * 6 );
for( int r = 16 ; d < len ; r -= 8 )
dArr[ d++ ] = ( byte ) ( i >> r );
}
return dArr;
}
public static void main( String[] args ) {
String a = "abcabc";
String b64 = encode( a.getBytes() );
System.out.println( b64 );
b64 = "YWJj\r\nYWJj\r\nYWJj\r\n";
byte[] x = decode( b64.getBytes() );
System.out.println( encode( x ) );
}
}
此处获得的字符串为:
/home/hoary/backup/gitlab/keepLatestTar.sh > /home/hoary/logs/keepLatestTar.log 2>&1
通过Base的编码和解码很好的避免了jsp发送的内容到servlet端字符产生转义