HTTP,Servlet,加密方式的使用【以MD5为例】(五)

419 阅读10分钟

HTTP

全称:Hyper Typer Text Transfer Protocol【超文本传输协议】,是无状态协议。

作用:约束报文格式。

“报文”:浏览器与服务器通信时传输的数据,称之为报文。

浏览器与服务器通信方式:

​ 浏览器向服务器发出:请求;服务器向浏览器做出:响应。

1 报文种类

请求报文

​ 浏览器向服务器发送请求时,携带数据,称之为请求报文。

(1)请求报文的三个部分

①请求行:展示当前请求的最基本信息

​ 包含三个信息:请求方式,访问地址,HTTP协议的版本

②请求消息头:通过具体的参数对本次请求进行详细的说明

​ 使用的格式是键值对,键和值之间使用冒号隔开。

名称功能
Host服务器的主机地址
Accept声明当前请求能够接受的『媒体类型』
Referer当前请求来源页面的地址
Content-Length请求体内容的长度
Content-Type请求体的内容类型,这一项的具体值是媒体类型中的某一种
Cookie浏览器访问服务器时携带的Cookie数据

③请求体:作为请求的主体,发送数据给服务器。具体来说其实就是POST请求方式下的请求参数。

(2)请求方式

HTTP1.1中共定义了八种请求方式:

  • GET:从服务器端获取数据
  • POST:将数据保存到服务器端
  • PUT:命令服务器对数据执行更新
  • DELETE:命令服务器删除数据
  • HEAD
  • CONNECT
  • OPTIONS
  • TRACE

①GET请求

  • 没有请求体
  • 请求参数附着在URL地址后面?拼接参数
  • 请求参数在浏览器地址栏能够直接被看到,存在安全隐患
  • 在URL地址后面携带请求参数,数据容量非常有限。如果数据量大,那么超出容量的数据会丢失
  • 从报文角度分析,请求参数是在请求行中携带的,因为访问地址在请求行

②POST请求

  • 有请求体
  • 请求参数放在请求体中
  • 请求体发送数据的大小没有限制
  • 可以发送各种不同类型的数据
  • 从报文角度分析,请求参数是在请求体中携带的
  • 由于请求参数是放在请求体form data中,所以浏览器地址栏看不到

对比:

​ 参数区别:GET请求参数在URL后面使用?拼接参数;POST请求参数,存储在请求体中。

​ 安全性:GET相对不安全,POST相对安全。

​ 数据大小:GET请求参数在URL后面数据有大小限制;POST参数在请求体中,大小没有限制。

响应报文

​ 服务器向浏览器做出响应时,携带数据,称之为响应报文。

(1)响应报文的三个部分

①响应状态行

​ 包括三个部分:HTTP协议版本,响应状态码,响应状态的说明文字。

②响应消息头:响应体的说明书。服务器端对浏览器端设置数据,例如:服务器端返回Cookie信息。

名称功能
Content-Type响应体的内容类型
Content-Length响应体的内容长度
Set-Cookie服务器返回新的Cookie信息给浏览器
location重定向的情况下,告诉浏览器访问下一个资源的地址

③响应体:服务器返回的数据主体,有可能是各种数据类型。

  • HTML页面
  • 图片
  • 视频
  • 以下载形式返回的文件
  • CSS文件
  • JavaScript文件

响应状态码

作用:以编码的形式告诉浏览器当前请求处理的结果。

状态码含义
200服务器成功处理了当前请求,成功返回响应
302重定向
400[SpringMVC特定环境]请求参数问题
403没有权限
404找不到目标资源
405请求方式和服务器端对应的处理方式不一致
406[SpringMVC特定环境]请求扩展名和实际返回的响应体类型不一致
50X服务器端内部错误,通常都是服务器端抛异常了

404产生的具体原因:

  • 访问地址写错了,确实是没有这个资源
  • 访问了WEB-INF目录下的资源
  • Web应用启动的时候,控制台已经抛出异常,导致整个Web应用不可用,访问任何资源都是404
  • 服务器端缓存

Servlet

1 为什么使用Servlet

​ 原因:以实现注册功能为例,需要将html中数据最终插入数据库中,所以此时需要将html中数据先提交给java代码,在使用JDBC技术插入数据库。所以我们需要servlet技术。

2 什么是Servlet

​ 理解:Servlet就是具有URL属性【特性】的java程序。

​ 定义:Servlet就是运行在服务端(tomcat)的Java小程序,SUN公司提供了一套定义动态资源规范。

​ 代码:Servlet就是一个接口,或者javax.servlet.Servlet接口的实现类。

