浅谈JavaWeb

什么是Javaweb

  1. web:万维网(www),能够通过浏览器访问的网站

  2. JavaWeb:用java技术解决相关web互联网领域的技术栈

    QQ_1733885855693 - 采用B/S架构,浏览器/服务器 架构模式,特点:客户端只需要浏览器,应用程序的逻辑和数据都存储在服务端,浏览器只需要请求服务器,获取web资源,服务器把web资源发送给浏览器即可。好处:易于维护升级,服务器端升级后,客户端无需任何部署就可以使用最新版本
    • 静态资源:HTML、CSS、JavaScript、图片等。负责页面展现

    • 动态资源:Servlet、JSP等。负责逻辑处理

    • 数据库:负责存储数据

    • HTTP协议:定义通信规则,一次响应对应一次请求

      1. 对事务没有记忆能力

      2. 每次请求和响应都是独立的

      3. 访问速度快

      4. 多次请求间不能共享数据,Java中使用会话技术(cookie,session)来解决这个问题

      5. 分为POST请求(参数在请求体中)和GET请求(参数在请求行中,有大小限制)

      6. 常见的HTTP 响应头

        1
        2
        3
        4
        5
        6
        Content-Type:表示该响应内容的类型,例如text/html
        image/jpeg;
        Content-Length:表示该响应内容的长度(字节数)
        Content-Encoding:表示该响应压缩算法,例如gzip;
        Cache-Control:指示客户端应如何缓存,例如max-age=300
        表示可以最多缓存300秒
    • Web服务器:负责解析 HTTP 协议,解析请求数据,并发送响应数据

使用Java操作

认识Serlvet

  1. Servlet是用Java编写的服务器端程序,其主要功能在于交互式地浏览和修改数据,生成动态Web内容

  2. Servlet是指任何实现了这个Servlet接口的类

  3. 工作模式

    • 客户端发送请求至服务器
    • 服务器启动并调用Servlet,Servlet根据客户端请求生成响应内容并将其传给服务器
    • 服务器将响应返回客户端
  4. 工作原理

    • Servlet接口定义了Servletservlet容器之间的约定
    • Servlet容器将Servlet类载入内存,并产生Servlet实例和调用它具体的方法
    • 在一个应用程序中,每种Servlet类型只能有一个实例
    • ServletRequest中封装了当前的Http请求,ServletResponse表示当前用户的Http响应
    • 对于每一个应用程序,Servlet容器还会创建一个ServletContext对象。这个对象中封装了上下文(应用程序)的环境详情。每个应用程序只有一个ServletContext。每个Servlet对象也都有一个封装Servlet配置的ServletConfig对象
    • 通过编写实现Servlet的Servlet类进行操作
  5. 体系结构

    QQ_1733886752000

