1.form表单在网页中主要负责数据采集功能,一个表单有三个基本组成部分:
①表单标签:这里面包含了处理表单数据所用CGI程序的URL以及数据提交到服务器的方法。
<form></form>
功能:用于申明表单,定义采集数据的范围,也就是和里面包含的数据将被提交到服务器或者电子邮件里。
②表单域:包含了文本框、密码框、隐藏域、多行文本框、复选框、单选框、下拉选择框和文件上传框等。
③表单按钮:包括提交按钮、复位按钮和一般按钮;用于将数据传送到服务器上的CGI脚本或者取消输入,还可以用表单按钮来控制其他定义了处理脚本的处理工作。
表单相关内容:https://baike.so.com/doc/6669871-6883711.html
enctype这个属性管理的是表单的MIME(Multipurpose Internet Mail Extensions)编码,
共有三个值可选:
1、application/x-www-form-urlencoded ---默认值,
作用是设置表单传输的编码,不能用于上传文件。
eg: AJAX中xmlHttp.setRequestHeader("Content-Type","application/x-www-form- urlencoded"),不写会报错的
form表单里是可以不写enctype=application/x-www-form-urlencoded,
因为默认HTML表单就是这种传输编码类型
2、multipart/form-data ---制定传输数据的特殊类型,上传的非文本的内容,
比如图片或是是mp3。
eg:<form method="post" enctype="multipart/form-data" action="/upload/file.do">
3、text/plain ---纯文本传输,不能用于上传文件。
MIME: 多功能Internet邮件扩充服务,它是一种多用途网际邮件扩充协议,
在1992年最早应用于电子邮件系统,但后来也应用到浏览器。服务器会将它们
发送的多媒体数据的类型告诉浏览器,而通知手段就是说明该多媒体数据的MIME
类型,从而让浏览器知道接收到的信息哪些是MP3文件,哪些是Shockwave文件等,
服务器将 MIME标志符放入传送的数据中来告诉浏览器使用哪种插件读取相关文件。
例:用form表单写一个密码框:
//我在程序中将其写在web文档下的hello.html
<div>
<form action="login.do" method="post">
<!--<form> 用于为用户输入创建HTML表单(同步提交),其代表的意思是将form中包含name属性的标签的value值作为将要发送到服务器上的一个属性的整合标签-->
用户名:<input type="text" name="username"><br>
<!--input代表一种页面输入行为,其中属性type描述了这个输入行为是以一种什么样的展示形式来获得用户输入-->
<!--name属性一般来说没有任何作用,它的作用是在准备提交数据的时候表示这个数据的key值-->
密码:<input type="password" name="pwd"><br>
<input type="submit" value="登录">
</form>
</div>
按登录跳转至登录成功或登录失败页面:
@WebServlet("/login.do")
public class LoginController extends HttpServlet{
IUserService service = new UserService();
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("utf-8");
resp.setContentType("text/html; charset=utf-8");
String username = req.getParameter("username");
String pwd = req.getParameter("pwd");
boolean flag = service.login(username,pwd);
if(flag){
resp.getWriter().println("登录成功");
}else{
resp.getWriter().println("登录失败");
}
}
}
用户登录直接转到success.html (重定向):
将上面的login代码中的 resp.getWriter().println("登录成功");
改为resp.sendRedirect("success.html?username=" + username);
(重定向)
//将其写在success.html中
<body>
欢迎回来: <span id="username"></span>
</body>
<script type = "text/ javascript">
//JS是弱类型语言,获得浏览器地址的语句是
var u = window. location.href ;
var result = u. split("?")[1].split("=")[1];
//向目标点加入内容
document . getElementById("username" ). innerText = result;
</script>
在控制台查询相关内容:
以上也是后端向前端传送数据的一种形式,但是存在较大风险(可在前端直接改数据),容易泄露敏感数据。
所以引入cookie,让前端把一些临时数据先记录在隐藏的位置。
2.Cookie: (小饼干,HTTP定义的一种由客户端(浏览器)保存临时数据的形式)
(小饼干处理内容的过程有两种,一种是后台 (JAVA) ,一种是前端(JS) )
Cookie,有时也用其复数形式Cookies,指某些网站为了辨别用户身份、进行session跟踪而储存在用户本地终端上的数据(通常经过加密)。
Cookie保存内容的形式是K-V对,一般是用K=V, K=V形式来保存内容的。
每次浏览器发出请求的时候,都会将自己的Cookie发送给后台,有多少发多少。
后台如果想要设置让前端保存什么内容的话,会通过Set-Cookie头来通知前端保存内容。
Cookie是由服务器端生成,发送给User-Agent(一般是浏览器),浏览器会将Cookie的key/value保存到某个目录下的文本文件内,下次请求同一网站时就发送该Cookie给服务器(前提是浏览器设置为启用cookie)。Cookie名称和值可以由服务器端开发自己定义,对于JSP而言也可以直接写入jsessionid,这样服务器可以知道该用户是否合法用户以及是否需要重新登录等。
cookie相关内容:https://baike.so.com/doc/3766187-3956334.html
后台给前端加cookie:(在上述logincontroller程序中继续改)
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("utf-8");
resp.setContentType("text/html; charset=utf-8");
//从request中读取cookie,向response中写入cookie
String username = req.getParameter("username");
String pwd = req.getParameter("pwd");
User user = service.login(username,pwd);
if(user!=null){
//后台给前端加cookie
Cookie cookie = new Cookie("username",username);
resp.addCookie(cookie);
resp.sendRedirect("success.html");
}else{
resp.getWriter().println("登录失败");
}
}
}
在前端读到cookie:
前端获得Cookie内容的过程:
<body>
欢迎回来:<span id="username"></span>
</body>
<!--前端获得Cookie内容的过程 -->
<script type = "text/javascript">
var u = document.cookie.split("; ");
for(var i=0; i<u.length; i++){
if(u[i].startsWith("username")){
document.getElementById("username").innerText = u[i].split("=")[1];
}
}
</script>
</html>
cookie.path规则:
后台接收前端发来的cookie:
@WebServlet("/Test.do")
public class TestController extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//后台接收前端发来的cookie
resp.setContentType("text/html; charset=utf-8");
Cookie[] cookies = req.getCookies();
for (Cookie c : cookies) {
System.out.println(c.getName() + ":" + c.getValue());
//判断用户是否登陆成功 如果登录成功显示xxx登陆成功;没成功直接再跳转到登录页面
resp.setContentType("text/html; charset=utf-8");
Cookie[] cookies = req.getCookies();
String username = "";
for (Cookie c : cookies) {
if (c.getName().equals("username")) {
username = c.getValue();
}
}
if(!username.isEmpty){
resp.getWriter().println(Username() + "登陆成功");
}else{
resp.sendRedirect("hello.html");
}
}
错误地使用Cookie,过分依赖Cookie---->CSRF攻击
(CSRF(Cross-site request forgery)跨站请求伪造,也被称为"One Click Attack"或者Session Riding,通常缩写为CSRF或者XSRF,是一种对网站的恶意利用。尽管听起来像跨站脚本(XSS),但它与XSS非常不同,XSS利用站点内的信任用户,而CSRF则通过伪装来自受信任用户的请求来利用受信任的网站。与XSS攻击相比,CSRF攻击往往不大流行(因此对其进行防范的资源也相当稀少)和难以防范,所以被认为比XSS更具危险性。)
HTTP是一种无状态的协议,也就是说HTTP是不能记录用户状态的。
什么是无状态: 所谓的无状态,就是说同一个浏览器第一次和第二次发出的请求,服务器是没有办法分辨的。但是我们知道HTTP是基于TCP实现的,为什么会出现无状态呢?因为TCP是有状态的啊,比如连接建立以后,这个连接就是一种状态啊,如果登陆成功了,也就是说这个连接都登陆成功了
为什么HTTP是无状态的: 因为HTTP是短连接,HTTP基于TCP实现不假,但是HTTP的玩法是连接数据传输,断开,这个时候TCP已经断了,所以下一次HTTP是一个新的TCP请求,所以我们说HTTP是无状态的。
但是我们有时候确实需要HTTP协议在前端记录一些内容,来模拟HTTP的状态,这个时候,我们就可以使用一种叫做Cookie的东西来实现这个过程。
但是我们发现一个问题,Cookie这种保存在前端的内容对于后台来说其实是不可信的。 为啥呢? 因为任何稍微懂一点前端的"程序员”都可以改变这个Cookie的值,所以如果我们将一些很敏感并且很重要的信息保存在Cookie里面的话,就会对后台产生很大的业务影响。
所以一般来说Cookie中记录的值,都是一些无关紧要,或者不容易被修改的值。
3.为了解决HTTP的无状态特性,我们引入了Cookie;同时为了解决Cookie的不可信问题,我们引入了另一个关键信息,叫做Session(会话控制)。
Session: 一般来说是基于Cookie来做的, 特殊情况下,我们会加入4种Session的追踪方式。啥意思?
Cookie的缺点就是写在了前端,里面记录的K-V对是不可信的。错不在Cookie,在前端。也就是说,如果我们把内容保存在后台。妥了! 那么前端就看不见那么多敏感信息了,也就改不了这些信息了。
Session就是保存在后台的一种K-V对的存储结构但是其保存过程又要和前端保持一致。所以我们设计Session的时候,将其保存的内容格式设计成 <Y,<K,V>>
也就是说,最外层的Y是一个ID值,它将被写在Cookie中,它的值是一个32位的16进制数字(200年不重复)发给前端。这个Y对应了另一个Map结构,这个Map里面保存着持有Y的ID的前端所记录的所有状态。
Session 对象存储特定用户会话所需的属性及配置信息。这样,当用户在应用程序的 Web 页之间跳转时,存储在 Session 对象中的变量将不会丢失,而是在整个用户会话中一直存在下去。当用户请求来自应用程序的 Web 页时,如果该用户还没有会话,则 Web 服务器将自动创建一个 Session 对象。当会话过期或被放弃后,服务器将终止该会话。Session 对象最常见的一个用法就是存储用户的首选项。例如,如果用户指明不喜欢查看图形,就可以将该信息存储在 Session 对象中。有关使用注意会话状态仅在支持 cookie 的浏览器中保留。
session相关内容:https://baike.so.com/doc/2366283-2502104.html
@WebServlet("/Test.do")
public class TestController extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//后台给前端加session
resp.setContentType("text/html; charset=utf-8");
HttpSession session = req.getSession();
User user = (User) session.getAttribute("username");
if(user != null){
resp.getWriter().println(user.getUsername() + "登陆成功");
}else{
resp.sendRedirect("hello.html");
}
}
}
@WebServlet("/login.do")
public class LoginController extends HttpServlet{
IUserService service = new UserService();
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("utf-8");
resp.setContentType("text/html; charset=utf-8");
String username = req.getParameter("username");
String pwd = req.getParameter("pwd");
User user = service.login(username,pwd);
if(user!=null){
//后台给前端加session
Cookie cookie = new Cookie("username",username);
resp.addCookie(cookie);
HttpSession session = req.getSession();
//setAttribute() 方法添加指定的属性,并为其赋指定的值。如果这个指定的属性已存在,则仅设置/更改值。
session.setAttribute("username",user);
resp.sendRedirect("success.html");
}else{
resp.getWriter().println("登录失败");
//session.invalidate(); 失效
}
}
}
cookie和session对于HTTP有什么用?
HTTP协议本身是无法判断用户身份,所以需要cookie或者session。
什么是cookie?
cookie是由Web服务器保存在用户浏览器上的文件(key-value格式),可以包含用户相关的信息。客户端向服务器发起请求,就提取浏览器中的用户信息由http发送给服务器。
什么是session?
session 是浏览器和服务器会话过程中,服务器会分配的一块储存空间给session。
服务器默认为客户浏览器的cookie中设置 session id,这个session id就和cookie对应,浏览器在向服务器请求过程中传输的cookie 包含 session id ,服务器根据传输cookie 中的 session id 获取出会话中存储的信息,然后确定会话的身份信息。
cookie与session的区别:
①Cookie数据存放在客户机,而session存放在服务器;
②Cookie并不安全,容易被修改,session则安全性相对更高;
③ 单个cookie保存的数据不超过4k,一个站点最多保存20条,而session可存放很多;
④Cookie对于性能损耗比较低,而session会占用更多的服务器性能。
session一定时间内保存在服务器上,当访问增多,占用服务器性能,考虑到服务器性能方面,应当使用cookie。
session和cookie作用原理、区别:https://www.cnblogs.com/yunian/articles/5736066.html
4.filter(过滤器): Java中的Filter 并不是一个标准的Servlet ,它不能处理用户请求,也不能对客户端生成响应。 主要用于对HttpServletRequest 进行预处理,也可以对HttpServletResponse 进行后处理,是个典型的处理链。一般是用来决定Servlet要不要执行,怎么执行。
基本工作原理:
①Filter 程序是一个实现了特殊接口的 Java 类,与 Servlet 类似,也是由 Servlet 容器进行调用和执行的。
②当在 web.xml 注册了一个 Filter 来对某个 Servlet 程序进行拦截处理时,它可以决定是否将请求继续传递给 Servlet 程序,以及对请求和响应消息是否进行修改。
③当 Servlet 容器开始调用某个 Servlet 程序时,如果发现已经注册了一个 Filter 程序来对该 Servlet 进行拦截,那么容器不再直接调用 Servlet 的 service 方法,而是调用 Filter 的 doFilter 方法,再由 doFilter 方法决定是否去激活 service 方法。
④但在 Filter.doFilter 方法中不能直接调用 Servlet 的 service 方法,而是调用 FilterChain.doFilter 方法来激活目标 Servlet 的 service 方法,FilterChain 对象是通过 Filter.doFilter 方法的参数传递进来的。
⑤只要在 Filter.doFilter 方法中调用 FilterChain.doFilter 方法的语句前后增加某些程序代码,这样就可以在 Servlet 进行响应前后实现某些特殊功能。
⑥如果在 Filter.doFilter 方法中没有调用 FilterChain.doFilter 方法,则目标 Servlet 的 service 方法不会被执行,这样通过 Filter 就可以阻止某些非法的访问请求。
优点: 过滤链的好处是,执行过程中任何时候都可以打断,只要不执行chain.doFilter()就不会再执行后面的过滤器和请求的内容。而在实际使用时,就要特别注意过滤链的执行顺序问题。
三个方法:
① void setFilterConfig(FilterConfig config) //设置filter 的配置对象;
②FilterConfig getFilterConfig() //返回filter的配置对象;
③void doFilter(ServletRequest req,ServletResponse res,FilterChain chain) //执行filter 的工作;
注意: 现setFilterConfig和getFilterConfig方法已取消,代之为init(FilterConfig config)和destory()方法。
每一个filter从doFilter()方法中得到当前的request及response。在这个方法里,可以进行任何的针对request及response的操作(包括收集数据,包装数据等)。filter调用chain.doFilter()方法把控制权交给下一个filter,一个filter在doFilter()方法中结束。如果一个filter想停止request处理而获得对response的完全的控制,那它可以不调用下一个filter。
FilterChain(过滤器链):
①在一个 Web 应用程序中可以注册多个 Filter 程序,每个 Filter 程序都可以对一个或多个 Servlet 程序进行拦截。如果有多个 Filter 程序都可以对某个 Servlet 程序的访问过程进行拦截,当针对该 Servlet 的访问请求到达时,Web 容器将把这多个 Filter 程序组合成一个 Filter 链(也叫过滤器链)。
②Filter 链中的各个 Filter 的拦截顺序与它们在 web.xml 文件中的映射顺序一致,上一个 Filter.doFilter 方法中调用 FilterChain.doFilter 方法将激活下一个 Filter的doFilter 方法,最后一个 Filter.doFilter 方法中调用的 FilterChain.doFilter 方法将激活目标 Servlet的service 方法。
③只要 Filter 链中任意一个 Filter 没有调用 FilterChain.doFilter 方法,则目标 Servlet 的 service 方法都不会被执行。
FilterConfig接口: FilterConfig对象提供对servlet环境及web.xml文件中指派的过滤器名的访问。(Servlet 规范将代表 ServletContext 对象和 Filter 的配置参数信息都封装到一个称为 FilterConfig 的对象中。)
FilterConfig 接口则用于定义 FilterConfig 对象应该对外提供的方法,以便在 Filter 程序中可以调用这些方法来获取 ServletContext 对象,以及获取在 web.xml 文件中为 Filter 设置的友好名称和初始化参数。
FilterConfig接口定义的各个方法:
getFilterName 方法,返回 <filter-name>
元素的设置值。
getServletContext 方法,返回 FilterConfig 对象中所包装的 ServletContext 对象的引用。
getInitParameter 方法,用于返回在 web.xml 文件中为 Filter 所设置的某个名称的初始化的参数值。
getInitParameterNames 方法,返回一个 Enumeration 集合对象。
Filter 的注册与映射:
注册Filter : 一个 <filter>
元素用于注册一个 Filter。其中,<filter-name>
元素是必需的,<filter-class>
元素也是必需的,<init-param>
元素是可选的,可以有多个 < init-param> 元素。( init-param 的param-name 就是参数名 param-value就是参数值, 支持多个参数)
<filter>
<filter-name>loginfilter</filter-name>
<filter-class>edu.yau.test.controller.filter.LoginFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
映射 Filter: <filter-mapping>
元素用于设置一个 Filter 所负责拦截的资源。一个 Filter 拦截的资源可以通过两种方式来指定:资源的访问请求路径和 Servlet 名称。
<filter-mapping>
<filter-name>loginFilter</filter-name>
<url-pattern>*.do</url-pattern>
</filter-mapping>
<url-pattern>
元素中的访问路径的设置方式遵循 Servlet 的 URL 映射规范。
/*
:表示拦截所有的访问请求。
/filter/*
:表示拦截 filter 目录下的所有访问请求,如http://localhost:8888/testFilter_001/filter/xxxxxx 。
/test.html
:表示拦截根目录下以 test.html 为资源名的访问请求,访问链接只会是:http://localhost:8888/test.html。
用户登录验证过滤器(LoginFilter):
@WebFilter("*.do")
//一个 Filter 程序就是一个 Java 类,这个类必须实现 Filter 接口。
//javax.servlet.Filter 接口中定义了三个方法:init、doFilter、destory。
public class LoginFilter implements Filter {
Set<String> ignore = new HashSet<>();
//Web 容器创建 Filter 的实例对象后,将立即调用该 Filter 对象的 init 方法。
//init 方法在 Filter 生命周期中仅被执行一次,Web 容器在调用 init 方法时,
//会传递一个包含 Filter 的配置和运行环境信息的 FilterConfig 对象。
@Override
public void init(FilterConfig filterConfig) throws ServletException {
//不过滤
ignore.add("/web/login.do");
ignore.add("/web/register.do");
}
//当一个 Filter 对象能够拦截访问请求时,Servlet 容器将调用 Filter 对象的 doFilter 方法。
//其中,参数 request 和 response 为 Web 容器或 Filter 链中上一个 Filter
//传递过来的请求和响应对象;参数 chain 为代表当前 Filter 链的对象。
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("过滤器执行");
HttpServletRequest request = (HttpServletRequest) servletRequest;
// 获得session
HttpSession session = request.getSession();
// 从session中获取SessionKey对应值,若值不存在,则重定向到redirectUrl
Object obj = session.getAttribute("username");
HttpServletResponse response = (HttpServletResponse) servletResponse;
// String url = request.getRequestURL().toString();
String uri = request.getRequestURI().toString();
// System.out.println(url);
// System.out.println(uri);
if(obj == null && !ignore.contains(uri)){
//不放过内容
response.sendRedirect("hello.html");
}else{
//放过内容
filterChain.doFilter(request,response);
}
System.out.println("过滤器执行结束");
}
//该方法在 Web 容器卸载 Filter 对象之前被调用,也仅执行一次。
//可以完成与 init 方法相反的功能,释放被该 Filter 对象打开的资源,
@Override
public void destroy() {
}
}
web项目乱码过滤器(CharectorFilter):
@WebFilter("*.do")
public class CharectorFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) servletResponse;
response.setContentType("text/html; charset=utf8");
HttpServletRequest request = (HttpServletRequest) servletRequest;
request.setCharacterEncoding("utf8");
filterChain.doFilter(request,response);
}
@Override
public void destroy() {
}
}