3 Servlet之HelloWorld

  1. 创建动态工程
    • javax.servlet.Servlet这个包在servlet-api.jar包下
    • 需要为web.xml【注解】中为Servlet实现注册
  2. 准备html发送请求
  3. 创建HelloworldServlet类
    • HelloworldServlet实现javax.servlet.Servlet
    • 重写接口中相应方法【接口中方法,默认抽象方法】
  4. 在web.xml中注册HelloworldServlet
    • 设置全类名【类的全限定名】,通知Servlet容器【web容器、服务器】创建当前Servlet
    • 为当前Servlet设置URL特性

运行时的servlet默认在web目录下。

4 Servlet工作原理

  • 浏览器向服务器端组件Servlet发送请求【<a href="HelloWorldServlet">HelloworldServlet</a>】
  • 通过指定URL去web.xml中检索注册URL【<url-pattern>/HelloWorldServlet</url-pattern>】
  • 通过servlet-mapping中的servlet-name与servlet中的servlet-name进行匹配
  • 匹配成功,会找到指定servlet-class
  • 执行Servlet中的相应方法【service()】

5 Servlet生命周期

构造器:Servlet创建时,调用构造器方法:

​ 执行时机:第一次请求Servlet时,触发构造器,创建Servlet对象。

​ 执行次数:整个生命周期中,只执行一次。

init():初始化Servlet时,调用init()方法。

​ 执行时机:第一次请求servlet时,构造器之后,执行init()。

​ 执行次数:在整个生命周期中,只执行一次。

service():处理请求,做出响应时,调用service()方法。

​ 执行时机:每次请求Servlet时,均调用service(),处理请求做出相应。

​ 执行次数:在整个生命周期中,执行多次。

destory():销毁Servlet对象时,调用destory()方法。

​ 执行时机:在关闭服务器时,调用destory()方法,销毁Servlet对象

​ 执行次数:在整个生命周期中,只执行一次。

注意:有特殊情况!

当在web.xml中设置了整个Servlet的

<load-on-startup>1</load-on-starup>

也就是Servlet优先级。

这个数值越小,优先级越高。数值一般写正整数。

**此时,启动服务器时,就已经创建了Servlet对象【执行构造器】,接着调用了init()方法进行初始化。**然后每次请求Servlet时,均调用service()。在关闭服务器时,调用destory()方法,销毁Servlet对象

6 ServletConfig与ServletContext

ServletConfig

概念:

​ ServletConfig对象封装Servlet配置信息,每个Servlet都有唯一对应ServletConfig对象,该对象由服务器创建,最终以参数的形式传入到init()方法中。

主要的几个方法:

  • 获取servlet名称:servletConfig.getServletName()
  • 获取servlet初始化参数值:servletConfig.getInitParameter()
  • 获取ServletContext对象:servletConfig.getServletContext()

ServletContext【重要】

概念:

​ ServletContext对象是Servlet上下文对象,每个web应用对应唯一ServletContext,该对象由服务器创建,获取方式如下:

  • servletConfig.getServletContext()
  • request.getServletContext()

方法:

  • 获取指定资源的真实路径【d:/】:servletContext.getRealPath()

  • 获取上下文路径:servletContext.getContextPath()

    • 上下文路径:通过上下文路径,访问当前项目
  • 获取上下文初始化参数:servletContext.getInitParameter()

    • 配置上下文初始化参数
<context-param>
    <param-name>jdbcurl</param-name>
    <param-value>jdbc:mysql://localhost:3306/dbname</param-value>
</context-param>
<servlet>
</servlet>

ServletContext是域对象

  • 一共四个域
  • 它具有域对象都有的方法:
    • ServletContext.setAttribute()
    • ServletContext.getAttribute()
    • ServletContext.removeAttribute()
  • 根据有作用范围由小到大:
    • page(jsp有效)------》page域指的是pageContext.
    • request(一次请求)---》request域request HttpServletContext
    • session(一次会话)---》session域session HttpSession
    • application(当前web应用)---》application域指的是application ServletContext;

6. 最终创建Servlet方式

7. 最终创建Servlet方式

目前创建Servlet方式不足

  1. 最好只留下service()方法,处理请求做出响应
  2. 最好提示注册Servlet

继承HttpServlet

自定义servlet ——继承——> HttpServlet ——继承——> GenericServlet——实现——> javax.servlet.Servlet(和servletConfig)

GenericServlet与HttpServlet作用

(1)GenericServlet作用:

①提供获取对象方法【getServletConfig()和getServletContext】

