1. 配置客户端请求HTTP首部
HTTP客户端(如浏览器)向服务器发送一个请求行和一个首部,如下:
web服务器可以根据这个信息向不同的客户端提供不同的页面,获取和设置cookie,通过口令认证用户。通过在客户端发送和服务器响应的首部中放置不同的字段,就可以完成这些工作。在打开连接前可以使用setRequestProoerty()方法为HTTP首部增加首部字段。
public void setRequestProperty(String name,String value)
该方法用指定的名和值为这个URLConnection的首部增加一个字段。这个方法只能在连接打开之前调用。如果连接打开了,它将抛出一个IllegalStateException异常。getRequestProperty()
会返回这个URLConnection所用HTTP首部中指定字段的值,public Map<String,List<String>> getRequestProperties()
会将字段值用map的形式返回。如果我们要给URLConnection添加新的属性值就调用addRequestProperty(String name, String value)
方法。其实并没有一个固定的首部,服务器一般会忽略无法识别的首部,HTTP确实对首部字段的名和值的内容有一定的限制,例如,名不能包含空格,值不能包含换行符号等待,不然getRequestProperty()
和public Map<String,List<String>> getRequestProperties()
会抛出IllegalArgumentException
。
2. 向服务器写入数据
有时你需要向URLConnection写入数据,例如POST向Web服务器提交表单,获者使用PUT上传文件等。getOutputStream()
方法返回一个OutputStream,可以用来吸入数据传送给服务器。
注意由于URLConnection默认情况下不允许输出,所以要调用setDoOutput(true)来运行输出,一旦设置该方法,那么GET方法就会变成POST方法
一旦得到OutputStream就可以串链到BufferdOutputStream或BufferedWriter进行缓冲。
public class Main{
public static void main(String[] args) {
try{
URL u=new URL("http://www.baidu.com?");
URLConnection uc=u.openConnection();
uc.setDoOutput(true);
OutputStream raw=uc.getOutputStream();
OutputStream bufferd=new BufferedOutputStream(raw);
OutputStreamWriter out=new OutputStreamWriter(bufferd,"8859_1");
out.write("first=Julie&middle=&last=Harting&work=String+Quarter\r\n");
out.flush();
out.close();
//开始连接
uc.connect();
BufferedReader in=new BufferedReader(new InputStreamReader(uc.getInputStream(),"utf-8"));
String line=null;
StringBuffer rest=new StringBuffer();
while (null != (line=in.readLine())){
rest.append(line);
}
System.out.println(rest.toString());
}catch(IOException e){
System.err.print(e);
}
}
}
下面是百度的响应输出:
利用POST发送数据和GET几乎一样,区别是我们利用POST方法是通过URLConnection的getOutputStream()方法写入查询字符串,而不是附加到URL。java会缓冲写入输出流的所有数据,直到流关闭。所以只要你能控制客户端和服务器,就可以使用你喜欢的任何其他数据编码方式。
下面这段程序使用URLConnection类和QueryString类来提交POST表单数据,构造函数设置了URL。查询字符串是使用add方法来构建的。post()方法具体向服务器发送数据,它打开与指定URL的URLConnection,设置其doOutput字段为true,并将查询字符串写入输出流,然后返回包含服务器响应的输入流。
public class Main{
public static void main(String[] args) throws IOException {
URL url;
try{
url=new URL("http://www.cafeaulait.org/books/jnp4/postquery.phtml");
}catch(MalformedURLException ex){
System.err.println("Usage: java FormPoster url");
return;
}
FormPoster poster=new FormPoster(url);
poster.add("name","jakiechai");
poster.add("email","elharo@ibiblio.org");
try(InputStream in=poster.post()){
Reader r=new InputStreamReader(in);
int c;
while((c=r.read())!=-1){
System.out.print((char)c);
}
System.out.println();
}
}
}
class FormPoster{
private URL url;
static String val="";
public FormPoster(URL url){
if(!url.getProtocol().toLowerCase().startsWith("http"))
{
throw new IllegalAccessError("Posting only works for http URLs");
}
this.url=url;
}
public void add(String name,String value){
val+=name+":"+value;
}
public InputStream post() throws IOException{
URLConnection uc=url.openConnection();
uc.setDoOutput(true);
try(OutputStreamWriter out=new OutputStreamWriter(uc.getOutputStream(),"UTF-8")){
out.write(val);
out.write("\r\n");
out.flush();
}
return uc.getInputStream();
}
}
下面是服务器返回的内容:
Query Results
You submitted the following name/value pairs:
- name:jakiechaiemail:elharo@ibiblio_org =
The request included the following cookies:
Here's the HTTP request header:
USER_AGENT: Java/1.8.0_332 HOST: www.cafeaulait.org ACCEPT: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2 X_FORWARDED_PROTO: http X_FORWARDED_FOR: 106.15.224.186 CONNECTION: close
Here's the complete body of the request:
name:jakiechaiemail:elharo@ibiblio.org
Some links:
This Page Another PageLast Modified July 25, 2004
上面方法的核心是POST方法,它首先打开一个连接,指向存储在url字段中的URL。由于这个URLConnection需要发送输出。所以post()方法将这个连接的doOutput字段设置为true,然后将这个URL的OutputStream串联到一个发送数据的ASCII的OutputStreamWriter,接下来刷新输出并关闭流,流不关闭就不会发送任务数据。最后返回InputStream获得服务器响应。下面总结一下表单提交的步骤:
- 确定要发送给服务器的名-值对
- 编写接受和处理请求的服务端程序。如果没有使用定制的数据编码,可以使用普通的HTML表单和WEB浏览器测试这个程序
- 在Java程序中创建一个查询字符串。字符串的形式如下:
name=value1&name2=value2&jname3=value3
在增加到查询字符串之前,先将各个名和值传递到URLEncoder.encode() - 打开一个URLConnection,指向接受数据的程序URL
- 将查询字符串写入到URLConnection的OutputStream
- 关闭URLConnection的OutputStream
- 从URLConnection的Input Stream读取服务器的响应
getOutputStream方法还可以用于PUT请求方法,这是一种在WEB服务器上存储文件的方法。要存储的数据写入getOutputStream返回的OutputStream。不过,这只能在URLConnection的HttpURLConnection子类中进行。
3. URLConnection的安全考虑
建立网络连接,读/写文件等存在一些常见的安全限制,URLConnection对象会受到这些安全限制的约束。在尝试连接一个URL前,你可能想知道是否运行连接。为此URLConnection类包含了一个getPermission()方法
public Permission getPermission()throws IOException
它返回一个Java.security.Permission对象,指出连接这个URL所需的权限。如果不需要任何权限就会返回null。URLConnection的子类会返回java.security.Permission的不同子类。
Permission 类的主要目的是描述对于系统资源的访问权限。这些系统资源可以是文件、网络连接、操作系统功能等。通过使用权限,Java 应用程序可以定义和强制执行安全策略,以控制对敏感资源的访问。Permission 类是一个抽象类,它定义了权限的基本属性和行为,包括权限的名称、描述、是否允许操作等。具体的权限类(如 FilePermission、SocketPermission 等)是 Permission 类的子类,用于表示特定类型的权限。在 Java 安全模型中,权限由权限名和可选的操作列表组成。权限名是一个字符串,用于标识权限的类型和资源的标识符。操作列表表示对资源所允许的操作,如读取、写入、执行等。权限的粒度可以根据需要进行调整,以满足具体的安全需求。通过使用权限对象,可以进行权限检查,以确保应用程序在访问敏感资源之前具有适当的权限。这有助于提供安全性和防止恶意操作。下面是 Permission 类的一些常用方法:
- getName():获取权限的名称。
- getActions():获取权限的操作列表。
- implies(Permission p):检查当前权限是否包含指定的权限。
- equals(Object obj):比较两个权限对象是否相等。
- hashCode():获取权限对象的哈希码值。
- toString():返回权限对象的字符串表示。
总结来说,java.security.Permission 类提供了一种标准化的方式来表示和管理 Java 应用程序的访问权限。它是 Java 安全模型的核心组件之一,用于实现安全策略和保护敏感资源。
public static void main(String[] args) throws IOException {
URL u = new URL("\n" +
"https://hm.baidu.com/hm.gif?hca=36FB9E633015756B&cc=1&ck=1&cl=24-bit&ds=1920x1080&vl=1001&ep=7111%2C2543&et=10&ja=0&ln=zh-cn&lo=0<=1684721081&rnd=108138644&si=6bcd52f51e9b3dce32bec4a3997715ac&su=https%3A%2F%2Fwww.baidu.com%2Flink%3Furl%3DQUDdVYc4ExLePOefaclpkkQyDsywEmMmz4W37KJFhafJByp_lyapL-JzapNuyFy2rK_qZJfRbkQ6CuWmXotAc4dSqZuZENMBepfFjFjxwdq%26wd%3D%26eqid%3D95614ab00000085400000005646f571f&v=1.3.0&lv=3&sn=52924&r=0&ww=705&p=islogin*1*1!isonline*1*1!isvip*0*1!uid_*qq_43456605*1!view_h_*914&u=https%3A%2F%2Fblog.csdn.net%2Fqq_44231797%2Farticle%2Fdetails%2F121343892 ");
URLConnection ur=u.openConnection();
System.out.println(ur.getPermission());
}
java.net.SocketPermission 是 java.security.Permission 的一个具体子类,用于表示对网络套接字的权限。它允许应用程序控制对指定主机和端口的网络连接和解析操作。在给定的示例中,“hm.baidu.com:80” 表示要访问的目标主机和端口。它指定了主机名为 hm.baidu.com,端口号为 80,这是 HTTP 协议的默认端口。“connect,resolve” 是指对该主机和端口所允许的操作。具体来说:connect 表示允许建立网络连接。应用程序可以与指定的主机和端口进行网络通信。resolve 表示允许解析主机名。应用程序可以将主机名解析为 IP 地址。
4. HttpURLConnection
java.net.HttpURLConnection类就是URLConnection的抽象子类。它提供了另外一些方法,在处理http URL时尤其有帮助。具体地,它包含的方法可以获得和设置请求方法,确定是否重定向、获取响应码和消息,以及确定是否使用了代理服务器。它还包括了几十个便于记忆的常量,对于各种HTTP响应码。最后,它覆盖了URLConnection超类的getPermission方法。由于这个类是抽象类,唯一的构造函数是保护类型的,所以不能直接创建HttpURLConnection实例及。不过使用http URL构造一个URL对象并调用其Openconnection方法,返回的URLConnection就是HttpURLConnection的一个实例。可以将其强转为HttpURLConnection
URL u=new URL("http://lesswrong.com/
URLConnection uc=u.openConnection()
HttpURLConnection http=(HttpURLConnection) uc;
默认情况下HttpURLConnection会使用GET方法,不过,可以用setRequestMethod来改变请求方法
public void setRequestMethod(String method)throws ProtocolException
这个方法的参数应当是以下7个字符串之一(区分大小写)
- GET
- POST
- HEAD
- PUT
- DELETE
- OPTIONS(向web服务器询问一个指定URL支持的选项列表)
- TRACE
当然单纯的调用setRequestMethod设置请求参数效果是不够的,更多的还需要设置我们的请求头消息
tips:connect方法和openConnection方法的区别
url.openConnection() 和 url.connect() 是 Java 中用于建立与指定 URL 的连接的两种方式,它们有一些区别。
url.openConnection():
- url.openConnection() 是 URL 类的一个方法,它返回一个 URLConnection 对象,用于建立与指定 URL 的连接。
- URLConnection 是一个抽象类,具体的实现类根据 URL 的协议不同而有所不同,比如对于 HTTP 协议可以返回 HttpURLConnection 的实例。
- openConnection() 方法返回的 URLConnection 对象可以通过设置请求头、请求方法、读取响应等方式进行更多的控制和操>作。
- openConnection() 方法只是建立了与指定 URL 的连接,实际的网络通信在后续的操作中进行,比如读取输入流或写入输出流。
url.connect():
- url.connect() 是 URL 类的一个方法,它是一个简单的调用,用于显式地建立与指定 URL 的连接。
- 它实际上是 openConnection() 方法的一个隐式调用,即在调用 connect() 时会自动执行 openConnection()。
- connect() 方法在建立连接后立即返回,不会返回具体的连接对象,因此无法对连接进行更多的控制和操作。
- connect() 方法适用于简单的连接场景,不需要对连接进行进一步配置和控制的情况。
总结来说,url.openConnection() 返回一个具体的 URLConnection 对象,可以对连接进行更多的控制和操作,而 url.connect() 是 openConnection() 方法的简化调用,用于简单的连接场景,无需额外的配置和操作。
HEAD
HEAD和GET非常类似,它告诉服务器只返回HTTP首部,不用实际发送文件,这个方法最常用的是检查文件在最后一次缓存之后是否有修改。
public class QuizCardBuilder {
public static void main(String[] args) throws IOException {
URL url;
try{
url=new URL("http://www.baidu.com");
HttpURLConnection http=(HttpURLConnection) url.openConnection();
http.setRequestMethod("HEAD");
System.out.println(url+"was last modified at"+new Date(http.getLastModified()));
Map<String, List<String>> map = http.getHeaderFields();
for (Map.Entry<String, List<String>> entry : map.entrySet()) {
System.out.println("Key : " + entry.getKey() +
" ,Value : " + entry.getValue());
}
}catch(MalformedURLException ex){
System.err.println("Usage: java FormPoster url");
}
}
}
DELETE
DELETE方法将删除WEB服务器上位于指定URL得文件,由于这个请求存在明显的安全风险,所以不是所有的服务器都支持该方法,即便支持,也要进行一定的身份认证。
PUT
许多HTML编辑器和其他希望在Web服务器上存储文件的程序会使用PUT方法,这个方法允许客户端将文档放在网站的抽象层次结构中,而不需要知道网站如果映射到实际本地文件系统的。这正与FTP相反,FTP需要知道实际的目录结构,而不是服务器的抽象结构。同样DELETE一样它也是不安全的。
OPTIONS
该请求方法询问某个特定的URL支持哪些选项,如果请求的URL是星号,那么这个请求会应用到整个服务器而不是服务器上的某个URL。服务器响应OPTIONS,会发送一个HTTP首部以及这个URL上允许的命令列表。
TRACE
该方法会发送HTTP首部,服务器将从客户端接受这个HTTP首部。之所以需要这个信息,主要原因是要查看服务器和客户端之间的代理服务器做了哪些修改。