Servlet具体使用

  1. 创建一个类实现Servlet接口,重写方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    public class LoginServlet implements Servlet { 
    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
    //初始化方法
    //当Servlet第一次被请求时,Servlet容器就会开始调用这个方法来初始化一个Servlet对象出来,但是这个方法在后续请求中不会在被Servlet容器调用
    }
    @Override
    public ServletConfig getServletConfig() {
    return null;
    }
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
    //处理get/post请求的方法
    //每当请求Servlet时,Servlet容器就会调用这个方法
    }
    @Override
    public String getServletInfo() {
    return null;
    }
    @Override
    public void destroy() {
    //销毁的方法
    //要销毁Servlet时,Servlet容器就会调用这个方法
    }
    }
    //其他的方法
    //getServletInfo( ),这个方法会返回Servlet的一段描述,可以返回一段字符串。getServletConfig( ),这个方法会返回由Servlet容器传给init( )方法的ServletConfig对象

  2. web.xml文件中配置Servlet的映射关系

    1
    2
    3
    4
    5
    6
    7
    8
    <servlet> 
    <servlet-name>自定义名称</servlet-name> //3
    <servlet-class>处理请求的类的完整路径</servlet-class> //4
    </servlet>
    <servlet-mapping><!-- mapping 表示映射 -->
    <servlet-name>自定义名称</servlet-name> //2
    <url-pattern>请求名</url-pattern> //1
    </servlet-mapping>
  3. 使用注解的方式开发Servlet 3.0版本后支持,注意/和/*的区别

    • 当我们的项目中的Servet配置了“/”,会覆盖掉tomcat中的DefaultSerlet,当其他的 url-pattern都匹配不上时都会走这个Servlet,导致静态资源访问不到
    • 当我们的项目中配置了“/*”,意味着匹配任意访问路径
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    @WebServlet(
    //name和value属性不能同时出现
    name = "TestWebServlet",
    /*value = {"/demo", "/web"},*/
    //配置的路径,配置规则
    //urlPattern 配置规则
    //精确匹配 目录匹配 扩展名匹配 任意匹配
    urlPatterns = {"/demo01", "/web01"},
    //servlet的加载顺序
    loadOnStartup = 1,
    //指定一组Servlet初始化参数
    initParams = {
    @WebInitParam(name = "username", value = "root"),
    @WebInitParam(name = "password", value = "123456"),
    }
    )
  4. 在日常使用中我们都继承HttpServlet接口

    1. HttpServlet内部自己实现了serlvet方法,把接收到的ServletRequsest类型的对象转换成了HttpServletRequest类型的对象,把ServletResponse类型的对象转换成了HttpServletResponse类型的对象,把两个转换后的对象传入了另一个service方法
    2. 自己的serlvet方法会解析HttpServletRequest中的方法参数,调用以下方法之一:doGet,doPost,doHead,doPut,doTrace,doOptions和doDelete
    3. 我们在实现的时候只需要覆盖doGet或者doPost方法
  5. HttpServlet的一些常用方法

    • 请求常用方法

      1
      2
      3
      4
      5
      String getParameter(String name)// 根据表单组件名称获取提交数据 name和表单name相同
      String[ ] getParameterValues(String name) //获取表单组件对应多个值时的请求数据
      void setCharacterEncoding(String charset) //指定每个请求的编码(针对post请求才起作用),解决乱码问题
      RequestDispatcher getRequestDispatcher(String path).forward(req,resp) //--转发,带有原有的请求参数
      request.setAttribute(“key”,value);//存值,还有通过get方法取值
    • 响应常用方法

      1
      2
      3
      4
      void addCookie(Cookie var1);//给这个响应添加一个cookie
      void sendRedirect(String var1) ;//重定向,不带请求参数,地址栏会改变
      PrintWriter getWriter()// 获得字符流,通过字符流的write(String s)方法可以将字符串设置到response 缓冲区中,服务器将内容组成HTTP响应返回给客户端显示
      void setContentType("text/html;charset=UTF-8");//解决乱码问题
    • 转发属于一次请求一次响应,重定向属于两次请求,两次响应

  6. 重写HttpServlet中的Servlet方法,使用请求路径进行方法的分发

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;

    /**
    * 修改分发的方式
    */
    public class BaseServlet extends HttpServlet {

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    //获取请求路径
    String uri = req.getRequestURI();
    //获取最后一段路径 比如brand-case/brand/selectAll 中的selectAll
    int index = uri.lastIndexOf("/");
    String methodName = uri.substring(index + 1);
    //获取继承BaseServlet的字节码对象 Class
    //谁调用this所在的方法,this就代表谁
    Class<? extends BaseServlet> cls = this.getClass();
    try {
    //通过反射获取方法对象
    Method method = cls.getMethod(methodName, HttpServletRequest.class, HttpServletResponse.class);
    try {
    //调用cls实例上的方法
    method.invoke(this, req, resp);
    } catch (IllegalAccessException e) {
    throw new RuntimeException(e);
    } catch (InvocationTargetException e) {
    throw new RuntimeException(e);
    }
    } catch (NoSuchMethodException e) {
    throw new RuntimeException(e);
    }

    }
    }

通过Tomcat管理Servlet

  1. Tomcat是独立运行的Servlet容器,内置了Servlet的API

  2. Servlet需要依赖Tomcat才能运行

  3. 流程图

    QQ_1733896197827
  4. 如何使用

    • 本地安装tomcat

    • 配置相应的工件和web框架

    • 使用maven插件,pom文件的打包方式为war

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      <plugins>
      <plugin>
      <groupId>org.apache.tomcat.maven</groupId>
      <artifactId>tomcat7-maven-plugin</artifactId>
      <version>2.2</version>
      <configuration>
      <path>/</path>
      <port>8080</port>
      <uriEncoding>UTF-8</uriEncoding>
      </configuration>
      </plugin>
      </plugins>

会话跟踪技术

概述

  1. HTTP协议是无状态的,每次请求都会视为新的请求,通过会话跟踪技术实现会话内数据共享

  2. 什么是会话:

用户打开浏览器,访问web服务器的资源,会话建立,直到有一方断开连接,会话结束。在一次会话中可以包含多次请求和响应

  1. 会话跟踪:

一种维护浏览器状态的方法,服务器需要识别多次请求是否来自于同一浏览器,以便在同一次会话
的多次请求间共享数据

  1. 实现方式

客户端:Cookie

服务端:Session

Cookie基本使用

  1. 将数据保存在客户端,以后每次请求都携带Cookie数据进行访问

  2. 基本使用

1
2
3
4
5
6
7
8
9
10
//1.创建Cookie对象,设置数据
Cookie cookie = new Cookie("key","value");
//2.发送Cookie到客户端:使用response对象
response.addCookie(cookie);
//3.获取客户端携带的所有Cookie,使用request对象
Cookie[] cookies = request.getCookies();
//4.遍历数组,获取每一个Cookie对象:for
//5.使用Cookie对象方法获取数据
cookie.getName();
cookie.getValue();
  1. 原理

Cookie的实现是基于HTTP协议的
响应头:set-cookie
请求头:cookie

  1. 使用细节

    • 存活时间:默认存放在浏览器内存中,浏览器关闭内存释放,自动销毁

    • setMaxAge(int seconds):设置Cookie的存活时间

      正数:将 Cookie写入浏览器所在电脑的硬盘,持久化存储。到时间自动删除
      负数:默认值,Cookie在当前浏览器内存中,当浏览器关闭,则Cookie被销毁

      零:删除对应 Cookie

    • 不能直接存储中文,需要进行URL编码

  2. 优缺点

    优点:HTTP协议中支持的内容

    缺点:

    • 移动端APP无法使用
    • 用户可以自己禁用Cookie
    • 不能跨域,跨域分为三个维度:协议、IP/域名、端口,一个不同都算跨域