@Override
public ServletConfig getServletConfig() {
    return config;
} 
@Override
    public ServletContext getServletContext() {
        return getServletConfig().getServletContext();
    }

②将service()方法,抽象化

@Override
public abstract void service(ServletRequest req, ServletResponse res)
        throws ServletException, IOException;

(2)HttpServlet作用

①重写service():类型转换

@Override
public void service(ServletRequest req, ServletResponse res)
    throws ServletException, IOException {
    HttpServletRequest  request;
    HttpServletResponse response;
    try {
        request = (HttpServletRequest) req;
        response = (HttpServletResponse) res;
    } catch (ClassCastException e) {
        throw new ServletException(lStrings.getString("http.non_http"));
    }
    service(request, response);
}

②重载service():通过请求方式不同,调用不同处理方法

protected void service(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException {
	//获取请求方式
    String method = req.getMethod();
    if("GET".eq(method)){
        doGet(req,resp);
    }else if("POST".eq(method)){
        doPost(req,resp);
    }else if()...
    
}

Servlet中提供7种请求方式

GET POST PUT DELETE等。

HTTP/1.1协议中共定义了八种方法(有时也叫“动作”),来表明Request-URL指定的资源不同的操作方式:

HTTP1.0定义了三种请求方法: GET, POST 和 HEAD方法。

HTTP1.1新增了五种请求方法:OPTIONS, PUT, DELETE, TRACE 和 CONNECT 方法。

加密方式

简介

对称加密:加密和解密使用相同的密钥,常见的对称加密算法有:DES、3DES

非对称加密:加密和解密使用的密钥不同,常见的非对称加密算法有RSA。

加密:使用私钥加密。

解密:使用公钥解密。

消息摘要:消息摘要算法的主要特征是加密过程中不需要密钥,并且经过加密的消息无法被揭密,只有相同的原文经过消息摘要算法之后,才能得到相同的密文,所有消息摘要通常用来校验原文的真伪。常用的消息摘要算法有:MD5、SHA、MAC。

位置

包:java.security

抽象类:MessageDigest

方法:

public static MessageDigest getInstance(String algorithm) throws NoSuchAlgorithmException获得对应加密方法的实例,有返回值,需要重新赋值给对象MessageDigest。

public byte[] digest(byte[] input)对输入的字节数组执行加密算法,返回加密后的字节数组。

使用方式

  1. 创建MessageDigest对象messageDigest;

  2. 使用MessageDigest类的静态方法getInstance(加密方式名),将返回值赋给messageDigest;

  3. 将要进行加密的字符串转为字节数组input

  4. 使用messageDigest调用方法digest(input),对字节数组加密为output数组【新数组,使用对象接收】

  5. 使用BigInteger(int signum, byte[] magnitude)将BigInteger的符号大小表示形式转换为BigInteger。 将字节数组转换为一个大数。

    BigInteger(int signum, byte[] magnitude)构造方法

    signum - 数字的符号(-1为负,0为零,1为正)。

    magnitude - 大端二进制表示的数量的大小。

  6. 使用toString(int radix)通过十六进制将大数转换为字符串,得到最终编码的字符串encode。

    public String toString(int radix)非静态成员方法

    返回给定基数中BigInteger的String表示形式。 如果基数在Character.MIN_RADIX到Character.MAX_RADIX之间,则默认为10(如Integer.toString )。 使用由Character.forDigit提供的数字到字符的映射,如果合适,则添加减号。 (此表示与(String, int)构造函数兼容。)

    //Character部分类常量
    public static final int MIN_RADIX = 2;
    public static final int MAX_RADIX = 36;
    

实例代码

public class MD5Util {
    /**
     * 针对明文字符串执行MD5加密
     * @param source
     * @return
     */
    public static String encode(String source) {
        /*1. 首先判断铭文字符串是否有效*/
        if(source == null || "".equals(source)){
            throw new RuntimeException("要进行加密的明文不能是空的");
        }
        /*2. 声明算法名称*/
        String algorithm = "MD5";
        /*3. 获取MessageDigest对象*/
        MessageDigest messageDigest = null;
        try {
            messageDigest = MessageDigest.getInstance(algorithm);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        /*4. 获取明文字符串对应的字节数组*/
        byte[] input = source.getBytes();
        /*5. 执行加密*/
        byte[] output = messageDigest.digest(input);
        /*6. 创建BigInteger对象*/
        int signum = 1;
        BigInteger bigInteger = new BigInteger(signum, output);
        /*7. 按照16进制将bigInteger的值转换为字符串*/
        int radix = 16;
        String encoded = bigInteger.toString(radix).toUpperCase();
​
        return encoded;
    }
}

\