richie
6 years ago
commit
be82ec268d
16 changed files with 5431 additions and 0 deletions
Binary file not shown.
@ -0,0 +1,20 @@
|
||||
# http认证示例 |
||||
|
||||
在决策平台中做如下配置: |
||||
|
||||
![setting](screenshots/1.png) |
||||
|
||||
启动dist下的java应用程序: |
||||
|
||||
``` |
||||
java -jar http.jar |
||||
|
||||
``` |
||||
|
||||
可以看到类似的输出: |
||||
|
||||
![2](screenshots/2.png) |
||||
|
||||
这个时候登录决策平台,能看到测试认证服务器的一些提示输出: |
||||
|
||||
![3](screenshots/3.png) |
After Width: | Height: | Size: 225 KiB |
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 39 KiB |
@ -0,0 +1,121 @@
|
||||
import com.sun.net.httpserver.HttpExchange; |
||||
import com.sun.net.httpserver.HttpHandler; |
||||
import com.sun.net.httpserver.HttpServer; |
||||
import helper.KeyReader; |
||||
import helper.RSAUtils; |
||||
import helper.StringUtils; |
||||
|
||||
import java.io.IOException; |
||||
import java.io.OutputStream; |
||||
import java.io.UnsupportedEncodingException; |
||||
import java.net.InetSocketAddress; |
||||
import java.net.URI; |
||||
import java.net.URLDecoder; |
||||
import java.util.HashMap; |
||||
import java.util.Map; |
||||
|
||||
/** |
||||
* @author richie |
||||
* @version 10.0 |
||||
* Created by richie on 2018-12-03 |
||||
* http认证demo服务器 |
||||
*/ |
||||
public class Http { |
||||
|
||||
public static void main(String... args) throws IOException { |
||||
HttpServer server = HttpServer.create(new InetSocketAddress(8001), 0); |
||||
server.createContext("/demo", new AuthHandler()); |
||||
server.start(); |
||||
System.out.println("Server is started, please visit:http://localhost:8001/demo"); |
||||
} |
||||
|
||||
private static class AuthHandler implements HttpHandler { |
||||
|
||||
@Override |
||||
public void handle(HttpExchange exchange) throws IOException { |
||||
exchange.sendResponseHeaders(200, 0); |
||||
URI uri = exchange.getRequestURI(); |
||||
Map<String, String> parameters = parserQueryText(uri.getQuery()); |
||||
|
||||
String data = parameters.get("data"); |
||||
|
||||
// (必须)http认证的地方填的是公钥,则这里需要用私钥进行解密
|
||||
String text = RSAUtils.decrypt(data, KeyReader.getPrivateKey()); |
||||
|
||||
System.out.println("data:" + text); |
||||
|
||||
Map<String, String> map = parserText(text); |
||||
|
||||
String responseText; |
||||
|
||||
// username参数是从报表登录界面输入的地方获取的
|
||||
String username = map.get("username"); |
||||
// password参数是从报表登录界面输入的地方获取的
|
||||
String password = map.get("password"); |
||||
// uuid参数是报表发送http认证请求的时候生成的随机数
|
||||
String uuid = map.get("uuid"); |
||||
|
||||
if (isValidUser(username, password)) { |
||||
// (必须)认证成功时返回的文本格式{"success":"true","uuid":"xxx-yyy-zzz-dddd"}
|
||||
responseText = String.format("{\"success\":\"true\",\"uuid\":\"%s\"}", uuid); |
||||
} else { |
||||
responseText = "{\"success\":\"false\"}"; |
||||
} |
||||
|
||||
System.out.println("responseText:" + responseText); |
||||
|
||||
// (必须)这里需要把返回值用私钥进行加密,在报表服务器中,会自动使用公钥进行解密
|
||||
String returnValue = RSAUtils.encrypt(responseText, KeyReader.getPrivateKey()); |
||||
|
||||
OutputStream os = exchange.getResponseBody(); |
||||
os.write(returnValue.getBytes()); |
||||
os.close(); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 判断username和password是否可以正确的登录 |
||||
* @param username 用户名 |
||||
* @param password 密码 |
||||
* @return 如果能正确登录,则这里返回true表示,如果不能正确登录,则这里返回false表示 |
||||
*/ |
||||
private static boolean isValidUser(String username, String password) { |
||||
if (username == null || password == null) { |
||||
return false; |
||||
} |
||||
// 这里只是一个示例,当用户名和密码输入一样的时候,我们假设认证成功,允许登录
|
||||
return username.equals(password); |
||||
} |
||||
|
||||
private static Map<String, String> parserQueryText(String query) throws UnsupportedEncodingException { |
||||
Map<String, String> map = new HashMap<String, String>(); |
||||
if (query == null) { |
||||
return map; |
||||
} |
||||
String[] pairs = query.split("&"); |
||||
for (String pair : pairs) { |
||||
int idx = pair.indexOf("="); |
||||
map.put(URLDecoder.decode(pair.substring(0, idx), "UTF-8"), URLDecoder.decode(pair.substring(idx + 1), "UTF-8")); |
||||
} |
||||
return map; |
||||
} |
||||
|
||||
private static Map<String, String> parserText(String text) { |
||||
Map<String, String> map = new HashMap<>(); |
||||
if (StringUtils.isEmpty(text)) { |
||||
return map; |
||||
} |
||||
if (text.startsWith("{") && text.endsWith("}")) { |
||||
String[] arr = text.substring(1, text.length() - 1).split(","); |
||||
for (String child : arr) { |
||||
String[] pair = child.split(":"); |
||||
String key = pair[0]; |
||||
String value = pair[1]; |
||||
map.put(key.substring(1, key.length() - 1), value.substring(1, value.length() - 1)); |
||||
} |
||||
return map; |
||||
} else { |
||||
return map; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,11 @@
|
||||
package helper; |
||||
|
||||
/** |
||||
* @author richie |
||||
* @version 10.0 |
||||
* Created by richie on 2018-12-03 |
||||
*/ |
||||
public class EncodeConstants { |
||||
|
||||
public static final String ENCODING_UTF_8 = "UTF-8"; |
||||
} |
@ -0,0 +1,89 @@
|
||||
package helper; |
||||
|
||||
import java.io.ByteArrayOutputStream; |
||||
import java.io.IOException; |
||||
import java.io.InputStream; |
||||
import java.nio.charset.StandardCharsets; |
||||
import java.security.PrivateKey; |
||||
import java.security.PublicKey; |
||||
import java.util.regex.Matcher; |
||||
import java.util.regex.Pattern; |
||||
|
||||
/** |
||||
* @author richie |
||||
* @version 10.0 |
||||
* Created by richie on 2018-12-03 |
||||
*/ |
||||
public class KeyReader { |
||||
|
||||
private static final int TW_MB = 32 * 1024; |
||||
|
||||
private static final Pattern PUBLIC_PATTERN = Pattern.compile("-----BEGIN PUBLIC KEY-----([\\s\\S]*?)-----END PUBLIC KEY-----"); |
||||
private static final Pattern PRIVATE_PATTERN = Pattern.compile("-----BEGIN PRIVATE KEY-----([\\s\\S]*?)-----END PRIVATE KEY-----"); |
||||
|
||||
private static PrivateKey privateKey = null; |
||||
private static PublicKey publicKey = null; |
||||
|
||||
static { |
||||
try { |
||||
String text = readFileContext(); |
||||
privateKey = RSAUtils.string2PrivateKey(getMatchedText(text, PRIVATE_PATTERN)); |
||||
publicKey = RSAUtils.string2PublicKey(getMatchedText(text, PUBLIC_PATTERN)); |
||||
} catch (Exception e) { |
||||
LoggerFactory.getLogger().error(e.getMessage(), e); |
||||
} |
||||
} |
||||
|
||||
public static PrivateKey getPrivateKey() { |
||||
return privateKey; |
||||
} |
||||
|
||||
public static PublicKey getPublicKey() { |
||||
return publicKey; |
||||
} |
||||
|
||||
private static String readFileContext() { |
||||
InputStream in = KeyReader.class.getResourceAsStream("/key.txt"); |
||||
byte[] bytes = inputStream2Bytes(in); |
||||
return new String(bytes, StandardCharsets.UTF_8); |
||||
} |
||||
|
||||
private static String getMatchedText(String text, Pattern pattern){ |
||||
Matcher m = pattern.matcher(text); |
||||
if (m.find()){ |
||||
return m.group(1).trim(); |
||||
} |
||||
return ""; |
||||
} |
||||
|
||||
private static byte[] inputStream2Bytes(InputStream in) { |
||||
if (in == null) { |
||||
return new byte[0]; |
||||
} |
||||
byte[] temp = new byte[TW_MB]; |
||||
ByteArrayOutputStream bi = new ByteArrayOutputStream(); |
||||
try { |
||||
int count; |
||||
while ((count = in.read(temp)) > 0) { |
||||
byte[] b4Add; |
||||
if (temp.length == count) { |
||||
b4Add = temp; |
||||
} else { |
||||
b4Add = ArrayUtils.subarray(temp, 0, count); |
||||
} |
||||
bi.write(b4Add); |
||||
} |
||||
} catch (IOException e) { |
||||
LoggerFactory.getLogger().error(e.getMessage(), e); |
||||
} finally { |
||||
try { |
||||
in.close(); |
||||
} catch (IOException e) { |
||||
LoggerFactory.getLogger().error(e.getMessage(), e); |
||||
} |
||||
} |
||||
|
||||
return bi.toByteArray(); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,16 @@
|
||||
package helper; |
||||
|
||||
/** |
||||
* @author richie |
||||
* @version 10.0 |
||||
* Created by richie on 2018-12-03 |
||||
*/ |
||||
public class Logger { |
||||
|
||||
public void error(String message, Throwable t) { |
||||
System.out.println("error:" + message); |
||||
if (t != null) { |
||||
t.printStackTrace(); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,15 @@
|
||||
package helper; |
||||
|
||||
/** |
||||
* @author richie |
||||
* @version 10.0 |
||||
* Created by richie on 2018-12-03 |
||||
*/ |
||||
public class LoggerFactory { |
||||
|
||||
private static Logger logger = new Logger(); |
||||
|
||||
public static Logger getLogger() { |
||||
return logger; |
||||
} |
||||
} |
@ -0,0 +1,265 @@
|
||||
package helper; |
||||
|
||||
import sun.misc.BASE64Decoder; |
||||
import sun.misc.BASE64Encoder; |
||||
|
||||
import javax.crypto.BadPaddingException; |
||||
import javax.crypto.Cipher; |
||||
import javax.crypto.IllegalBlockSizeException; |
||||
import java.io.IOException; |
||||
import java.io.UnsupportedEncodingException; |
||||
import java.security.Key; |
||||
import java.security.KeyFactory; |
||||
import java.security.KeyPair; |
||||
import java.security.KeyPairGenerator; |
||||
import java.security.MessageDigest; |
||||
import java.security.PrivateKey; |
||||
import java.security.PublicKey; |
||||
import java.security.spec.PKCS8EncodedKeySpec; |
||||
import java.security.spec.X509EncodedKeySpec; |
||||
|
||||
|
||||
public class RSAUtils { |
||||
|
||||
private static final int FRAGMENT_LENGTH = 245; |
||||
private static final int FRAGMENT_LENGTH_DECRYPT = 256; |
||||
|
||||
|
||||
/** |
||||
* RSA加密 |
||||
* |
||||
* @param plainText 要加密的文本内容 |
||||
* @param publicKey 用于加密的公钥 |
||||
* @return 加密后的内容 |
||||
*/ |
||||
public static String encrypt(String plainText, Key publicKey) { |
||||
if (StringUtils.isEmpty(plainText)) { |
||||
return plainText; |
||||
} |
||||
try { |
||||
byte[] publicEncrypt = encrypt(plainText.getBytes(EncodeConstants.ENCODING_UTF_8), publicKey); |
||||
return RSAUtils.byte2Base64(publicEncrypt); |
||||
} catch (UnsupportedEncodingException e) { |
||||
throw new RuntimeException(e); |
||||
} |
||||
} |
||||
|
||||
|
||||
/** |
||||
* RSA解密 |
||||
* |
||||
* @param cipherText 密文数据 |
||||
* @return 解密后的内容 |
||||
*/ |
||||
public static String decrypt(String cipherText, Key privateKey) { |
||||
|
||||
if (StringUtils.isEmpty(cipherText)) { |
||||
return cipherText; |
||||
} |
||||
byte[] bytes = null; |
||||
try { |
||||
bytes = decrypt(base642Byte(cipherText), privateKey); |
||||
} catch (Exception e) { |
||||
LoggerFactory.getLogger().error(e.getMessage(), e); |
||||
} |
||||
if (bytes == null) { |
||||
return null; |
||||
} |
||||
try { |
||||
return new String(bytes, EncodeConstants.ENCODING_UTF_8); |
||||
} catch (UnsupportedEncodingException e) { |
||||
throw new RuntimeException(e); |
||||
} |
||||
} |
||||
|
||||
|
||||
|
||||
/** |
||||
* RSA加密 |
||||
* |
||||
* @param plainTextData 要加密的内容 |
||||
* @param publicKey 用于加密的公钥 |
||||
* @return 加密后的内容 |
||||
*/ |
||||
public static byte[] encrypt(byte[] plainTextData, Key publicKey) { |
||||
|
||||
if (ArrayUtils.isEmpty(plainTextData)) { |
||||
return plainTextData; |
||||
} |
||||
try { |
||||
Cipher c1 = Cipher.getInstance("RSA"); |
||||
c1.init(Cipher.ENCRYPT_MODE, publicKey); |
||||
return dealEncryptFragment(plainTextData, c1); |
||||
} catch (Exception e) { |
||||
LoggerFactory.getLogger().error(e.getMessage(), e); |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
private static byte[] dealEncryptFragment(byte[] data, Cipher cipher) throws IllegalBlockSizeException, BadPaddingException { |
||||
|
||||
byte[] result = new byte[]{}; |
||||
int i; |
||||
for (i = 0; i < data.length; i += FRAGMENT_LENGTH) { |
||||
byte[] fragment = ArrayUtils.subarray(data, i, i + FRAGMENT_LENGTH); |
||||
byte[] update = cipher.doFinal(fragment); |
||||
result = ArrayUtils.addAll(result, update); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
|
||||
/** |
||||
* RSA解密 |
||||
* |
||||
* @param cipherData 密文数据 |
||||
* @param privateKey 用于解密的私钥 |
||||
* @return 解密后的内容 |
||||
*/ |
||||
public static byte[] decrypt(byte[] cipherData, Key privateKey) { |
||||
try { |
||||
Cipher c1 = Cipher.getInstance("RSA"); |
||||
c1.init(Cipher.DECRYPT_MODE, privateKey); |
||||
return dealDecryptFragment(cipherData, c1); |
||||
} catch (Exception e) { |
||||
LoggerFactory.getLogger().error(e.getMessage(), e); |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
|
||||
private static byte[] dealDecryptFragment(byte[] data, Cipher cipher) throws IllegalBlockSizeException, BadPaddingException { |
||||
|
||||
byte[] result = new byte[]{}; |
||||
int i; |
||||
for (i = 0; i < data.length; i += FRAGMENT_LENGTH_DECRYPT) { |
||||
byte[] fragment = ArrayUtils.subarray(data, i, i + FRAGMENT_LENGTH_DECRYPT); |
||||
byte[] update = cipher.doFinal(fragment); |
||||
result = ArrayUtils.addAll(result, update); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
|
||||
|
||||
public static String sha256(String plainTextData) { |
||||
if (StringUtils.isEmpty(plainTextData)) { |
||||
return plainTextData; |
||||
} |
||||
try { |
||||
byte[] bytes = sha256(plainTextData.getBytes(EncodeConstants.ENCODING_UTF_8)); |
||||
return byteArrayToHexString(bytes); |
||||
} catch (UnsupportedEncodingException e) { |
||||
LoggerFactory.getLogger().error(e.getMessage(), e); |
||||
} |
||||
return plainTextData; |
||||
} |
||||
|
||||
private static byte[] sha256(byte[] plainTextData) { |
||||
if (plainTextData == null || ArrayUtils.isEmpty(plainTextData)) { |
||||
return plainTextData; |
||||
} |
||||
try { |
||||
MessageDigest messageDigest = MessageDigest.getInstance("SHA-256"); |
||||
messageDigest.update(plainTextData); |
||||
return messageDigest.digest(); |
||||
} catch (Exception e) { |
||||
LoggerFactory.getLogger().error(e.getMessage(), e); |
||||
} |
||||
return plainTextData; |
||||
} |
||||
|
||||
/** |
||||
* 将加密后的字节数组转换成字符串 |
||||
* |
||||
* @param b 字节数组 |
||||
* @return 字符串 |
||||
*/ |
||||
public static String byteArrayToHexString(byte[] b) { |
||||
StringBuilder hs = new StringBuilder(); |
||||
String tempStr; |
||||
for (int n = 0; b != null && n < b.length; n++) { |
||||
tempStr = Integer.toHexString(b[n] & 0XFF); |
||||
if (tempStr.length() == 1) |
||||
hs.append('0'); |
||||
hs.append(tempStr); |
||||
} |
||||
return hs.toString().toLowerCase(); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* 生成一个2048位的RSA秘钥对 |
||||
* |
||||
* @return 秘钥对 |
||||
* @throws Exception 如果无法生成秘钥对则抛出次异常 |
||||
*/ |
||||
public static KeyPair getKeyPair() throws Exception { |
||||
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); |
||||
keyPairGenerator.initialize(2048); |
||||
return keyPairGenerator.generateKeyPair(); |
||||
} |
||||
|
||||
/** |
||||
* 获取RSA公钥的base64编码字符串 |
||||
* |
||||
* @param keyPair 秘钥对 |
||||
* @return 公钥编码字符串 |
||||
*/ |
||||
public static String getPublicKey(KeyPair keyPair) { |
||||
PublicKey publicKey = keyPair.getPublic(); |
||||
byte[] bytes = publicKey.getEncoded(); |
||||
return byte2Base64(bytes); |
||||
} |
||||
|
||||
/** |
||||
* 获取RSA私钥的base64编码字符串 |
||||
* |
||||
* @param keyPair 秘钥对 |
||||
* @return 私钥编码字符串 |
||||
*/ |
||||
public static String getPrivateKey(KeyPair keyPair) { |
||||
PrivateKey privateKey = keyPair.getPrivate(); |
||||
byte[] bytes = privateKey.getEncoded(); |
||||
return byte2Base64(bytes); |
||||
} |
||||
|
||||
public static PublicKey string2PublicKey(String pubStr) { |
||||
try { |
||||
byte[] keyBytes = base642Byte(pubStr); |
||||
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes); |
||||
KeyFactory keyFactory = KeyFactory.getInstance("RSA"); |
||||
return keyFactory.generatePublic(keySpec); |
||||
} catch (Exception e) { |
||||
LoggerFactory.getLogger().error(e.getMessage(), e); |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
public static PrivateKey string2PrivateKey(String priStr) { |
||||
try { |
||||
byte[] keyBytes = base642Byte(priStr); |
||||
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes); |
||||
KeyFactory keyFactory = KeyFactory.getInstance("RSA"); |
||||
return keyFactory.generatePrivate(keySpec); |
||||
} catch (Exception e) { |
||||
LoggerFactory.getLogger().error(e.getMessage(), e); |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
public static String byte2Base64(byte[] bytes) { |
||||
BASE64Encoder encoder = new BASE64Encoder(); |
||||
return encoder.encode(bytes); |
||||
} |
||||
|
||||
public static byte[] base642Byte(String base64Key) throws IOException { |
||||
BASE64Decoder decoder = new BASE64Decoder(); |
||||
return decoder.decodeBuffer(base64Key); |
||||
} |
||||
|
||||
public static void main(String... args) { |
||||
|
||||
} |
||||
|
||||
} |
@ -0,0 +1,555 @@
|
||||
package helper; |
||||
|
||||
import java.io.UnsupportedEncodingException; |
||||
import java.util.Arrays; |
||||
import java.util.StringTokenizer; |
||||
|
||||
/** |
||||
* <p>Operations on {@link java.lang.String} that are |
||||
* <code>null</code> safe.</p> |
||||
* <p/> |
||||
* <ul> |
||||
* <li><b>IsEmpty/IsBlank</b> |
||||
* - checks if a String contains text</li> |
||||
* <li><b>Trim/Strip</b> |
||||
* - removes leading and trailing whitespace</li> |
||||
* <li><b>Equals</b> |
||||
* - compares two strings null-safe</li> |
||||
* <li><b>startsWith</b> |
||||
* - check if a String starts with a prefix null-safe</li> |
||||
* <li><b>endsWith</b> |
||||
* - check if a String ends with a suffix null-safe</li> |
||||
* <li><b>IndexOf/LastIndexOf/Contains</b> |
||||
* - null-safe index-of checks |
||||
* <li><b>IndexOfAny/LastIndexOfAny/IndexOfAnyBut/LastIndexOfAnyBut</b> |
||||
* - index-of any of a set of Strings</li> |
||||
* <li><b>ContainsOnly/ContainsNone/ContainsAny</b> |
||||
* - does String contains only/none/any of these characters</li> |
||||
* <li><b>Substring/Left/Right/Mid</b> |
||||
* - null-safe substring extractions</li> |
||||
* <li><b>SubstringBefore/SubstringAfter/SubstringBetween</b> |
||||
* - substring extraction relative to other strings</li> |
||||
* <li><b>Split/Join</b> |
||||
* - splits a String into an array of substrings and vice versa</li> |
||||
* <li><b>Remove/Delete</b> |
||||
* - removes part of a String</li> |
||||
* <li><b>Replace/Overlay</b> |
||||
* - Searches a String and replaces one String with another</li> |
||||
* <li><b>Chomp/Chop</b> |
||||
* - removes the last part of a String</li> |
||||
* <li><b>LeftPad/RightPad/Center/Repeat</b> |
||||
* - pads a String</li> |
||||
* <li><b>UpperCase/LowerCase/SwapCase/Capitalize/Uncapitalize</b> |
||||
* - changes the case of a String</li> |
||||
* <li><b>CountMatches</b> |
||||
* - counts the number of occurrences of one String in another</li> |
||||
* <li><b>IsAlpha/IsNumeric/IsWhitespace/IsAsciiPrintable</b> |
||||
* - checks the characters in a String</li> |
||||
* <li><b>DefaultString</b> |
||||
* - protects against a null input String</li> |
||||
* <li><b>Reverse/ReverseDelimited</b> |
||||
* - reverses a String</li> |
||||
* <li><b>Abbreviate</b> |
||||
* - abbreviates a string using ellipsis</li> |
||||
* <li><b>Difference</b> |
||||
* - compares Strings and reports on their differences</li> |
||||
* <li><b>LevensteinDistance</b> |
||||
* - the number of changes needed to change one String into another</li> |
||||
* </ul> |
||||
* <p/> |
||||
* <p>The <code>helper.StringUtils</code> class defines certain words related to |
||||
* String handling.</p> |
||||
* <p/> |
||||
* <ul> |
||||
* <li>null - <code>null</code></li> |
||||
* <li>empty - a zero-length string (<code>""</code>)</li> |
||||
* <li>space - the space character (<code>' '</code>, char 32)</li> |
||||
* <li>whitespace - the characters defined by {@link Character#isWhitespace(char)}</li> |
||||
* <li>trim - the characters <= 32 as in {@link String#trim()}</li> |
||||
* </ul> |
||||
* <p/> |
||||
* <p><code>helper.StringUtils</code> handles <code>null</code> input Strings quietly. |
||||
* That is to say that a <code>null</code> input will return <code>null</code>. |
||||
* Where a <code>boolean</code> or <code>int</code> is being returned |
||||
* details vary by method.</p> |
||||
* <p/> |
||||
* <p>A side effect of the <code>null</code> handling is that a |
||||
* <code>NullPointerException</code> should be considered a bug in |
||||
* <code>helper.StringUtils</code> (except for deprecated methods).</p> |
||||
* <p/> |
||||
* <p>Methods in this class give sample code to explain their operation. |
||||
* The symbol <code>*</code> is used to indicate any input including <code>null</code>.</p> |
||||
* |
||||
* @author <a href="http://jakarta.apache.org/turbine/">Apache Jakarta Turbine</a> |
||||
* @author <a href="mailto:jon@latchkey.com">Jon S. Stevens</a> |
||||
* @author Daniel L. Rall |
||||
* @author <a href="mailto:gcoladonato@yahoo.com">Greg Coladonato</a> |
||||
* @author <a href="mailto:ed@apache.org">Ed Korthof</a> |
||||
* @author <a href="mailto:rand_mcneely@yahoo.com">Rand McNeely</a> |
||||
* @author Stephen Colebourne |
||||
* @author <a href="mailto:fredrik@westermarck.com">Fredrik Westermarck</a> |
||||
* @author Holger Krauth |
||||
* @author <a href="mailto:alex@purpletech.com">Alexander Day Chaffee</a> |
||||
* @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a> |
||||
* @author Arun Mammen Thomas |
||||
* @author Gary Gregory |
||||
* @author Phil Steitz |
||||
* @author Al Chou |
||||
* @author Michael Davey |
||||
* @author Reuben Sivan |
||||
* @author Chris Hyzer |
||||
* @author Scott Johnson |
||||
* @version $Id: helper.StringUtils.java 635447 2008-03-10 06:27:09Z bayard $ |
||||
* @see java.lang.String |
||||
* @since 1.0 |
||||
*/ |
||||
public class StringUtils { |
||||
// Performance testing notes (JDK 1.4, Jul03, scolebourne)
|
||||
// Whitespace:
|
||||
// Character.isWhitespace() is faster than WHITESPACE.indexOf()
|
||||
// where WHITESPACE is a string of all whitespace characters
|
||||
//
|
||||
// Character access:
|
||||
// String.charAt(n) versus toCharArray(), then array[n]
|
||||
// String.charAt(n) is about 15% worse for a 10K string
|
||||
// They are about equal for a length 50 string
|
||||
// String.charAt(n) is about 4 times better for a length 3 string
|
||||
// String.charAt(n) is best bet overall
|
||||
//
|
||||
// Append:
|
||||
// String.concat about twice as fast as StringBuffer.append
|
||||
// (not sure who tested this)
|
||||
|
||||
/** |
||||
* 空字符串 |
||||
*/ |
||||
public static final String EMPTY = ""; |
||||
|
||||
/** |
||||
* 空白字符串 |
||||
*/ |
||||
public static final String BLANK = " "; |
||||
|
||||
|
||||
private StringUtils() { |
||||
|
||||
} |
||||
|
||||
/** |
||||
* 生成一个非null的字符串 |
||||
* |
||||
* @param txt 原对象 |
||||
* @return 非null的字符窜 |
||||
*/ |
||||
public static String alwaysNotNull(String txt) { |
||||
|
||||
return txt == null ? StringUtils.EMPTY : txt; |
||||
} |
||||
|
||||
|
||||
// Empty checks
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
/** |
||||
* <p>检查一个字符串是否是空字符串</p> |
||||
* <p/> |
||||
* <pre> |
||||
* helper.StringUtils.isEmpty(null) = true |
||||
* helper.StringUtils.isEmpty("") = true |
||||
* helper.StringUtils.isEmpty(" ") = false |
||||
* helper.StringUtils.isEmpty("bob") = false |
||||
* helper.StringUtils.isEmpty(" bob ") = false |
||||
* </pre> |
||||
* <p/> |
||||
* |
||||
* @param str 被检查的字符串,可能为null |
||||
* @return 如果字符串为空或者是null则返回true,否则返回false |
||||
*/ |
||||
public static boolean isEmpty(String str) { |
||||
|
||||
return str == null || str.length() == 0; |
||||
} |
||||
|
||||
/** |
||||
* <p>检查一个字符串是否不为空字符串</p> |
||||
* <p/> |
||||
* <pre> |
||||
* helper.StringUtils.isNotEmpty(null) = false |
||||
* helper.StringUtils.isNotEmpty("") = false |
||||
* helper.StringUtils.isNotEmpty(" ") = true |
||||
* helper.StringUtils.isNotEmpty("bob") = true |
||||
* helper.StringUtils.isNotEmpty(" bob ") = true |
||||
* </pre> |
||||
* |
||||
* @param str 被检查的字符串,可能是null |
||||
* @return 如果字符串不为空且不是null则返回true,否则返回false |
||||
*/ |
||||
public static boolean isNotEmpty(String str) { |
||||
|
||||
return !StringUtils.isEmpty(str); |
||||
} |
||||
|
||||
/** |
||||
* <p>检查一个字符串是否为空白字符串</p> |
||||
* <p/> |
||||
* <pre> |
||||
* helper.StringUtils.isBlank(null) = true |
||||
* helper.StringUtils.isBlank("") = true |
||||
* helper.StringUtils.isBlank(" ") = true |
||||
* helper.StringUtils.isBlank("bob") = false |
||||
* helper.StringUtils.isBlank(" bob ") = false |
||||
* </pre> |
||||
* |
||||
* @param str 被检查的字符串 |
||||
* @return 如果字符串为空、空格符或者null那么返回true,否则返回false |
||||
*/ |
||||
public static boolean isBlank(String str) { |
||||
|
||||
int strLen; |
||||
return str == null || (strLen = str.length()) == 0 ? true : isBlank(str, strLen); |
||||
} |
||||
|
||||
/** |
||||
* for JIT |
||||
*/ |
||||
private static boolean isBlank(String str, int strLen) { |
||||
|
||||
for (int i = 0; i < strLen; i++) { |
||||
if ((Character.isWhitespace(str.charAt(i)) == false)) { |
||||
return false; |
||||
} |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
/** |
||||
* 处理已A字符开头的字符,去除掉A |
||||
* |
||||
* @param org 原字符 |
||||
* @param mark 标记字符 |
||||
* @return 处理后的字符 |
||||
*/ |
||||
public static String cutStringStartWith(String org, String mark) { |
||||
|
||||
if (org == null) { |
||||
return null; |
||||
} |
||||
|
||||
if (mark == null) { |
||||
return org; |
||||
} |
||||
if (!org.startsWith(mark)) { |
||||
return org; |
||||
} |
||||
return org.substring(mark.length()); |
||||
} |
||||
|
||||
/** |
||||
* 1??某一个特定字符B结束的字符AB,得到去掉B的前面的字符A |
||||
* |
||||
* @param org 全部字符 |
||||
* @param mark 标记B |
||||
* @return 处理后的字符 |
||||
*/ |
||||
public static String cutStringEndWith(String org, String mark) { |
||||
|
||||
if (org == null) { |
||||
return null; |
||||
} |
||||
|
||||
if (mark == null) { |
||||
return org; |
||||
} |
||||
|
||||
if (!org.endsWith(mark)) { |
||||
return org; |
||||
} |
||||
int location = org.indexOf(mark); |
||||
if (location == -1) { |
||||
return org; |
||||
} |
||||
return org.substring(0, location); |
||||
} |
||||
|
||||
/** |
||||
* <p>检查一个字符串是否不是空白字符串</p> |
||||
* <p/> |
||||
* <pre> |
||||
* helper.StringUtils.isNotBlank(null) = false |
||||
* helper.StringUtils.isNotBlank("") = false |
||||
* helper.StringUtils.isNotBlank(" ") = false |
||||
* helper.StringUtils.isNotBlank("bob") = true |
||||
* helper.StringUtils.isNotBlank(" bob ") = true |
||||
* </pre> |
||||
* |
||||
* @param str 被检查的字符串 |
||||
* @return 如果字符串不是空字符串、空格符以及null那么就返回true,否则返回false |
||||
*/ |
||||
public static boolean isNotBlank(String str) { |
||||
|
||||
return !StringUtils.isBlank(str); |
||||
} |
||||
|
||||
// Trim
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
/** |
||||
* <p>Removes control characters (char <= 32) from both |
||||
* ends of this String, handling <code>null</code> by returning |
||||
* <code>null</code>.</p> |
||||
* <p/> |
||||
* <p>The String is trimmed using {@link String#trim()}. |
||||
* Trim removes start and end characters <= 32. |
||||
* To strip whitespace use {@link #(String)}.</p> |
||||
* <p/> |
||||
* <p>To trim your choice of characters, use the |
||||
* {@link #(String, String)} methods.</p> |
||||
* <p/> |
||||
* <pre> |
||||
* helper.StringUtils.trim(null) = null |
||||
* helper.StringUtils.trim("") = "" |
||||
* helper.StringUtils.trim(" ") = "" |
||||
* helper.StringUtils.trim("abc") = "abc" |
||||
* helper.StringUtils.trim(" abc ") = "abc" |
||||
* </pre> |
||||
* |
||||
* @param str the String to be trimmed, may be null |
||||
* @return the trimmed string, <code>null</code> if null String input |
||||
*/ |
||||
public static String trim(String str) { |
||||
|
||||
return str == null ? null : str.trim(); |
||||
} |
||||
|
||||
/** |
||||
* 去掉字符串首尾的空白,如果剩余的结果是空白字符串那么返回null |
||||
*/ |
||||
public static String trimToNull(String str) { |
||||
|
||||
String ts = trim(str); |
||||
return isEmpty(ts) ? null : ts; |
||||
} |
||||
|
||||
/** |
||||
* 检查一个字符串是否以某个指定的字符串开始,如果是的话不做改变,如果不是的话将把指定的字符串添加到原 |
||||
* 字符串的起始位置 |
||||
*/ |
||||
public static String perfectStart(String str, String attach) { |
||||
|
||||
if (str == null) { |
||||
return attach; |
||||
} |
||||
return str.startsWith(attach) ? str : (attach + str); |
||||
} |
||||
|
||||
/** |
||||
* 检查一个字符串是否以某个指定的字符串结束,如果是的话不做改变,如果不是的话将把指定的字符串添加到原 |
||||
* 字符串的末尾位置 |
||||
*/ |
||||
public static String perfectEnd(String str, String attach) { |
||||
|
||||
if (str == null) { |
||||
return attach; |
||||
} |
||||
return str.endsWith(attach) ? str : (str + attach); |
||||
} |
||||
|
||||
public static String perfectSurround(String str, String attach) { |
||||
|
||||
if (str == null) { |
||||
return attach; |
||||
} |
||||
str = str.endsWith(attach) ? str : (str + attach); |
||||
str = str.startsWith(attach) ? str : (attach + str); |
||||
|
||||
return str; |
||||
} |
||||
|
||||
/** |
||||
* 获取字符串的长度,如果是null则返回0 |
||||
*/ |
||||
public static int getLength(String str) { |
||||
|
||||
return str == null ? 0 : str.length(); |
||||
} |
||||
|
||||
/** |
||||
* richer:判断两个字符串是否出去前后的附加字符串外是相等的 |
||||
* eg:equalsIgnore("/File/", "File", "/") == true |
||||
* 不支持传null path |
||||
*/ |
||||
public static boolean equalsIgnore(String str1, String str2, String attach) { |
||||
if(str1 ==null || str2 == null) { |
||||
throw new RuntimeException("null path"); |
||||
} |
||||
return equals(str1, str2) || perfectStart(perfectEnd(str1, attach), attach).equals(perfectStart(perfectEnd(str2, attach), attach)); |
||||
} |
||||
|
||||
/** |
||||
* 判断字符串text是否包含字符串ch |
||||
* |
||||
* @param text 主字符串 |
||||
* @param ch 寻找的字符串 |
||||
* @return 是否包含 |
||||
* @date 2014-12-3-上午11:50:25 |
||||
*/ |
||||
public static boolean contains(String text, String ch) { |
||||
|
||||
return text != null && text.indexOf(ch) > -1; |
||||
} |
||||
|
||||
/** |
||||
* text 做兼容类. |
||||
* |
||||
* @param text 文本兼容 |
||||
* @return StringTokenizer |
||||
*/ |
||||
public static StringTokenizer text2StringTokenizer(String text) { |
||||
|
||||
return new StringTokenizer(text, "\r\n"); |
||||
} |
||||
|
||||
/** |
||||
* carl:拼接数组字符 |
||||
*/ |
||||
public static String join(String seperator, String[] strings) { |
||||
|
||||
if (strings == null) { |
||||
return null; |
||||
} |
||||
int length = strings.length; |
||||
if (length == 0) { |
||||
return ""; |
||||
} |
||||
StringBuffer buf = new StringBuffer(length * strings[0].length()) |
||||
.append(strings[0]); |
||||
for (int i = 1; i < length; i++) { |
||||
buf.append(seperator).append(strings[i]); |
||||
} |
||||
return buf.toString(); |
||||
} |
||||
|
||||
public static String parseVersion(String xmlDesignerVersion) { |
||||
|
||||
xmlDesignerVersion = xmlDesignerVersion.replace('A', '0'); |
||||
xmlDesignerVersion = xmlDesignerVersion.replace('B', '1'); |
||||
xmlDesignerVersion = xmlDesignerVersion.replace('C', '2'); |
||||
xmlDesignerVersion = xmlDesignerVersion.replace('D', '3'); |
||||
xmlDesignerVersion = xmlDesignerVersion.replace('E', '4'); |
||||
xmlDesignerVersion = xmlDesignerVersion.replace('F', '5'); |
||||
xmlDesignerVersion = xmlDesignerVersion.replace('G', '6'); |
||||
xmlDesignerVersion = xmlDesignerVersion.replace('H', '7'); |
||||
xmlDesignerVersion = xmlDesignerVersion.replace('I', '8'); |
||||
xmlDesignerVersion = xmlDesignerVersion.replace('J', '9'); |
||||
return xmlDesignerVersion; |
||||
} |
||||
|
||||
/** |
||||
* 是否是数组类型的字符串 |
||||
* |
||||
* @param text 目标字符串 |
||||
* @return 是否是数组类型的字符串 |
||||
*/ |
||||
public static boolean isArrayType(String text) { |
||||
|
||||
return text != null && ((text.startsWith("[") && text.endsWith("]")) || (text.startsWith("[[") && text.endsWith("]]"))); |
||||
} |
||||
|
||||
public static String[][] stringToArray(String v) { |
||||
//[["华东","江苏"],["华东","上海"]] 或者 ["华东","江苏"]
|
||||
if (isArrayType(v)) { |
||||
v = v.replaceAll("\"", ""); |
||||
if (v.startsWith("[[") && v.endsWith("]]")) { |
||||
String[] temp = (v.substring(2, v.length() - 2)).split("],\\["); |
||||
String[][] strs = new String[temp.length][]; |
||||
for (int i = 0; i < strs.length; i++) { |
||||
strs[i] = temp[i].split(","); |
||||
} |
||||
return strs; |
||||
} else { |
||||
String[][] strs = new String[1][]; |
||||
strs[0] = v.substring(1, v.length() - 1).split(","); |
||||
return strs; |
||||
} |
||||
} else { |
||||
//华东,江苏;华东,上海
|
||||
String[] temp = v.split(";"); |
||||
String[][] strs = new String[temp.length][]; |
||||
for (int i = 0; i < strs.length; i++) { |
||||
strs[i] = temp[i].split(","); |
||||
} |
||||
return strs; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 根据字节数截取字符串 |
||||
* |
||||
* @param originString 原始字符串 |
||||
* @param charsetName 字符编码 |
||||
* @param byteLength 字节长度 |
||||
* @return 截取后的字符串 |
||||
* @throws UnsupportedEncodingException |
||||
*/ |
||||
public static String subStringByByteLength(String originString, String charsetName, int byteLength) |
||||
throws UnsupportedEncodingException { |
||||
|
||||
if (StringUtils.isBlank(originString) || byteLength <= 0) { |
||||
return StringUtils.EMPTY; |
||||
} |
||||
char[] chars = originString.toCharArray(); |
||||
int length = 0, index = chars.length; |
||||
for (int i = 0; i < chars.length; i++) { |
||||
final int len = String.valueOf(chars[i]).getBytes(charsetName).length + length; |
||||
if (len <= byteLength) { |
||||
length = len; |
||||
} else { |
||||
index = i; |
||||
break; |
||||
} |
||||
} |
||||
return String.valueOf(chars, 0, index); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* peter:比较相等. |
||||
* |
||||
* @param obj1 may be null. |
||||
* @param obj2 may be null. |
||||
*/ |
||||
public static boolean equals(String obj1, String obj2) { |
||||
|
||||
if (obj1 == obj2) { |
||||
return true; |
||||
} |
||||
if (obj1 == null || obj2 == null) { |
||||
return false; |
||||
} |
||||
return obj1.equals(obj2); |
||||
} |
||||
|
||||
/** |
||||
* 不区分大小写比较字符串 |
||||
*/ |
||||
public static boolean equalsIgnoreCase(String s1, String s2) { |
||||
|
||||
if (s1 == null) { |
||||
return s2 == null; |
||||
} |
||||
return s2 != null && s1.equalsIgnoreCase(s2); |
||||
} |
||||
|
||||
/** |
||||
* 右侧填充空格直到字符串达到指定长度 |
||||
*/ |
||||
public static String rightPad(String str, int size) { |
||||
int len = size - str.length(); |
||||
if (len <= 0) { |
||||
return str; |
||||
} |
||||
char[] spaces = new char[len]; |
||||
Arrays.fill(spaces, ' '); |
||||
return str + String.valueOf(spaces); |
||||
} |
||||
} |
@ -0,0 +1,32 @@
|
||||
-----BEGIN PUBLIC KEY----- |
||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx9rvqPePsTPZ+vARSpi7wfg8KokGUqnC |
||||
RVUzgQKFOMz/oGVvf9MPXyxw4NVYNJFWpyj1tBMXfsoPZs6/ET8/xd29xbl5UihMhz2AR+0Pu0ym |
||||
1znQRSp8SWInvAZxJe/B1fkhWyor+JYLOW5tN2sFlZ8NEFLjoGJ4AbjoMbcX3aHgCOaat5/+GNIr |
||||
R0yirJkv+gPgeKiO0H4YMbrqQaMhsSn/9WVCEh9sL5VQHi/Q5vQI4Qfs2cLBgQ7aH9LfnYnKoMD4 |
||||
mlczgmE56XVjuA7BpwdFr+AYx2/NY/K83Dpckxmv/WbsHKU8nPqfCo6kmqctDz3ieZSI7RK9fFsi |
||||
AYS7GwIDAQAB |
||||
-----END PUBLIC KEY----- |
||||
-----BEGIN PRIVATE KEY----- |
||||
MIIEwAIBADANBgkqhkiG9w0BAQEFAASCBKowggSmAgEAAoIBAQDH2u+o94+xM9n68BFKmLvB+Dwq |
||||
iQZSqcJFVTOBAoU4zP+gZW9/0w9fLHDg1Vg0kVanKPW0Exd+yg9mzr8RPz/F3b3FuXlSKEyHPYBH |
||||
7Q+7TKbXOdBFKnxJYie8BnEl78HV+SFbKiv4lgs5bm03awWVnw0QUuOgYngBuOgxtxfdoeAI5pq3 |
||||
n/4Y0itHTKKsmS/6A+B4qI7QfhgxuupBoyGxKf/1ZUISH2wvlVAeL9Dm9AjhB+zZwsGBDtof0t+d |
||||
icqgwPiaVzOCYTnpdWO4DsGnB0Wv4BjHb81j8rzcOlyTGa/9ZuwcpTyc+p8KjqSapy0PPeJ5lIjt |
||||
Er18WyIBhLsbAgMBAAECggEBAKMzQJPdHQTaT72f/q7IzEIYQKGHZZdpMtBFRBqsgTeiB3jmfEeI |
||||
hbv1YXPoI/BBYt58DzBuirgprqwIVnRyDtS4P3jP/ac+a0fgy/lwN3F+pZuJhW6FxBp0wffD1u/g |
||||
uovNthRo+qEzfZT3fM5NYbENwbA7z8+vuUnGwi2e0ylSzlGwMokmWRerPHocf7olkcFbd+ZenjiL |
||||
leM/MyNkxhtTgk1Jbrp09eZmK0pOg1fZJ3hm6uBPU5swFgMkP7z7LUdn23t2Ww36v7mjcDDJc74Y |
||||
VybvnWmYDpSYxTA1w7Wl8yZP6Ob55h/DnRHoxwwT0PauZSVpChhndTtCDXQYP1ECgYEA5l3kMl2x |
||||
n8xzKxPH9ZRNlM+HFZv2CaxVsFD7hp05UqStDY6cPvh2BJusEWIw06ngDouM3bxApEIgyGM1jsDI |
||||
mHekbtbmfsjCtmoIhc1q3zYyqV1P2CBy6i9vhxvZKLDamzE0eTpVhSTC/IcDP+Q1QSgNYwbnbLIf |
||||
FllbWYqAz0kCgYEA3hfooMzO3h+nMqbElH6zHM84s9XBBCD1ZZ7zJSgvf/7o06YxLtJ7/i0DKK4W |
||||
JKX5PvmQOY26lFMQtyUvmfo4uvFYLzlei5NY3e7qBOMKdb8wZoo9dSVN3y3WfMcCiENh4Kkl24T5 |
||||
5lSku8cmnAey2lci+IoBLG4SF5qDozg7o0MCgYEAtQabSCjwaA6VgghtXcJIpOPf109TrI5MV92L |
||||
imEKprLZeonSpnlA5KYgNRjgHbSkaUmoTKaedXWxpUaw05kTCR0bji45uu+wcwAn6l1d2kIQ1Z6K |
||||
G+CD7RuRnK55m8w+PS4ReiIpChO1VhQSraZ7YtRCkMrgGT5vx3Q4oMiCKGECgYEAkKRNkvwP9kXz |
||||
JF/MQ1HI4QhsS2L5E/FLIOXGQPrNBLVFeSIRggb/TeiO2B2YukGF9GegcHtHenYmusBzIfr+m3G3 |
||||
FvpsAsbx54hDzO74zvq3UPDTWcnzz1gRCq6pjYkk46YFy9Ps1P9nUgw/rvqsltqNIgTvArqk+c5d |
||||
0R08afUCgYEAnUUNC1FozLVTh2YV5MUuqymXRCmlvcYcWwxY6wjBPJBLBn7hxuoKIZAT7fQbKqwu |
||||
ErMUn1wxjCl0Tr+q/BYlujd/WFKqWQg/7jcs6tgLqJwyc1W1N9P2yUb8NdntuadhTEI0EufqAm1O |
||||
J8o+Gpm7JlhDmgF6rGKXaFojuDFzuRA= |
||||
-----END PRIVATE KEY----- |
Loading…
Reference in new issue