add method

This commit is contained in:
Looly 2019-10-14 17:28:57 +08:00
parent d41b917006
commit ab4f58bb31
14 changed files with 472 additions and 292 deletions

View File

@ -9,6 +9,8 @@
* 【all】 升级JDK最低 支持到8
* 【log】 Log接口添加get的static方法
* 【all】 部分接口添加FunctionalInterface修饰
* 【crypto】 KeyUtil增加readKeyStore重载
* 【extra】 JschUtil增加私钥传入支持issue#INKDR@Gitee
### Bug修复
* 【http】 修复Cookie中host失效导致的问题issue#583@Github

View File

@ -1549,6 +1549,12 @@ public class FileUtil {
String pathToUse = StrUtil.removePrefixIgnoreCase(path, URLUtil.CLASSPATH_URL_PREFIX);
// 去除file:前缀
pathToUse = StrUtil.removePrefixIgnoreCase(pathToUse, URLUtil.FILE_URL_PREFIX);
// 识别home目录形式并转换为绝对路径
if(pathToUse.startsWith("~")){
pathToUse = pathToUse.replace("~", getUserHomePath());
}
// 统一使用斜杠
pathToUse = pathToUse.replaceAll("[/\\\\]+", StrUtil.SLASH).trim();
//兼容Windows下的共享目录路径原始路径如果以\\开头则保留这种路径

View File

@ -1,5 +1,6 @@
package cn.hutool.crypto;
import java.io.File;
import java.io.InputStream;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
@ -32,6 +33,7 @@ import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.CharUtil;
@ -619,6 +621,20 @@ public class KeyUtil {
return algorithm;
}
/**
* 读取密钥库(Java Key StoreJKS) KeyStore文件<br>
* KeyStore文件用于数字证书的密钥对保存<br>
* see: http://snowolf.iteye.com/blog/391931
*
* @param keyFile 证书文件
* @param password 密码
* @return {@link KeyStore}
* @since 5.0.0
*/
public static KeyStore readJKSKeyStore(File keyFile, char[] password) {
return readKeyStore(KEY_TYPE_JKS, keyFile, password);
}
/**
* 读取密钥库(Java Key StoreJKS) KeyStore文件<br>
* KeyStore文件用于数字证书的密钥对保存<br>
@ -632,6 +648,53 @@ public class KeyUtil {
return readKeyStore(KEY_TYPE_JKS, in, password);
}
/**
* 读取PKCS12 KeyStore文件<br>
* KeyStore文件用于数字证书的密钥对保存
*
* @param keyFile 证书文件
* @param password 密码
* @return {@link KeyStore}
* @since 5.0.0
*/
public static KeyStore readPKCS12KeyStore(File keyFile, char[] password) {
return readKeyStore(KEY_TYPE_PKCS12, keyFile, password);
}
/**
* 读取PKCS12 KeyStore文件<br>
* KeyStore文件用于数字证书的密钥对保存
*
* @param in {@link InputStream} 如果想从文件读取.keystore文件使用 {@link FileUtil#getInputStream(java.io.File)} 读取
* @param password 密码
* @return {@link KeyStore}
* @since 5.0.0
*/
public static KeyStore readPKCS12KeyStore(InputStream in, char[] password) {
return readKeyStore(KEY_TYPE_PKCS12, in, password);
}
/**
* 读取KeyStore文件<br>
* KeyStore文件用于数字证书的密钥对保存<br>
* see: http://snowolf.iteye.com/blog/391931
*
* @param type 类型
* @param keyFile 证书文件
* @param password 密码null表示无密码
* @return {@link KeyStore}
* @since 5.0.0
*/
public static KeyStore readKeyStore(String type, File keyFile, char[] password) {
InputStream in = null;
try {
in = FileUtil.getInputStream(keyFile);
return readKeyStore(type, in, password);
} finally {
IoUtil.close(in);
}
}
/**
* 读取KeyStore文件<br>
* KeyStore文件用于数字证书的密钥对保存<br>

View File

@ -1,11 +1,11 @@
package cn.hutool.db.nosql.mongo;
import java.io.Closeable;
import java.util.ArrayList;
import java.util.List;
import org.bson.Document;
import cn.hutool.core.exceptions.NotInitedException;
import cn.hutool.core.net.NetUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.db.DbRuntimeException;
import cn.hutool.log.Log;
import cn.hutool.setting.Setting;
import com.mongodb.MongoClient;
import com.mongodb.MongoClientOptions;
import com.mongodb.MongoClientOptions.Builder;
@ -13,14 +13,11 @@ import com.mongodb.MongoCredential;
import com.mongodb.ServerAddress;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import org.bson.Document;
import cn.hutool.core.exceptions.NotInitedException;
import cn.hutool.core.net.NetUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.db.DbRuntimeException;
import cn.hutool.log.Log;
import cn.hutool.log.StaticLog;
import cn.hutool.setting.Setting;
import java.io.Closeable;
import java.util.ArrayList;
import java.util.List;
/**
* MongoDB工具类
@ -29,7 +26,7 @@ import cn.hutool.setting.Setting;
*
*/
public class MongoDS implements Closeable {
private final static Log log = StaticLog.get();
private final static Log log = Log.get();
/** 默认配置文件 */
public final static String MONGO_CONFIG_PATH = "config/mongo.setting";
@ -188,7 +185,7 @@ public class MongoDS implements Closeable {
setting = new Setting(MONGO_CONFIG_PATH, true);
}
final List<ServerAddress> addrList = new ArrayList<ServerAddress>();
final List<ServerAddress> addrList = new ArrayList<>();
for (String group : groups) {
addrList.add(createServerAddress(group));
}

View File

@ -14,7 +14,7 @@ import cn.hutool.setting.Setting;
*
*/
public class UploadSetting {
private static Log log = StaticLog.get();
private static final Log log = Log.get();
/** 默认的配置文件路径相对ClassPath */
public final static String DEFAULT_SETTING_PATH = "config/upload.setting";

View File

@ -1,42 +1,44 @@
package cn.hutool.extra.ssh;
import cn.hutool.core.util.StrUtil;
import com.jcraft.jsch.Session;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import com.jcraft.jsch.Session;
import cn.hutool.core.util.StrUtil;
/**
* Jsch会话池
*
* @author looly
*
* @author looly
*/
public enum JschSessionPool {
INSTANCE;
/** SSH会话池keyhostvalueSession对象 */
private Map<String, Session> sessionPool = new ConcurrentHashMap<String, Session>();
/** 锁 */
/**
* SSH会话池keyhostvalueSession对象
*/
private Map<String, Session> sessionPool = new ConcurrentHashMap<>();
/**
*
*/
private static final Object lock = new Object();
/**
* 获取Session不存在返回null
*
*
* @param key
* @return Session
*/
public Session get(String key) {
return sessionPool.get(key);
}
/**
* 获得一个SSH跳板机会话重用已经使用的会话
*
*
* @param sshHost 跳板机主机
* @param sshPort 跳板机端口
* @param sshUser 跳板机用户名
@ -58,10 +60,35 @@ public enum JschSessionPool {
return session;
}
/**
* 获得一个SSH跳板机会话重用已经使用的会话
*
* @param sshHost 跳板机主机
* @param sshPort 跳板机端口
* @param sshUser 跳板机用户名
* @param prvkey 跳板机私钥路径
* @param passphrase 跳板机私钥密码
* @return SSH会话
*/
public Session getSession(String sshHost, int sshPort, String sshUser, String prvkey, byte[] passphrase) {
final String key = StrUtil.format("{}@{}:{}", sshUser, sshHost, sshPort);
Session session = get(key);
if (null == session || false == session.isConnected()) {
synchronized (lock) {
session = get(key);
if (null == session || false == session.isConnected()) {
session = JschUtil.openSession(sshHost, sshPort, sshUser, prvkey, passphrase);
put(key, session);
}
}
}
return session;
}
/**
* 加入Session
*
* @param key
*
* @param key
* @param session Session
*/
public void put(String key, Session session) {
@ -70,7 +97,7 @@ public enum JschSessionPool {
/**
* 关闭SSH连接会话
*
*
* @param key 主机格式为user@host:port
*/
public void close(String key) {
@ -80,20 +107,20 @@ public enum JschSessionPool {
}
sessionPool.remove(key);
}
/**
* 移除指定Session
*
*
* @param session Session会话
* @since 4.1.15
*/
public void remove(Session session) {
if(null != session) {
if (null != session) {
final Iterator<Entry<String, Session>> iterator = this.sessionPool.entrySet().iterator();
Entry<String, Session> entry;
while(iterator.hasNext()) {
while (iterator.hasNext()) {
entry = iterator.next();
if(session.equals(entry.getValue())) {
if (session.equals(entry.getValue())) {
iterator.remove();
break;
}

View File

@ -1,43 +1,41 @@
package cn.hutool.extra.ssh;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.net.LocalPortGenerater;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import com.jcraft.jsch.*;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.Charset;
import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.ChannelShell;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.net.LocalPortGenerater;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
/**
* Jsch工具类<br>
* Jsch是Java Secure Channel的缩写JSch是一个SSH2的纯Java实现<br>
* 它允许你连接到一个SSH服务器并且可以使用端口转发X11转发文件传输等<br>
*
*
* @author Looly
* @since 4.0.0
*/
public class JschUtil {
/** 不使用SSH的值 */
/**
* 不使用SSH的值
*/
public final static String SSH_NONE = "none";
/** 本地端口生成器 */
/**
* 本地端口生成器
*/
private static final LocalPortGenerater portGenerater = new LocalPortGenerater(10000);
/**
* 生成一个本地端口用于远程端口映射
*
*
* @return 未被使用的本地端口
*/
public static int generateLocalPort() {
@ -46,7 +44,7 @@ public class JschUtil {
/**
* 获得一个SSH会话重用已经使用的会话
*
*
* @param sshHost 主机
* @param sshPort 端口
* @param sshUser 用户名
@ -57,9 +55,23 @@ public class JschUtil {
return JschSessionPool.INSTANCE.getSession(sshHost, sshPort, sshUser, sshPass);
}
/**
* 获得一个SSH会话重用已经使用的会话
*
* @param sshHost 主机
* @param sshPort 端口
* @param sshUser 用户名
* @param privateKeyPath 私钥路径
* @param passphrase 私钥密码
* @return SSH会话
*/
public static Session getSession(String sshHost, int sshPort, String sshUser, String privateKeyPath, byte[] passphrase) {
return JschSessionPool.INSTANCE.getSession(sshHost, sshPort, sshUser, privateKeyPath, passphrase);
}
/**
* 打开一个新的SSH会话
*
*
* @param sshHost 主机
* @param sshPort 端口
* @param sshUser 用户名
@ -77,39 +89,103 @@ public class JschUtil {
}
/**
* 新建一个新的SSH会话
*
* @param sshHost 主机
* @param sshPort 端口
* @param sshUser 机用户名
* @param sshPass 密码
* 打开一个新的SSH会话
*
* @param sshHost 主机
* @param sshPort 端口
* @param sshUser 用户名
* @param privateKeyPath 私钥的路径
* @param passphrase 私钥文件的密码可以为null
* @return SSH会话
* @since 4.5.2
*/
public static Session createSession(String sshHost, int sshPort, String sshUser, String sshPass) {
if (StrUtil.isEmpty(sshHost) || sshPort < 0 || StrUtil.isEmpty(sshUser) || StrUtil.isEmpty(sshPass)) {
return null;
}
Session session;
public static Session openSession(String sshHost, int sshPort, String sshUser, String privateKeyPath, byte[] passphrase) {
final Session session = createSession(sshHost, sshPort, sshUser, privateKeyPath, passphrase);
try {
session = new JSch().getSession(sshUser, sshHost, sshPort);
session.setPassword(sshPass);
// 设置第一次登陆的时候提示可选值(ask | yes | no)
session.setConfig("StrictHostKeyChecking", "no");
session.connect();
} catch (JSchException e) {
throw new JschRuntimeException(e);
}
return session;
}
/**
* 新建一个新的SSH会话此方法并不打开会话既不调用connect方法
*
* @param sshHost 主机
* @param sshPort 端口
* @param sshUser 用户名如果为null默认root
* @param sshPass 密码
* @return SSH会话
* @since 4.5.2
*/
public static Session createSession(String sshHost, int sshPort, String sshUser, String sshPass) {
Assert.notEmpty(sshHost, "SSH Host must be not empty!");
Assert.isTrue(sshPort < 0, "SSH Host must be not empty!");
// 默认root用户
if (StrUtil.isEmpty(sshUser)) {
sshUser = "root";
}
final JSch jsch = new JSch();
Session session;
try {
session = jsch.getSession(sshUser, sshHost, sshPort);
} catch (JSchException e) {
throw new JschRuntimeException(e);
}
if (StrUtil.isNotEmpty(sshPass)) {
session.setPassword(sshPass);
}
// 设置第一次登陆的时候提示可选值(ask | yes | no)
session.setConfig("StrictHostKeyChecking", "no");
return session;
}
/**
* 新建一个新的SSH会话此方法并不打开会话既不调用connect方法
*
* @param sshHost 主机
* @param sshPort 端口
* @param sshUser 用户名如果为null默认root
* @param privateKeyPath 私钥的路径
* @param passphrase 私钥文件的密码可以为null
* @return SSH会话
* @since 5.0.0
*/
public static Session createSession(String sshHost, int sshPort, String sshUser, String privateKeyPath, byte[] passphrase) {
Assert.notEmpty(sshHost, "SSH Host must be not empty!");
Assert.isTrue(sshPort < 0, "SSH Host must be not empty!");
Assert.notEmpty(privateKeyPath, "PrivateKey Path must be not empty!");
// 默认root用户
if (StrUtil.isEmpty(sshUser)) {
sshUser = "root";
}
final JSch jsch = new JSch();
Session session;
try {
jsch.addIdentity(privateKeyPath, passphrase);
session = jsch.getSession(sshUser, sshHost, sshPort);
} catch (JSchException e) {
throw new JschRuntimeException(e);
}
// 设置第一次登录的时候提示可选值(ask | yes | no)
session.setConfig("StrictHostKeyChecking", "no");
return session;
}
/**
* 绑定端口到本地 一个会话可绑定多个端口
*
* @param session 需要绑定端口的SSH会话
*
* @param session 需要绑定端口的SSH会话
* @param remoteHost 远程主机
* @param remotePort 远程端口
* @param localPort 本地端口
* @param localPort 本地端口
* @return 成功与否
* @throws JschRuntimeException 端口绑定失败异常
*/
@ -127,8 +203,8 @@ public class JschUtil {
/**
* 解除端口映射
*
* @param session 需要解除端口映射的SSH会话
*
* @param session 需要解除端口映射的SSH会话
* @param localPort 需要解除的本地端口
* @return 解除成功与否
*/
@ -143,8 +219,8 @@ public class JschUtil {
/**
* 打开SSH会话并绑定远程端口到本地的一个随机端口
*
* @param sshConn SSH连接信息对象
*
* @param sshConn SSH连接信息对象
* @param remoteHost 远程主机
* @param remotePort 远程端口
* @return 映射后的本地端口
@ -162,7 +238,7 @@ public class JschUtil {
/**
* 打开SFTP连接
*
*
* @param session Session会话
* @return {@link ChannelSftp}
* @since 4.0.3
@ -173,7 +249,7 @@ public class JschUtil {
/**
* 创建Sftp
*
*
* @param sshHost 远程主机
* @param sshPort 远程主机端口
* @param sshUser 远程主机用户名
@ -187,7 +263,7 @@ public class JschUtil {
/**
* 创建Sftp
*
*
* @param session SSH会话
* @return {@link Sftp}
* @since 4.0.5
@ -198,7 +274,7 @@ public class JschUtil {
/**
* 打开Shell连接
*
*
* @param session Session会话
* @return {@link ChannelShell}
* @since 4.0.3
@ -209,8 +285,8 @@ public class JschUtil {
/**
* 打开Channel连接
*
* @param session Session会话
*
* @param session Session会话
* @param channelType 通道类型可以是shell或sftp等{@link ChannelType}
* @return {@link Channel}
* @since 4.5.2
@ -224,11 +300,11 @@ public class JschUtil {
}
return channel;
}
/**
* 创建Channel连接
*
* @param session Session会话
*
* @param session Session会话
* @param channelType 通道类型可以是shell或sftp等{@link ChannelType}
* @return {@link Channel}
* @since 4.5.2
@ -248,9 +324,9 @@ public class JschUtil {
/**
* 执行Shell命令
*
*
* @param session Session会话
* @param cmd 命令
* @param cmd 命令
* @param charset 发送和读取内容的编码
* @return {@link ChannelExec}
* @since 4.0.3
@ -261,10 +337,10 @@ public class JschUtil {
/**
* 执行Shell命令
*
* @param session Session会话
* @param cmd 命令
* @param charset 发送和读取内容的编码
*
* @param session Session会话
* @param cmd 命令
* @param charset 发送和读取内容的编码
* @param errStream 错误信息输出到的位置
* @return {@link ChannelExec}
* @since 4.3.1
@ -294,7 +370,7 @@ public class JschUtil {
/**
* 关闭SSH连接会话
*
*
* @param session SSH会话
*/
public static void close(Session session) {
@ -306,7 +382,7 @@ public class JschUtil {
/**
* 关闭会话通道
*
*
* @param channel 会话通道
* @since 4.0.3
*/
@ -318,7 +394,7 @@ public class JschUtil {
/**
* 关闭SSH连接会话
*
*
* @param key 主机格式为user@host:port
*/
public static void close(String key) {

View File

@ -21,7 +21,7 @@ public enum GlobalHeaders {
INSTANCE;
/** 存储头信息 */
protected Map<String, List<String>> headers = new HashMap<String, List<String>>();
protected Map<String, List<String>> headers = new HashMap<>();
/**
* 构造

View File

@ -11,35 +11,30 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
import cn.hutool.core.lang.Console;
import cn.hutool.core.util.CharUtil;
/**
* HTML过滤器用于去除XSS(Cross Site Scripting) 漏洞隐患
*
* HTML filtering utility for protecting against XSS (Cross Site Scripting).
* <p>
* 此类中的方法非线程安全
* </p>
*
* This code is licensed LGPLv3
* <pre>
* String clean = new HTMLFilter().filter(input);
* </pre>
* <p>
* 此类来自http://xss-html-filter.sf.net
*
* This code is a Java port of the original work in PHP by Cal Hendersen. http://code.iamcal.com/php/lib_filter/
*
* The trickiest part of the translation was handling the differences in regex handling between PHP and Java. These resources were helpful in the process:
*
* http://java.sun.com/j2se/1.4.2/docs/api/java/util/regex/Pattern.html http://us2.php.net/manual/en/reference.pcre.pattern.modifiers.php http://www.regular-expressions.info/modifiers.html
*
* A note on naming conventions: instance variables are prefixed with a "v"; global constants are in all caps.
*
* Sample use: String input = ... String clean = new HTMLFilter().filter( input );
*
* The class is not thread safe. Create a new instance if in doubt.
*
* If you find bugs or have suggestions on improvement (especially regarding performance), please contact us. The latest version of this source, and our contact details, can be found at
* http://xss-html-filter.sf.net
*
* @author Joseph O'Connell
* @author Cal Hendersen
* @author Michael Semb Wever
*/
public final class HTMLFilter {
/** regex flag union representing /si modifiers in php **/
/**
* regex flag union representing /si modifiers in php
**/
private static final int REGEX_FLAGS_SI = Pattern.CASE_INSENSITIVE | Pattern.DOTALL;
private static final Pattern P_COMMENTS = Pattern.compile("<!--(.*?)-->", Pattern.DOTALL);
private static final Pattern P_COMMENT = Pattern.compile("^!--(.*)--$", REGEX_FLAGS_SI);
@ -66,29 +61,49 @@ public final class HTMLFilter {
private static final Pattern P_BOTH_ARROWS = Pattern.compile("<>");
// @xxx could grow large... maybe use sesat's ReferenceMap
private static final ConcurrentMap<String, Pattern> P_REMOVE_PAIR_BLANKS = new ConcurrentHashMap<String, Pattern>();
private static final ConcurrentMap<String, Pattern> P_REMOVE_SELF_BLANKS = new ConcurrentHashMap<String, Pattern>();
private static final ConcurrentMap<String, Pattern> P_REMOVE_PAIR_BLANKS = new ConcurrentHashMap<>();
private static final ConcurrentMap<String, Pattern> P_REMOVE_SELF_BLANKS = new ConcurrentHashMap<>();
/** set of allowed html elements, along with allowed attributes for each element **/
/**
* set of allowed html elements, along with allowed attributes for each element
**/
private final Map<String, List<String>> vAllowed;
/** counts of open tags for each (allowable) html element **/
private final Map<String, Integer> vTagCounts = new HashMap<String, Integer>();
/**
* counts of open tags for each (allowable) html element
**/
private final Map<String, Integer> vTagCounts = new HashMap<>();
/** html elements which must always be self-closing (e.g. "<img />") **/
/**
* html elements which must always be self-closing (e.g. "<img />")
**/
private final String[] vSelfClosingTags;
/** html elements which must always have separate opening and closing tags (e.g. "<b></b>") **/
/**
* html elements which must always have separate opening and closing tags (e.g. "<b></b>")
**/
private final String[] vNeedClosingTags;
/** set of disallowed html elements **/
/**
* set of disallowed html elements
**/
private final String[] vDisallowed;
/** attributes which should be checked for valid protocols **/
/**
* attributes which should be checked for valid protocols
**/
private final String[] vProtocolAtts;
/** allowed protocols **/
/**
* allowed protocols
**/
private final String[] vAllowedProtocols;
/** tags which should be removed if they contain no content (e.g. "<b></b>" or "<b />") **/
/**
* tags which should be removed if they contain no content (e.g. "<b></b>" or "<b />")
**/
private final String[] vRemoveBlanks;
/** entities allowed within html markup **/
/**
* entities allowed within html markup
**/
private final String[] vAllowedEntities;
/** flag determining whether comments are allowed in input String. */
/**
* flag determining whether comments are allowed in input String.
*/
private final boolean stripComment;
private final boolean encodeQuotes;
private boolean vDebug = false;
@ -100,36 +115,35 @@ public final class HTMLFilter {
/**
* Default constructor.
*
*/
public HTMLFilter() {
vAllowed = new HashMap<String, List<String>>();
vAllowed = new HashMap<>();
final ArrayList<String> a_atts = new ArrayList<String>();
final ArrayList<String> a_atts = new ArrayList<>();
a_atts.add("href");
a_atts.add("target");
vAllowed.put("a", a_atts);
final ArrayList<String> img_atts = new ArrayList<String>();
final ArrayList<String> img_atts = new ArrayList<>();
img_atts.add("src");
img_atts.add("width");
img_atts.add("height");
img_atts.add("alt");
vAllowed.put("img", img_atts);
final ArrayList<String> no_atts = new ArrayList<String>();
final ArrayList<String> no_atts = new ArrayList<>();
vAllowed.put("b", no_atts);
vAllowed.put("strong", no_atts);
vAllowed.put("i", no_atts);
vAllowed.put("em", no_atts);
vSelfClosingTags = new String[] { "img" };
vNeedClosingTags = new String[] { "a", "b", "strong", "i", "em" };
vDisallowed = new String[] {};
vAllowedProtocols = new String[] { "http", "mailto", "https" }; // no ftp.
vProtocolAtts = new String[] { "src", "href" };
vRemoveBlanks = new String[] { "a", "b", "strong", "i", "em" };
vAllowedEntities = new String[] { "amp", "gt", "lt", "quot" };
vSelfClosingTags = new String[]{"img"};
vNeedClosingTags = new String[]{"a", "b", "strong", "i", "em"};
vDisallowed = new String[]{};
vAllowedProtocols = new String[]{"http", "mailto", "https"}; // no ftp.
vProtocolAtts = new String[]{"src", "href"};
vRemoveBlanks = new String[]{"a", "b", "strong", "i", "em"};
vAllowedEntities = new String[]{"amp", "gt", "lt", "quot"};
stripComment = true;
encodeQuotes = true;
alwaysMakeTags = true;
@ -202,6 +216,7 @@ public final class HTMLFilter {
}
// ---------------------------------------------------------------
/**
* given a user submitted input String, filter out any invalid or restricted html.
*
@ -292,15 +307,15 @@ public final class HTMLFilter {
}
m.appendTail(buf);
s = buf.toString();
// these get tallied in processTag
// (remember to reset before subsequent calls to filter method)
final StringBuilder sBuilder = new StringBuilder(buf.toString());
for (String key : vTagCounts.keySet()) {
for (int ii = 0; ii < vTagCounts.get(key); ii++) {
s += "</" + key + ">";
sBuilder.append("</").append(key).append(">");
}
}
s = sBuilder.toString();
return s;
}
@ -350,12 +365,12 @@ public final class HTMLFilter {
// debug( "in a starting tag, name='" + name + "'; body='" + body + "'; ending='" + ending + "'" );
if (allowed(name)) {
String params = "";
final StringBuilder params = new StringBuilder();
final Matcher m2 = P_QUOTED_ATTRIBUTES.matcher(body);
final Matcher m3 = P_UNQUOTED_ATTRIBUTES.matcher(body);
final List<String> paramNames = new ArrayList<String>();
final List<String> paramValues = new ArrayList<String>();
final List<String> paramNames = new ArrayList<>();
final List<String> paramValues = new ArrayList<>();
while (m2.find()) {
paramNames.add(m2.group(1)); // ([a-z0-9]+)
paramValues.add(m2.group(3)); // (.*?)
@ -378,7 +393,7 @@ public final class HTMLFilter {
if (inArray(paramName, vProtocolAtts)) {
paramValue = processParamProtocol(paramValue);
}
params += " " + paramName + "=\"" + paramValue + "\"";
params.append(CharUtil.SPACE).append(paramName).append("=\"").append(paramValue).append("\"");
}
}
@ -421,9 +436,9 @@ public final class HTMLFilter {
final String protocol = m.group(1);
if (!inArray(protocol, vAllowedProtocols)) {
// bad protocol, turn into local anchor link instead
s = "#" + s.substring(protocol.length() + 1, s.length());
s = "#" + s.substring(protocol.length() + 1);
if (s.startsWith("#//")) {
s = "#" + s.substring(3, s.length());
s = "#" + s.substring(3);
}
}
}

View File

@ -429,9 +429,8 @@ public class HttpConnection {
* 当返回错误代码时获得错误内容流
*
* @return 错误内容
* @throws IOException IO异常
*/
public InputStream getErrorStream() throws IOException {
public InputStream getErrorStream() {
if (null != this.conn) {
return this.conn.getErrorStream();
}
@ -514,8 +513,6 @@ public class HttpConnection {
* 初始化http或https请求参数<br>
* 有些时候htts请求会出现com.sun.net.ssl.internal.www.protocol.https.HttpsURLConnectionOldImpl的实现此为sun内部api按照普通http请求处理
*
* @param hostnameVerifier 域名验证器非https传入null
* @param ssf SSLSocketFactory非https传入null
* @return {@link HttpURLConnection}https返回{@link HttpsURLConnection}
*/
private HttpURLConnection openHttp() throws IOException {
@ -532,7 +529,7 @@ public class HttpConnection {
* 建立连接
*
* @return {@link URLConnection}
* @throws IOException
* @throws IOException IO异常
*/
private URLConnection openConnection() throws IOException {
return (null == this.proxy) ? url.openConnection() : url.openConnection(this.proxy);

View File

@ -1,5 +1,6 @@
package cn.hutool.http;
import java.io.Serializable;
import java.net.CookieManager;
import cn.hutool.http.cookie.GlobalCookieManager;
@ -10,10 +11,11 @@ import cn.hutool.http.cookie.GlobalCookieManager;
* @author Looly
* @since 4.6.2
*/
public class HttpGlobalConfig {
public class HttpGlobalConfig implements Serializable {
private static final long serialVersionUID = 1L;
protected static int timeout = -1;
/**
* 获取全局默认的超时时长
*

View File

@ -18,7 +18,7 @@ import cn.hutool.core.util.StrUtil;
public class HttpInputStream extends InputStream {
/** 原始流 */
private volatile InputStream in;
private InputStream in;
/**
* 构造

View File

@ -1,21 +1,5 @@
package cn.hutool.http;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Pattern;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.convert.Convert;
@ -25,27 +9,36 @@ import cn.hutool.core.io.IoUtil;
import cn.hutool.core.io.StreamProgress;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.text.StrBuilder;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import cn.hutool.core.util.*;
import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.nio.charset.Charset;
import java.util.*;
import java.util.Map.Entry;
import java.util.regex.Pattern;
/**
* Http请求工具类
*
*
* @author xiaoleilu
*/
public class HttpUtil {
/** 正则Content-Type中的编码信息 */
/**
* 正则Content-Type中的编码信息
*/
public static final Pattern CHARSET_PATTERN = Pattern.compile("charset\\s*=\\s*([a-z0-9-]*)", Pattern.CASE_INSENSITIVE);
/** 正则匹配meta标签的编码信息 */
/**
* 正则匹配meta标签的编码信息
*/
public static final Pattern META_CHARSET_PATTERN = Pattern.compile("<meta[^>]*?charset\\s*=\\s*['\"]?([a-z0-9-]*)", Pattern.CASE_INSENSITIVE);
/**
* 检测是否https
*
*
* @param url URL
* @return 是否https
*/
@ -55,9 +48,9 @@ public class HttpUtil {
/**
* 创建Http请求对象
*
*
* @param method 方法枚举{@link Method}
* @param url 请求的URL可以使HTTP或者HTTPS
* @param url 请求的URL可以使HTTP或者HTTPS
* @return {@link HttpRequest}
* @since 3.0.9
*/
@ -67,7 +60,7 @@ public class HttpUtil {
/**
* 创建Http GET请求对象
*
*
* @param url 请求的URL可以使HTTP或者HTTPS
* @return {@link HttpRequest}
* @since 3.2.0
@ -78,7 +71,7 @@ public class HttpUtil {
/**
* 创建Http POST请求对象
*
*
* @param url 请求的URL可以使HTTP或者HTTPS
* @return {@link HttpRequest}
* @since 3.2.0
@ -89,8 +82,8 @@ public class HttpUtil {
/**
* 发送get请求
*
* @param urlString 网址
*
* @param urlString 网址
* @param customCharset 自定义请求字符集如果字符集获取不到使用此字符集
* @return 返回内容如果只检查状态码正常只返回 ""不正常返回 null
*/
@ -100,7 +93,7 @@ public class HttpUtil {
/**
* 发送get请求
*
*
* @param urlString 网址
* @return 返回内容如果只检查状态码正常只返回 ""不正常返回 null
*/
@ -110,9 +103,9 @@ public class HttpUtil {
/**
* 发送get请求
*
*
* @param urlString 网址
* @param timeout 超时时长-1表示默认超时单位毫秒
* @param timeout 超时时长-1表示默认超时单位毫秒
* @return 返回内容如果只检查状态码正常只返回 ""不正常返回 null
* @since 3.2.0
*/
@ -122,9 +115,9 @@ public class HttpUtil {
/**
* 发送get请求
*
*
* @param urlString 网址
* @param paramMap post表单数据
* @param paramMap post表单数据
* @return 返回数据
*/
public static String get(String urlString, Map<String, Object> paramMap) {
@ -133,10 +126,10 @@ public class HttpUtil {
/**
* 发送get请求
*
*
* @param urlString 网址
* @param paramMap post表单数据
* @param timeout 超时时长-1表示默认超时单位毫秒
* @param paramMap post表单数据
* @param timeout 超时时长-1表示默认超时单位毫秒
* @return 返回数据
* @since 3.3.0
*/
@ -146,9 +139,9 @@ public class HttpUtil {
/**
* 发送post请求
*
*
* @param urlString 网址
* @param paramMap post表单数据
* @param paramMap post表单数据
* @return 返回数据
*/
public static String post(String urlString, Map<String, Object> paramMap) {
@ -157,10 +150,10 @@ public class HttpUtil {
/**
* 发送post请求
*
*
* @param urlString 网址
* @param paramMap post表单数据
* @param timeout 超时时长-1表示默认超时单位毫秒
* @param paramMap post表单数据
* @param timeout 超时时长-1表示默认超时单位毫秒
* @return 返回数据
* @since 3.2.0
*/
@ -171,14 +164,14 @@ public class HttpUtil {
/**
* 发送post请求<br>
* 请求体body参数支持两种类型
*
*
* <pre>
* 1. 标准参数例如 a=1&amp;b=2 这种格式
* 2. Rest模式此时body需要传入一个JSON或者XML字符串Hutool会自动绑定其对应的Content-Type
* </pre>
*
*
* @param urlString 网址
* @param body post表单数据
* @param body post表单数据
* @return 返回数据
*/
public static String post(String urlString, String body) {
@ -188,15 +181,15 @@ public class HttpUtil {
/**
* 发送post请求<br>
* 请求体body参数支持两种类型
*
*
* <pre>
* 1. 标准参数例如 a=1&amp;b=2 这种格式
* 2. Rest模式此时body需要传入一个JSON或者XML字符串Hutool会自动绑定其对应的Content-Type
* </pre>
*
*
* @param urlString 网址
* @param body post表单数据
* @param timeout 超时时长-1表示默认超时单位毫秒
* @param body post表单数据
* @param timeout 超时时长-1表示默认超时单位毫秒
* @return 返回数据
* @since 3.2.0
*/
@ -205,10 +198,11 @@ public class HttpUtil {
}
// ---------------------------------------------------------------------------------------- download
/**
* 下载远程文本
*
* @param url 请求的url
*
* @param url 请求的url
* @param customCharsetName 自定义的字符集
* @return 文本
*/
@ -218,8 +212,8 @@ public class HttpUtil {
/**
* 下载远程文本
*
* @param url 请求的url
*
* @param url 请求的url
* @param customCharset 自定义的字符集可以使用{@link CharsetUtil#charset} 方法转换
* @return 文本
*/
@ -229,10 +223,10 @@ public class HttpUtil {
/**
* 下载远程文本
*
* @param url 请求的url
*
* @param url 请求的url
* @param customCharset 自定义的字符集可以使用{@link CharsetUtil#charset} 方法转换
* @param streamPress 进度条 {@link StreamProgress}
* @param streamPress 进度条 {@link StreamProgress}
* @return 文本
*/
public static String downloadString(String url, Charset customCharset, StreamProgress streamPress) {
@ -247,8 +241,8 @@ public class HttpUtil {
/**
* 下载远程文件
*
* @param url 请求的url
*
* @param url 请求的url
* @param dest 目标文件或目录当为目录时取URL中的文件名取不到使用编码后的URL做为文件名
* @return 文件大小
*/
@ -258,8 +252,8 @@ public class HttpUtil {
/**
* 下载远程文件
*
* @param url 请求的url
*
* @param url 请求的url
* @param destFile 目标文件或目录当为目录时取URL中的文件名取不到使用编码后的URL做为文件名
* @return 文件大小
*/
@ -269,10 +263,10 @@ public class HttpUtil {
/**
* 下载远程文件
*
* @param url 请求的url
*
* @param url 请求的url
* @param destFile 目标文件或目录当为目录时取URL中的文件名取不到使用编码后的URL做为文件名
* @param timeout 超时单位毫秒-1表示默认超时
* @param timeout 超时单位毫秒-1表示默认超时
* @return 文件大小
* @since 4.0.4
*/
@ -282,9 +276,9 @@ public class HttpUtil {
/**
* 下载远程文件
*
* @param url 请求的url
* @param destFile 目标文件或目录当为目录时取URL中的文件名取不到使用编码后的URL做为文件名
*
* @param url 请求的url
* @param destFile 目标文件或目录当为目录时取URL中的文件名取不到使用编码后的URL做为文件名
* @param streamProgress 进度条
* @return 文件大小
*/
@ -294,10 +288,10 @@ public class HttpUtil {
/**
* 下载远程文件
*
* @param url 请求的url
* @param destFile 目标文件或目录当为目录时取URL中的文件名取不到使用编码后的URL做为文件名
* @param timeout 超时单位毫秒-1表示默认超时
*
* @param url 请求的url
* @param destFile 目标文件或目录当为目录时取URL中的文件名取不到使用编码后的URL做为文件名
* @param timeout 超时单位毫秒-1表示默认超时
* @param streamProgress 进度条
* @return 文件大小
* @since 4.0.4
@ -318,9 +312,9 @@ public class HttpUtil {
/**
* 下载远程文件
*
* @param url 请求的url
* @param out 将下载内容写到输出流中 {@link OutputStream}
*
* @param url 请求的url
* @param out 将下载内容写到输出流中 {@link OutputStream}
* @param isCloseOut 是否关闭输出流
* @return 文件大小
*/
@ -330,10 +324,10 @@ public class HttpUtil {
/**
* 下载远程文件
*
* @param url 请求的url
* @param out 将下载内容写到输出流中 {@link OutputStream}
* @param isCloseOut 是否关闭输出流
*
* @param url 请求的url
* @param out 将下载内容写到输出流中 {@link OutputStream}
* @param isCloseOut 是否关闭输出流
* @param streamProgress 进度条
* @return 文件大小
*/
@ -354,7 +348,7 @@ public class HttpUtil {
/**
* 将Map形式的Form表单数据转换为Url参数形式不做编码
*
*
* @param paramMap 表单数据
* @return url参数
*/
@ -365,8 +359,8 @@ public class HttpUtil {
/**
* 将Map形式的Form表单数据转换为Url参数形式<br>
* 编码键和值对
*
* @param paramMap 表单数据
*
* @param paramMap 表单数据
* @param charsetName 编码
* @return url参数
*/
@ -378,13 +372,13 @@ public class HttpUtil {
* 将Map形式的Form表单数据转换为Url参数形式<br>
* paramMap中如果key为空null和""会被忽略如果value为null会被做为空白符""<br>
* 会自动url编码键和值
*
*
* <pre>
* key1=v1&amp;key2=&amp;key3=v3
* </pre>
*
*
* @param paramMap 表单数据
* @param charset 编码
* @param charset 编码
* @return url参数
*/
public static String toParams(Map<String, ?> paramMap, Charset charset) {
@ -423,16 +417,15 @@ public class HttpUtil {
}
return sb.toString();
}
/**
* 对URL参数做编码只编码键和值<br>
* 提供的值可以是url附带参数但是不能只是url
*
*
* <p>注意此方法只能标准化整个URL并不适合于单独编码参数值</p>
*
*
*
* @param paramsStr url参数可以包含url本身
* @param charset 编码
* @param charset 编码
* @return 编码后的url和参数
* @since 4.0.1
*/
@ -458,17 +451,17 @@ public class HttpUtil {
}
paramPart = normalizeParams(paramPart, charset);
return StrUtil.isBlank(urlPart) ? paramPart : urlPart + "?" + paramPart;
}
/**
* 标准化参数字符串即URL中后的部分
*
*
* <p>注意此方法只能标准化整个URL并不适合于单独编码参数值</p>
*
*
* @param paramPart 参数字符串
* @param charset 编码
* @param charset 编码
* @return 标准化的参数字符串
* @since 4.5.2
*/
@ -523,9 +516,9 @@ public class HttpUtil {
/**
* 将URL参数解析为Map也可以解析Post中的键值对参数
*
*
* @param paramsStr 参数字符串或者带参数的Path
* @param charset 字符集
* @param charset 字符集
* @return 参数Map
* @since 4.0.2
*/
@ -542,9 +535,9 @@ public class HttpUtil {
/**
* 将URL参数解析为Map也可以解析Post中的键值对参数
*
*
* @param paramsStr 参数字符串或者带参数的Path
* @param charset 字符集
* @param charset 字符集
* @return 参数Map
*/
public static Map<String, List<String>> decodeParams(String paramsStr, String charset) {
@ -558,7 +551,7 @@ public class HttpUtil {
paramsStr = StrUtil.subSuf(paramsStr, pathEndPos + 1);
}
final Map<String, List<String>> params = new LinkedHashMap<String, List<String>>();
final Map<String, List<String>> params = new LinkedHashMap<>();
final int len = paramsStr.length();
String name = null;
int pos = 0; // 未处理字符开始位置
@ -601,10 +594,10 @@ public class HttpUtil {
/**
* 将表单数据加到URL中用于GET表单提交<br>
* 表单的键值对会被url编码但是url中原参数不会被编码
*
* @param url URL
* @param form 表单数据
* @param charset 编码
*
* @param url URL
* @param form 表单数据
* @param charset 编码
* @param isEncodeParams 是否对键和值做转义处理
* @return 合成后的URL
*/
@ -620,11 +613,11 @@ public class HttpUtil {
/**
* 将表单数据字符串加到URL中用于GET表单提交
*
* @param url URL
*
* @param url URL
* @param queryString 表单数据字符串
* @param charset 编码
* @param isEncode 是否对键和值做转义处理
* @param charset 编码
* @param isEncode 是否对键和值做转义处理
* @return 拼接后的字符串
*/
public static String urlWithForm(String url, String queryString, Charset charset, boolean isEncode) {
@ -662,7 +655,7 @@ public class HttpUtil {
/**
* 从Http连接的头信息中获得字符集<br>
* 从ContentType中获取
*
*
* @param conn HTTP连接对象
* @return 字符集
*/
@ -676,14 +669,13 @@ public class HttpUtil {
/**
* 从流中读取内容<br>
* 首先尝试使用charset编码读取内容如果为空默认UTF-8如果isGetCharsetFromContent为true则通过正则在正文中获取编码信息转换为指定编码
*
* @param in 输入流
* @param charset 字符集
*
* @param in 输入流
* @param charset 字符集
* @param isGetCharsetFromContent 是否从返回内容中获得编码信息
* @return 内容
* @throws IOException IO异常
*/
public static String getString(InputStream in, Charset charset, boolean isGetCharsetFromContent) throws IOException {
public static String getString(InputStream in, Charset charset, boolean isGetCharsetFromContent) {
final byte[] contentBytes = IoUtil.readBytes(in);
return getString(contentBytes, charset, isGetCharsetFromContent);
}
@ -691,9 +683,9 @@ public class HttpUtil {
/**
* 从流中读取内容<br>
* 首先尝试使用charset编码读取内容如果为空默认UTF-8如果isGetCharsetFromContent为true则通过正则在正文中获取编码信息转换为指定编码
*
* @param contentBytes 内容byte数组
* @param charset 字符集
*
* @param contentBytes 内容byte数组
* @param charset 字符集
* @param isGetCharsetFromContent 是否从返回内容中获得编码信息
* @return 内容
*/
@ -727,11 +719,11 @@ public class HttpUtil {
}
return content;
}
/**
* 根据文件扩展名获得MimeType
*
* @param filePath 文件路径或文件名
*
* @param filePath 文件路径或文件名
* @param defaultValue 当获取MimeType为null时的默认值
* @return MimeType
* @see FileUtil#getMimeType(String)
@ -743,7 +735,7 @@ public class HttpUtil {
/**
* 根据文件扩展名获得MimeType
*
*
* @param filePath 文件路径或文件名
* @return MimeType
* @see FileUtil#getMimeType(String)
@ -754,16 +746,16 @@ public class HttpUtil {
/**
* 从请求参数的body中判断请求的Content-Type类型支持的类型有
*
*
* <pre>
* 1. application/json
* 1. application/xml
* </pre>
*
*
* @param body 请求参数体
* @return Content-Type类型如果无法判断返回null
* @since 3.2.0
* @see ContentType#get(String)
* @since 3.2.0
*/
public static String getContentTypeByRequestBody(String body) {
final ContentType contentType = ContentType.get(body);
@ -773,20 +765,17 @@ public class HttpUtil {
/**
* 将键值对加入到值为List类型的Map中
*
* @param params 参数
* @param name key
* @param value value
*
* @param params 参数
* @param name key
* @param value value
* @param charset 编码
*/
private static void addParam(Map<String, List<String>> params, String name, String value, String charset) {
name = URLUtil.decode(name, charset);
value = URLUtil.decode(value, charset);
List<String> values = params.get(name);
if (values == null) {
values = new ArrayList<String>(1); // 一般是一个参数
params.put(name, values);
}
final List<String> values = params.computeIfAbsent(name, k -> new ArrayList<>(1));
// 一般是一个参数
values.add(value);
}

View File

@ -220,7 +220,9 @@ public final class StaticLog {
*
* @param clazz 日志发出的类
* @return Log
* @deprecated 请使用 {@link Log#get(Class)}
*/
@Deprecated
public static Log get(Class<?> clazz) {
return LogFactory.get(clazz);
}
@ -230,14 +232,18 @@ public final class StaticLog {
*
* @param name 自定义的日志发出者名称
* @return Log
* @deprecated 请使用 {@link Log#get(String)}
*/
@Deprecated
public static Log get(String name) {
return LogFactory.get(name);
}
/**
* @return 获得日志自动判定日志发出者
* @deprecated 请使用 {@link Log#get()}
*/
@Deprecated
public static Log get() {
return LogFactory.get(CallerUtil.getCallerCaller());
}