Java从零开始代码审计

前言

大二暑假开始系统学习java,并记录自己的学习过程和知识点总结。虽然专业课已经学过java了,有一些基础,但是代码审计还是需要知道详细的语法知识,这里还是重新学一遍,不然也不算是从零到一。

另外这篇笔记仅仅记录到javaweb的基础实现。

主要参考视频

问道安全工防实验室

MS08067安全实验室

JavaSE

JavaSE就是java编程的一些基础,后续的JavaEE就是Java Web了。

参考文章:https://c.biancheng.net/java/20/

基础

标识符

由数字(09)和字母(AZ 和 a~z)、美元符号($)、下划线(_)以及 Unicode 字符集中符号大于 0xC0 的所有符号组合构成(各符号之间没有空格)。

标识符的第一个符号为字母、下划线和美元符号,后面可以是任何字母、数字、美元符号或下划线。不能用数字开头

关键字

保留特殊意义的固定单词,单词意义后续会讲

类别 内容
流程控制: if、else、do、while、for、switch、case、default、break、continue、return、try、catch、finally
修饰符: public、protected、private、final、void、static、strict、abstract、transient、synchronized、volatile、native
动作: package、import、throw、throws、extends、implements、this、supper、instanceof、new
保留字: true、false、null、goto、const
数据类型: boolean、int、long、short、byte、float、double、char、class、interface
public是关键字,但Public不是

注释

单行注释// 多行注释/* */

1
2
3
4
5
文档注释:
/** 
*文档注释
*与多行差不多
*/

常量定义

final定义常量

final int a=10不可修改

public static final double PI = 3.14静态常量,不可修改,不用实例化对象就可以引用

static final

整数常量值

进制
十进制 54、67、0
八进制 0开头,0125
十六进制 0x或0X开头,0x125

变量

与c相同,先声明后使用

成员变量分为全局变量和静态变量,前者无static修饰,后者有

全局变量只要对象被引用,变量就存在,有static修饰的静态变量就不需要实例化对象就可以引用

局部变量是指在方法或者方法代码块中定义的变量,其作用域是其所在的代码块。可分为以下三种:

类别 特点
方法参数变量(形参): 在整个方法内有效。(传参的变量)
方法局部变量(方法内定义): 从定义这个变量开始到方法结束这一段时间内有效。(类中方法定义的变量)
代码块局部变量(代码块内定义): 从定义这个变量开始到代码块结束这一段时间内有效。(异常处理语句)

数据类型与转换

类型名称 关键字 占用内存
字节型 byte 1 字节
短整型 short 2 字节
整型 int 4 字节
长整型 long 8 字节
单精度浮点型 float 4 字节
双精度浮点型 double 8 字节
字符型 char 2 字节
布尔型 boolean 1 字节

隐式转换

当满足以下条件,将执行自动类型转换

  • 两种数据类型彼此兼容
  • 目标类型的取值范围大于源数据类型(低级类型数据转换成高级类型数据)

(byte→short→int→long→float→double)(char→int(ascii码转化))

显示转化(强制转化)

1
2
3
int a = 3;
double b = 5.0;
a = (int)b;

运算符

同c,不多赘述

一元:-(取反)、++、–

二元:+、-、*、/、%

算数赋值:+=、-=、*=、/=、%=

逻辑运算符:&&、||、!(非)、|(或)、&(与)

关系:>、>=、<=、…….

位移:»(右移)、«(左移)

复合位赋值:&=、|=、^=、-=、«=、»=(同算数赋值)

三目运算符:z = x>y ? x-y : x+y; x>y为真时返回x-y,为假时返回x+y

优先级:略

直接量:int a=5 5就是直接量

int 123
long 123L
float 123F
double 12.3
boolean ture/false
char ‘a’、’\n’、’\u0061’
string “字符串”
null null

流程控制语句

大体与c相同,这里粗略过一遍

语句结束需要分号;