Session的使用

  1. 服务端会话技术,将数据保存在服务端

  2. JavaEE 提供 HttpSession接口,来实现一次会话的多次请求间数据共享功能

  3. 使用

1
2
3
4
5
6
//1.获取Session对象
HttpSession session = request.getSession();
//2.Session对象功能:
void setAttribute(String name, Object o)//存储数据到 session 域中
Object getAttribute(String name)//根据 key,获取值
void removeAttribute(String name)//根据 key,删除该键值对
  1. 原理

基于Cookie实现的
同一会话的两次请求访问的session是同一对象tomcat会将创建的session当做cookie对象发送到请求头中,而后在请求时访问的就是已经有的session对象

  1. 使用细节

Session的钝化:服务器正常关闭后,Tomcat会自动将Session数据写入硬盘

​ 活化:再次启动服务器后,从文件中加载数据到Session中

Session的销毁:默认无操作,30分钟后销毁,调用invalidate()方法

1
2
3
4
配置文件配置
<session-config>
<session-timeout>30</session-timeout>
</session-config>
  1. 优缺点

    优点:存储在服务端,安全

    缺点:服务器集群环境下无法直接使用,包含Cookie的所有缺点

两者的相同和区别

  1. 都是来完成一次会话内多次请求间数据共享的
  2. 区别:
  • 存储位置:Cookie 是将数据存储在客户端,Session 将数据存储在服务端
  • 安全性:Cookie不安全,Session安全
  • 数据大小:Cookie最大3KB,Session无大小限制
  • 存储时间:Cookie 可以长期存储,Session默认30分钟
  • 服务器性能:Cookie 不占服务器资源,Session 占用服务器资源

JWT令牌

  • 概述

    全称:JSON Web Token

    定义了一种简洁,可自定义的格式,通过JSON格式进行封装在通信双方间进行传输,以数字签名保证信息的安全性

  • 具体组成

  • 使用

    导入maven坐标

    1
    2
    3
    4
    5
    <dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
    </dependency>

    创建配置类进行配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    package com.sky.properties;

    import lombok.Data;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.stereotype.Component;

    @Component
    @ConfigurationProperties(prefix = "sky.jwt")
    @Data
    public class JwtProperties {

    /**
    * 管理端员工生成jwt令牌相关配置
    */
    //设置jwt签名加密时使用的秘钥
    private String adminSecretKey;
    //设置jwt过期时间
    private long adminTtl;
    //设置前端传递过来的令牌名称
    private String adminTokenName;

    }

    yml配置文件中设置,到时候通过自动装配的形式将配置加载到JwtProperties bean中

    1
    2
    3
    4
    5
    6
    7
    8
    sky:
    jwt:
    # 设置jwt签名加密时使用的秘钥
    admin-secret-key: itcast
    # 设置jwt过期时间
    admin-ttl: 7200000000000000
    # 设置前端传递过来的令牌名称
    admin-token-name: token

    创建加密和解密的工具类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    public class JwtUtil {
    /**
    * 生成jwt
    * 使用Hs256算法, 私匙使用固定秘钥
    *
    * @param secretKey jwt秘钥
    * @param ttlMillis jwt过期时间(毫秒)
    * @param claims 设置的信息
    * @return
    */
    public static String createJWT(String secretKey, long ttlMillis, Map<String, Object> claims) {
    // 指定签名的时候使用的签名算法,也就是header那部分
    SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;

    // 生成JWT的时间
    long expMillis = System.currentTimeMillis() + ttlMillis;
    Date exp = new Date(expMillis);

    // 设置jwt的body
    JwtBuilder builder = Jwts.builder()
    // 如果有私有声明,一定要先设置这个自己创建的私有的声明,这个是给builder的claim赋值,一旦写在标准的声明赋值之后,就是覆盖了那些标准的声明的
    .setClaims(claims)
    // 设置签名使用的签名算法和签名使用的秘钥
    .signWith(signatureAlgorithm, secretKey.getBytes(StandardCharsets.UTF_8))
    // 设置过期时间
    .setExpiration(exp);

    return builder.compact();
    }

    /**
    * Token解密
    *
    * @param secretKey jwt秘钥 此秘钥一定要保留好在服务端, 不能暴露出去, 否则sign就可以被伪造, 如果对接多个客户端建议改造成多个
    * @param token 加密后的token
    * @return
    */
    public static Claims parseJWT(String secretKey, String token) {
    // 得到DefaultJwtParser
    Claims claims = Jwts.parser()
    // 设置签名的秘钥
    .setSigningKey(secretKey.getBytes(StandardCharsets.UTF_8))
    // 设置需要解析的jwt
    .parseClaimsJws(token).getBody();
    return claims;
    }

    }