1
2
3
4
5
6
//if else语句
if(){
	语句1;
}else{
	语句2;
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
//switch语句
switch(表达式) {
    case 值1:
        语句块1;
        break;
    case 值2:
        语句块2;
        break;
    
    case 值n:
        语句块n;
        break;
    default:
        语句块n+1;
    break;
}
1
2
3
4
//while语句
while(条件表达式) {
    语句块;
}
1
2
3
4
//do while语句
do {
    语句块;
}while(条件表达式);
1
2
3
4
//for循环语句
for(赋值语句;条件语句;迭代语句) {
    语句块;
}
1
2
3
4
//foreach 循环语句,用于遍历
for(类型 变量名:集合) {
    语句块;
}
1
2
//return;
return 变量;    //方法结束,并返回与方法类型相同变量
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
//break;
break;          //跳出循环  
break label标识代码块的标签;
public static void main(String[] args) {
        label: for (int i = 0; i < 10; i++) {
            for (int j = 0; j < 8; j++) {
                System.out.println(j);
                if (j % 2 != 0) {
                    break label;
                }
            }
        }
    }
/*break label;可以终止执行一个或者几个任意代码块,这些代码块不必是一个循环或一个 switch 语句的一部分。同时这种扩展的 break 语句带有标签,可以明确指定从何处重新开始执行。*/
1
2
3
//continue;
continue;       //跳出本次循环
//同样支持label

字符串

定义

用数据类型

String str = "Hello Java";

或字符串类定义

String() :新创建的字符串对象s

String(String original):赋值与参数相同的字符序列

String(char[ ]value):将字符数组拼接成字符串

String(char[] value,int offset,int count):同上,但是可以自己定义从哪里拼接(offset),拼接多少个字符(count)

转化

String转化int

(String的值必须要是整数)

通过包装类Integer.parse(str)Integer.valueOf(str).intValue()

int转化String

String s = String.valueOf(i);

String s = Integer.toString(i);

String s = "" + i;

valueOf() 方法将数据的内部格式转换为可读的形式。静态方法,在字符串内被重载,所以每一种类型都能变成字符串
parseXxx(String) 这种形式,是指把字符串转换为数值型,其中 Xxx 对应不同的数据类型,然后转换为 Xxx 指定的类型,如 int 型和 float 型。
toString() 可以把一个引用类型转换为 String 字符串类型,是 sun 公司开发 Java 的时候为了方便所有类的字符串操作而特意加入的一个方法。(编程还挺常用的)

处理

拼接:

1
2
3
`+

用concat()方法 字符串1.concat(字符串2);

获取长度:

1
字符串名.length();

大小写转换:

1
2
3
字符串名.toLowerCase()全字母转化成小写

字符串名.toUpperCase()全字母转化成大写

去除字符串中的空格:

1
字符串名.trim()

提取子字符串(按子符截取):

1
2
3
substring(int beginIndex)从索引beginIndex处开始至结尾

substring(int beginIndexint endIndex)从beginIndex到endIndex

分割字符串:

1
2
3
4
5
6
7
str.split(String sign)
str.split(String sign,int limit)
/*
str 为需要分割的目标字符串;
sign 为指定的分割符,可以是任意字符串。
limit 表示分割后生成的字符串的限制个数,如果不指定,则表示不限制,直到将整个目标字符串完全分割为止。
*/

替换:

replace():用于将目标字符串中的指定字符(串)替换成新的字符(串)

1
字符串.replace(String oldChar, String newChar)

replaceFirst() :用于将目标字符串中匹配某正则表达式的第一个子字符串替换成新的字符串

1
字符串.replaceFirst(String regex, String replacement)

replaceAll():用于将目标字符串中匹配某正则表达式的所有子字符串替换成新的字符串

1
字符串.replaceAll(String regex, String replacement)

比较

equals() :逐个地比较两个字符串的每个字符是否相同

1
2
3
4
5
6
7
str1.equals(str2);
//注意,equals() 方法比较字符串对象中的字符。而==运算符比较两个对象引用看它们是否引用相同的实例。

String s1 = "Hello";
String s2 = new String(s1);
System.out.println(s1.equals(s2)); // 输出true
System.out.println(s1 == s2); // 输出false

equalsIgnoreCase() :不区分大小写,其他与equals()相同

1
str1.equalsIgnoreCase(str2);

compareTo() :按字典顺序比较两个字符串的大小,该比较是基于字符串各个字符的 Unicode 值。(用于排序)

1
str.compareTo(String otherstr);

查找

indexOf() :返回字符(串)在指定字符串中首次出现的索引位置,如果能找到,则返回索引值,否则返回 -1

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
str.indexOf(value)
str.indexOf(value,int fromIndex)
/*
str 表示指定字符串;
value 表示待查找的字符(串);
fromIndex 表示查找时的起始索引,如果不指定 fromIndex,则默认从指定字符串中的开始位置(即 fromIndex 默认为 0)开始查找。
*/

String s = "Hello Java";
int size = s.indexOf('v');    // size的结果为8

lastlndexOf():返回字符(串)在指定字符串中最后出现的索引位置,如果能找到,则返回索引值,否则返回 -1

1
2
3
str.indexOf(value)
str.indexOf(value,int fromIndex)
//同上

charAt():在字符串内根据指定的索引查找字符(字符串本质也是字符数组,索引从零开始)

1
字符串名.charAt(索引值)

Java内置包装类

将基本数据类型转化为引用数据类型

序号 基本数据类型 包装类
1 byte Byte
2 short Short
3 int Integer
4 long Long
5 char Character
6 float Float
7 double Double
8 boolean Boolean
1
2
上面在讲字符串String转化int时提到了Integer.parse(str)
其实这里就是用了这个int包装类。我们讲int装箱成Integer就是为了使用这个类的方法

Object类

作为所有类的父类,它的方法继承给了所有子类,所有子类都可以重写他的方法

方法 说明
Object clone() 创建与该对象的类相同的新对象
boolean equals(Object) 比较两对象是否相等
void finalize() 当垃圾回收器确定不存在对该对象的更多引用时,对象垃圾回收器调用该方法
Class getClass() 返回一个对象运行时的实例类
int hashCode() 返回该对象的散列码值
void notify() 激活等待在该对象的监视器上的一个线程
void notifyAll() 激活等待在该对象的监视器上的全部线程
String toString() 返回该对象的字符串表示
void wait() 在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待

其余包装类省略

数组

java数组是引用数据类型,大体与c数组相同。这里主要讲定义和初始化

一维数组

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
定义
type[] arrayName;    // 数据类型[] 数组名;
type arrayName[];    // 数据类型 数组名[];
int[] score

分配空间
arrayName = new type[size]  // 数组名 = new 数据类型[数组长度];
score = new int[10];

初始化
type[] arrayName = new type[]{ 1, 2, 3, 4,  , n};

number[0] = 1;
number[1] = 2;
number[2] = 3;
number[3] = 5;
number[4] = 8;

二维数组

1
2
3
4
初始化
type[][] arrayName = new type[][]{ 1, 2, 3,, n};    // 在定义时初始化
type[][] arrayName = new type[size1][size2];    // 给定空间,在赋值
type[][] arrayName = new type[size][];    // 数组第二维长度为空,可变化

面向对象

从这里开始就是java的主要重点

java类定义

 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
/*
public:修饰后变为共用,可以被其他类和程序访问。相似的还有protect、无和private,后续会详细区分区别
abstract:修饰后变为抽象类,抽象类无法实例化,后续会详细讲解
final:如果类被 final 修饰,则不允许被继承。
class:声明类的关键字
class_name:类的名称
extends:表示继承其他类,就是作为其他类的子类
implements:表示实现某些接口,后续会详细讲解
*/
[public][abstract|final]class<class_name>[extends<class_name>][implements<interface_name>] {
    // 定义属性部分
    [public|protected|private][static][final]<type><variable_name>
/*
    public:同上
    static:修饰后表示静态变量,可以不通过实例化对象调用
	final:表示将该成员变量声明为常量,其值无法更改。
    type:变量类型,int、byte、......
    variable_name:变量名称
*/
    
    // 定义方法部分
     [public|private|protected][static]<void|return_type><method_name>([paramList]) {
        // 方法体
    }
/*
	public:同上
	static:同上
	void|return_type:方法类型
	method_name:方法名字
	paramList:所需参数列表
*/
}

this关键字

this.属性名用于在类的方法中访问类的私有成员

例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
public class Teacher {
    private String name;    // 教师名称
    private double salary;    // 工资
    private int age;    // 年龄
}
public Teacher(String name,double salary,int age) {
    this.name = name;    // 设置教师名称
    this.salary = salary;    // 设置教师工资
    this.age = age;    // 设置教师年龄
}

this.方法名用于让类中一个方法,访问该类里的另一个方法或实例变量。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
public class Dog {
    // 定义一个jump()方法
    public void jump() {
        System.out.println("正在执行jump方法");
    }
    // 定义一个run()方法,run()方法需要借助jump()方法
    public void run() {
        this.jump();
        System.out.println("正在执行 run 方法");
    }
}

this( )访问构造方法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
//只能在构造方法中使用,且只能在第一个语句执行
public class Student {
    String name;
    // 无参构造方法(没有参数的构造方法)
    public Student() {
        this("张三"); //调用有参构造方法
    }
    // 有参构造方法
    public Student(String name) {
        this.name = name;
    }

创建对象

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
//new
类名 对象名 = new 类名()
    
//反射(**后续详解**)
//调用 java.lang.Class 或者 java.lang.reflect.Constuctor 类的 newlnstance() 实例方法
 java.lang.Class Class 类对象名称 = java.lang.Class.forName(要实例化的类全称);
类名 对象名 = (类名)Class类对象名称.newInstance();

//clone()不常用
//使用该方法创建对象时,要实例化的类必须继承 java.lang.Cloneable 接口。
类名对象名 = (类名)已创建好的类对象名.clone();

//调用 java.io.ObjectlnputStream 对象的 readObject() 方法

匿名对象

仅用一次的对象创建

new Person("张三",30).tell();

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
public class Person {
    public String name; // 姓名
    public int age; // 年龄
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public void tell() {
        System.out.println("姓名:" + name + ",年龄:" + age);
    }
    
    public static void main(String[] args) {
        new Person("张三", 30).tell(); // 匿名对象
    }
}

访问对象的成员和方法

对象名.成员变量名

对象名.成员方法名

例:

1
2
3
4
Student stu = null;
stu.Name = "李子文";
stu.Sex = true;
stu.Age = 15;

静态方法、变量可以不需要实例化对象就可以调用

修饰符

访问范围 private friendly(缺省) protected public
同一个类 可访问 可访问 可访问 可访问
同一包中的其他类 不可访问 可访问 可访问 可访问
不同包中的子类 不可访问 不可访问 可访问 可访问
不同包中的非子类 不可访问 不可访问 不可访问 可访问

static

类别 作用
静态变量 在类的内部,可以在任何方法内直接访问静态变量。
在其他类中,可以通过类名访问该类中的静态变量。
静态方法 静态方法不需要通过它所属的类的任何实例就可以被调用

final

使用 final 声明变量时,要求全部的字母大写

final修饰 特点
数据类型变量 无法被改变
引用类型变量 对数组赋值非法、对数组元素赋值合法
方法 无法被重写、可被重载
无法被继承

abstract

类别 特点
抽象类 无法实例化、用于描述一个没有包含足够信息的类
抽象方法 没有方法体、必须存在于抽象类中、子类重写父类时,必须重写父类所有的抽象方法

mian方法

java程序入口,按照以下格式就行

1
2
3
4
5
public class HelloWorld {
    public static void main(String args[]) { 
        System.out.println("Hello World!");
    }
}

构造方法

用来初始化类的一个新的对象,在创建对象(new 运算符)之后自动调用

无参构造方法

1
2
3
Test() {
        m = 0;
    }

有参构造方法

1
2
3
    Test(int m) {
        this.m = m;
    }

定义

1
package 包名;

导入

1
2
example.Test test = new example.Test();
//创建example包里的Test类的对象

用以下语句导入包就不用按照上面的语句调用类

1
2
3
4
5
import 包名+类名;
import example.Test;//导入某包某类
import example.*;   //导入某包所有类

//写了这个语句后,之前

系统包

说明
java.lang Java 的核心类库,包含运行 Java 程序必不可少的系统类,如基本数据类型、基本数学函数、 字符串处理、异常处理和线程类等,系统默认加载这个包
java.io Java 语言的标准输入/输出类库,如基本输入/输出流、文件输入/输出、过滤输入/输出流等
java.util 包含如处理时间的 Date 类,处理动态数组的 Vector 类,
java.awt 构建图形用户界面(GUI)的类库,低级绘图操作 Graphics 类、图形界面组件和布局管理 (如 Checkbox 类、Container 类、LayoutManger 接口等),以及用户界面交互控制和事 件响应(如 Event 类)
java.awt.image 处理和操纵来自网上的图片的 Java 工具类库
java.wat.peer 很少在程序中直接用到,使得同一个 Java 程序在不同的软硬件平台上运行
java.net 实现网络功能的类库有 Socket 类、ServerSocket 类
java.lang.reflect 提供用于反射对象的工具
java.util.zip 实现文件压缩功能
java.awt.datatransfer 处理数据传输的工具类,包括剪贴板、字符串发送器等
java.sql 实现 JDBC 的类库
java.rmi 提供远程连接与载入的支持
java. security 提供安全性方面的有关支持

继承多态

同样是java中的重点

extends继承

1
2
3
4
5
 修饰符 class class_name extends extend_class {
    // 类的主体
}

//表明该类继承extend_class类

类扩展父类之后就可以获得父类的属性和方法,且不改变类成员的访问权限

java单继承,子类只能拥有一个父类

*注:如果在父类中存在有参的构造方法而并没有重载无参的构造方法,那么在子类中必须含有有参的构造方法,因为如果在子类中不含有构造方法,默认会调用父类中无参的构造方法,而在父类中并没有无参的构造方法,因此会出错。

super

super 可以用来访问父类的构造方法、普通方法和属性。

调用构造方法

1
super(parameter-list);

调用成员

1
2
super.member
super.member()

对象类型转型

向上转型

父类引用指向子类对象为向上转型

1
2
3
fatherClass obj = new sonClass();
//Animal cat = new cat();
//Cat cat = new cat();

向下转型

子类对象指向父类引用为向下转型

1
2
3
sonClass obj = (sonClass) fatherClass;
//需要强制类型转化
//Dog dog = (Dog) animal;

重载与重写

重载:同一个类中包含了两个或两个以上方法名相同的方法,但形参列表不同,这种情况被称为方法重载

重写:子类中如果创建了一个与父类中相同名称、相同返回值类型、相同参数列表的方法,只是方法体中的实现不同,以实现不同于父类的功能,这种方式被称为方法重写(override),又称为方法覆盖。

interface

抽象类是从多个类中抽象出来的模板,如果将这种抽象进行的更彻底,则可以提炼出一种更加特殊的“抽象类”——接口

定义接口

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
[public] interface interface_name [extends interface1_name[, interface2_name,]] {
    [public] [static] [final] type constant_name = value; 
    [public] [abstract] returnType method_name(parameter_list);   
}
/*
public:表示接口的修饰符,当没有修饰符时,则使用默认的修饰符,此时该接口的访问权限仅局限于所属的包;

interface_name:表示接口的名称。

extends:表示接口的继承关系;

interface1_name:表示要继承的接口名称;

constant_name:表示变量名称,一般是 static 和 final 型的;

returnType:表示方法的返回值类型;

parameter_list:表示参数列表,在接口中的方法是没有方法体的。
*/

实现接口

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<public> class <class_name> [extends superclass_name] [implements interface1_name[, interface2_name]] {
    // 主体
}
/*
public:类的修饰符;

superclass_name:需要继承的父类名称;

interface1_name:要实现的接口名称。可以实现多个接口
*/

JavaSE小结

java的基础知识还是需要一些时间啃下来的。不过因为专业课学过,有些基础,这里就很简单的过一遍了,接下来学习JavaEE!

JavaEE

JavaEE拥有十三种核心技术,重点关注以下技术

技术 简介
JDBC (Java DataBase Connectivity)Java数据库链接
Servlet 用 Java 编写的服务器端程序,用于互式地浏览和修改数据,生成动态 Web 内容。
JSP (Java ServerPages)一种动态网页技术标准JSP 部署于网络服务器上,可以响应客户端发送的请求,并根据请求内容动态地生成 HTML、XML 或其他格式文档的 Web 网页,然后返回给请求者。

MVC架构

是一种架构模式,强制性的使应用程序的输入、处理和输出分开。使用MVC应用程序被分为三个核心部件:模型、视图、控制器。他们各自处理自己的任务

部件 简介
视图 用户看到并与之交互的界面JSP
模型 表示企业数据(数据模型:dao)和业务规划以及操作(service)
控制器 表示用户的输入并调用模型和视图去完成用户的需求

在java架构模式中,MVC可以抽象为如下结构

层次 解释/可采用技术
View层 UI层,可采用JSP、Structs、SpringMVC等技术
Controller层 控制层,可采用Servlet/Filter、Spring等技术
Service层 核心服务层,向架构上层提供服务
DAO层 数据访问层,可采用JDBC和ORM框架(如Spring JDBC,Hibernate,Mybaits)技术
Moudel层 java底层的一些对象
Utilities层 用JDBC封装的工具类

框架如下

通常Java Web应用可以分为:视图层、控制层、业务逻辑层、数据库访问层,关系如图:

这里简单带过。

JDBC

参考链接:Java中JDBC的超详细总结

*Java数据库链接(Java DataBase Connectivity,JDBC)*是java语言中用于规范客户端程序如何访问数据库的应用程序接口的一个规范。提供了很多方法用于查询、更新数据库等等

JDBC是由一组用Java语言编写的类和接口组成的。提供了一整套接口,允许以一种可移植的访问底层数据库API。*在java.sql.包下

大致作用如下

JDBC驱动程序

JDBC驱动程序在JDBC API中实现定义的接口,用于数据库服务器执行交互

例:可以用JDBC驱动程序发送sql或数据库命令,使Java接受结果来打开数据库链接并交互

分为四种类型:

类型 简介
JDBC-ODBC桥 把JDBC的调用传递给ODBC,然后后者调用数据库本地驱动代码
本地API驱动 本地加载数据库厂商提供的本地代码库访问数据库
网络协议驱动 提供一个网络API,使用Socket调用服务器上的中间件程序,将请求转化为所需的具体API调用
本地协议驱动 (常用) 使用Socket直接在客户端和数据库间通信

JDBC架构

两层架构,了解即可,应用 –> API –> JDBC驱动程序。这个被包含在数据库访问层

API

位于java.sql包与javax.sql包中,主要包括以下接口和类

接口/类 简介
DriverManager类 负责管理数据库驱动程序
Driver接口 处理与数据库服务器之间的通信
Connection接口 负责数据库链接,并与数据库通讯
Statement接口 创捷的对象用于执行SQL语句(过滤不当可能会有Sql注入
PreparedStatement 执行包含动态参数的SQL语句(在服务器端编译)比较安全的的接
ResultSet 保存检索数据的对象
CallableStatement 用于数据库中的存储过程
SQLException 错误

JDBC代码实现

前置条件
所需环境 解决方案
Java环境 我这里用的是burpsuite自带的JDK11.0.11
mysql 我这里用的是phpstudy自带的MYSQL5.7.26
mysql驱动 MYSQL Connector/J 下载5.1.49版本,符合我的mysql版本
具体代码

该代码实现了JDBC链接数据库,执行select * from users语句并返回

 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
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

class jdbcTest1{
    public static void main(String[] args) throws Exception {
        //注册驱动
        Class.forName("com.mysql.jdbc.Driver");
        //获取数据库链接
        Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?useSSL=false","liernian","123456");
        //获取执行者对象
        Statement stat = con.createStatement();
        //执行sql语句并返回结果
        String sql = "select * from users";
        ResultSet res = stat.executeQuery(sql);
        //处理结果
        while(res.next()){
            System.out.println(res.getString("username")+"\t"+res.getString("email"));
        }
        //释放资源
        con.close();
    }
}

/*输出如下
*test1   test1@runoob.com
*test2   test2@runoob.com
*test3   test3@runoob.com
*/

image-20250715130352069

产生的问题与问题解决
出现的问题 解决方案
无数据库无表无数据 用phpstudy便捷创建名为test的数据库
在菜鸟教程中搜到现有的创建数据表和插入数据的语句并使用
未找到驱动产生的报错 在目录下创建lib文件夹,并把驱动的jar文件放入,用IDEA的话需要配置moudle添加ja文件
链接数据库失败 包证mysql的运行,检查数据库名是否正确、数据库用户名与密码是否正确,
查询语句错误 确保test库下的users表中列名存在username与emaii

JDBC小结

JDBC告一段落,最后以实现连接数据库连接作为结尾。本来还有JDBC封装工具类的代码,和JDBC代码使用解析的,不过那些内容有点偏向开发了,权衡再三决定先放下JDBC投入Servlet的学习

Servlet

Servlet是基于Java的动态网站开发技术,主要用于处理用户的请求,执行流程如下

配置

有java环境后,再安装一个Web容器就可以运行Servlet代码,这里选用Tomcat

我用的是bp自带的JDK11.0.11,根据Tomcat官方的版本对应,我选择了10.1.43版本

然后在IDEA新建项目配置Tomcat

新建项目(填好名称选好保存位置和JDK) –> 右键根目录选择添加框架支持 –> 选择Web应用程序(勾选创建web.xml) –> 确定后点击右上角添加配置,再点加号,选择本地的Tomcat –> 配置tomcat的绝对路径,url,端口等 –> 确定后即创建好了容器

大致如下

然后是Servlet环境部署

项目中创建libs目录存放servlet-api.jar(Tomcat的lib目录里有) –> 创建servlet包,存放servlet代码 –> 在包内创建IndexServlet实现Servlet重写方法 –> IndexServlet类中加上@WebServlet("/miracle")注解定义URL访问的路径 –> 重写Servlet类中service,在service中编写动态资源

Servlet简单代码实现

这段代码直接接入了Servlet接口,这样的化Servlet的所有方法就都需要重写一遍(注释中也有提及),一般开发的时候会继承HttpServlet,这样就只需关心doGet或者doPost了。(后续Filter过滤器的实现会采用继承HttpServlet)

 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
package com.web.servlet;

import jakarta.servlet.*;
import jakarta.servlet.annotation.WebServlet;

import java.io.IOException;
import java.io.PrintWriter;

@WebServlet("/miracle")
public class IndexServlet implements Servlet {    //接入Servlet接口
    @Override  //重写Servlet的init(),该方法在启动容器的时候自动调用,方法体为空,并抛出异常至ServletException
    public void init(ServletConfig servletConfig) throws ServletException {  }
    @Override //重写Servlet的getServletConfig(),方法体为返回空
    public ServletConfig getServletConfig(){return null;}
    @Override //重写Servlet的 service(),该方法用于处理用户请求,参数为servlet的请求和返回,方法体逻辑为获取get请求的username参数,若参数值为liuhuaqing,返回可以访问,反之,则返回无法访问。并抛出异常至ServletException()
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        String userName = servletRequest.getParameter("userName"); 
        servletResponse.setContentType("text/html;charset=utf-8"); //避免返回时出现乱码
        PrintWriter writer = servletResponse.getWriter();//导入write对象,用于调用PrintWriter的println方法。
        if ("liuhuaqing".equals(userName)){
            writer.println("可以访问");
        } else {
            writer.println("无法访问");
        }
                    writer.close();
    }
    @Override //重写getServletInfo(),方法体返回空
    public String getServletInfo(){ return null; }
    @Override //重写destroy(),无方法体
    public void destroy(){   }
}

注:鼠标点击并停在那里可以通过ALT+SHIFT+ENTER快捷键快速导入类

运行服务器

Filter过滤器

用于对Servlet容器传给Web资源的request对象和response对象执行检查和修改。

无法被直接访问,也不能生成request对象和response对象,执行提供一些过滤功能

1
2
3
4
1.在Web资源被访问前,检查request对象,修改请求头和请求正文,或对请求执行预处理操作。
2.将请求传递到下一个过滤器或目标资源。
3.在Web资源被访问后,检查response对象,修改响应头和响应正文。
(并不是必须将请求传递到下一个过滤器或目标资源,也可也自行处理,并发送响应给客户端,也可以请求转发或重定向到其他Web资源)

工作流程如下

image-20250716162727969

Filter简单代码实现

因为Filter在Servlet容器内,所以写Filter时也要更改一下Servlet代码,这里我们继承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
package com.web.servlet; // 替换成你自己的包名

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.io.PrintWriter;

/**
 * 注意:我们现在继承HttpServlet,这是一个更方便的抽象类。
 */
@WebServlet("/miracle")
public class IndexServlet extends HttpServlet {

    /**
     * 处理GET和POST请求的通用方法。
     * 因为Filter已经完成了权限检查,所以这里的代码可以假设用户是合法的。
     * Servlet的职责变得更加单一:只为合法用户提供服务。
     */
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 注意:这里的response编码已经在Filter中设置过了,但为了代码健壮性,再设置一次也无妨。
        // response.setContentType("text/html;charset=UTF-8");

        PrintWriter writer = response.getWriter();
        writer.println("<h1>欢迎, " + request.getParameter("userName") + "!</h1>");
        writer.println("<p>你已成功通过Filter的验证,可以访问此核心资源。</p>");
        writer.close();
    }
}

Filter代码

 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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
package com.web.servlet; // 替换成你自己的包名

import jakarta.servlet.*;
import jakarta.servlet.annotation.WebFilter;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * 一个用于身份验证的过滤器
 *
 * @WebFilter注解用于声明这是一个Filter。
 * "urlPatterns"属性指定了这个过滤器要拦截的URL模式。
 * "/miracle"意味着任何访问"/miracle"这个路径的请求都会先被这个Filter拦截。
 */
@WebFilter(urlPatterns = "/miracle")
public class AuthFilter implements Filter { //接入Filter接口

    @Override  //重写init
    public void init(FilterConfig filterConfig) throws ServletException {
        // Filter初始化时调用,通常用于加载一些资源。这里我们暂时不需要。
        System.out.println("AuthFilter 初始化...");
    }

    /**
     * 核心方法:每次请求匹配的URL时,都会执行此方法。
     *
     * @param request  请求对象
     * @param response 响应对象
     * @param chain    过滤器链,这是最重要的部分!
     */
    @Override //重写doFilter
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        System.out.println("AuthFilter 开始工作...");

        // 1. 在请求到达Servlet前进行处理
        // 统一设置编码 (这是一个非常好的实践)
        request.setCharacterEncoding("UTF-8");
        response.setContentType("text/html;charset=UTF-8");

        // 2. 执行安全检查
        String userName = request.getParameter("userName");

        if ("liuhuaqing".equals(userName)) {
            // 用户名正确,放行!
            // 调用chain.doFilter()将请求传递给下一个过滤器或目标Servlet,Filter链可在web.xml中配置
            System.out.println("用户名正确,放行!");
            chain.doFilter(request, response);
        } else {
            // 用户名错误,拦截!
            // 直接向客户端返回错误信息,不调用chain.doFilter()
            System.out.println("用户名错误,拦截!");
            PrintWriter writer = response.getWriter();
            writer.println("<h1>非法访问,禁止入内!</h1>");
            writer.println("<p>你的用户名不正确,无法访问受保护的资源。</p>");
            writer.close();
        }

        // 3. 在Servlet处理完请求,响应返回客户端前,也可以进行处理(这里我们没做)
        System.out.println("AuthFilter 工作结束。");
    }

    @Override
    public void destroy() {
        // Filter销毁时调用。
        System.out.println("AuthFilter 销毁...");
    }
}

服务器运行如下

image-20250716165405228

image-20250716165340330

控制台输出如下

image-20250716165726824

JSP

封装了Servlet(本质就是一个Servlet),作用于网页前端(类似于PHP),可以在html文件里插入java代码

JSP简单代码实现

还是不多花时间在JSP的语法上了,这里找ai写了个语法大全实例。

  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
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
<%--
  Created by IntelliJ IDEA.
  User: 28105
  Date: 2025/7/16
  Time: 14:44
  To change this template use File | Settings | File Templates.
--%>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.util.*,java.text.SimpleDateFormat" %>
<!DOCTYPE html>
<html>
<head>
  <title>JSP语法大全示例</title>
</head>
<body>
<!-- 1. JSP注释(不会输出到页面) -->
<%-- 这是JSP注释,不会被浏览器看到 --%>

<!-- 2. HTML注释(会输出到页面源码) -->
<!-- 这是HTML注释,浏览器可以看到 -->

<!-- 3. JSP脚本元素 -->
<%-- 声明变量 --%>
<%
  String name = "JSP学习者";
  int age = 20;
  List<String> fruits = Arrays.asList("苹果", "香蕉", "橙子");
  request.setAttribute("city", "北京");
  session.setAttribute("user", "Tom");
  application.setAttribute("appName", "JSP示例应用");
%>

<%-- 4. JSP表达式(输出内容到页面) --%>
<h2>欢迎,<%= name %>!</h2>
<p>你的年龄是:<%= age %></p>

<%-- 5. JSP声明(定义方法或变量) --%>
<%!
  public String getCurrentTime() {
    return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
  }
%>
<p>当前时间:<%= getCurrentTime() %></p>

<%-- 6. JSP指令(已在顶部使用) --%>
<%-- page、include、taglib --%>

<%-- 7. JSP动作元素 --%>
<jsp:useBean id="person" class="java.util.HashMap" scope="page" />
<%
  ((Map)pageContext.getAttribute("person")).put("gender", "男");
%>


<%-- 8. include指令和动作 --%>
<%-- 静态包含 --%>
<%-- <%@ include file="header.jsp" %> --%>
<%-- 动态包含 --%>
<%-- <jsp:include page="footer.jsp" /> --%>

<%-- 9. JSP内置对象 --%>
<p>request city: <%= request.getAttribute("city") %></p>
<p>session user: <%= session.getAttribute("user") %></p>
<p>application appName: <%= application.getAttribute("appName") %></p>
<p>contextPath: <%= request.getContextPath() %></p>
<p>服务器IP: <%= request.getLocalAddr() %></p>

<%-- 10. JSP条件语句和循环 --%>
<h3>喜欢的水果:</h3>
<ul>
  <% for(String fruit : fruits) { %>
  <li><%= fruit %></li>
  <% } %>
</ul>

<%-- 11. JSP异常处理 --%>
<%
  try {
    int result = 10 / 2;
    out.println("10 / 2 = " + result);
  } catch(Exception e) {
    out.println("发生异常:" + e.getMessage());
  }
%>

<%-- 13. EL表达式 --%>
<h3>EL表达式演示:</h3>
<p>城市:${city}</p>
<p>用户:${sessionScope.user}</p>
<p>应用名:${applicationScope.appName}</p>
<p>年龄:${age}</p>
<p>喜欢的水果第一个:${fruits[0]}</p>

<%-- 14. 表单与请求参数 --%>
<form method="post">
  请输入你的爱好:<input type="text" name="hobby" />
  <input type="submit" value="提交" />
</form>
<%
  String hobby = request.getParameter("hobby");
  if(hobby != null && !hobby.isEmpty()) {
%>
<p>你的爱好是:<%= hobby %></p>
<% } %>

<%-- 15. 跳转与重定向 --%>
<%--
<%
    // response.sendRedirect("https://www.baidu.com");
    // pageContext.forward("other.jsp");
%>
--%>

<%-- 16. JSP页面指令 errorPage 和 isErrorPage --%>
<%--
<%@ page errorPage="error.jsp" %>
--%>

<hr>
<p style="color:gray;">本页面演示了JSP的常用语法,建议结合源码和运行结果一起学习。</p>
</body>
</html>

运行结果

image-20250716175001006

总结

现在是7.16,用了五天时间简单快速过了一遍基础,具体还有很多细节是不清楚得,不过那些东西都可以在后续的学习中补上。之后会按照《B站最全的Java安全学习路线》这个视频中讲的路线或者零溢出师傅的教学路线(如下图)和Hello java Sec和关于具体的漏洞代码去学习java。

3e2982559f738ad158783e7197f3154b

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计