言成言成啊 | Kit Chen's Blog

会话技术

发布于2020-06-23 09:12:05,更新于2023-10-29 10:24:53,标签:http web jsp  文章会持续修订,转载请注明来源地址:https://meethigher.top/blog

之前写得一篇cookie与session区别

一、会话技术

会话:一次会话中包含多次请求和响应。

一次会话:浏览器第一次给服务器资源发送请求,会话建立,直到有一方断开为止。

功能:在一次会话的范围内的多次请求间,共享数据

方式

  1. 客户端会话技术:cookie
  2. 服务器端会话技术:session

有关http协议的知识,移步到这里

二、Cookie

概念:客户端会话技术,将数据保存到客户端

2.1 快速入门

快速入门

  1. 创建cookie对象,绑定数据
    • Cookie(String name, String value)
  2. 发送cookie
    • response.addCookie(Cookie cookie)
  3. 获取cookie,拿到数据
    • Cookie[] request.getCookies()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@WebServlet("/demo01Cookie")
public class Demo01Cookie extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req,resp);
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//创建cookie对象
Cookie cookie=new Cookie("msg","hello");
//发送cookie
resp.addCookie(cookie);

}
}

上面这串代码,在访问该网址时,服务器返回一串cookie,”msg=hello”。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@WebServlet("/demo02Cookie")
public class Demo02Cookie extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request,response);
}

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取cookie
Cookie[] cookies = request.getCookies();
//遍历cookies
if(cookies!=null){
for(Cookie c:cookies){
String name=c.getName();
String value=c.getValue();
System.out.println(name+"=="+value);
}
}
}
}

上面这些代码,是将浏览器端请求服务器时携带的cookie打印到控制台。

如果想要在浏览器查看cookie的话

2.2 实现原理

浏览器初次请求服务器,服务器响应放回set-cookie,浏览器将接收到的cookie保存到本地。

浏览器二次请求服务器,请求头中携带cookie的值。

就相当于买东西

Cookie 就像是在超级市场买东西拿到的小票,由超市(Server)发给消费者(Browser),超市方面不用记住每一个消费者的脸,但是他们认识消费者手里的小票(Cookie),可以通过小票知道消费者之前的一些消费信息(在服务端产生的数据)。

2.3 Cookie细节

  1. 一次可以发送多个Cookie

    • 可以创建多个对象,使用response调用多次addCookie方法发送cookie即可
  2. Cookie在浏览器中保存时间

    • 默认情况下,当浏览器关闭后,Cookie数据被销毁
    • 设置Cookie生命周期:持久化存储
      • setMaxAge(int seconds)
        • 正数:将Cookie数据写到硬盘的文件中,持久化存储。正数代表cookie的存活时间。比如30,在30s之后,这个Cookie文件将被删除掉
        • 负数:默认值,存储在内存中,浏览器关闭,就销毁
        • 零:删除Cookie信息
  3. Cookie存储中文

    • Tomcat8之前,Cookie中不能直接存储中文数据。需要将中文数据转码,即url编码(%E3,即%跟两个十六进制的数字表示一个字节,有多少个字节,就会有多少%)
    • Tomcat8之后,Cookie支持中文数据,但是特殊字符还是不支持,比方说空格。需要用url编码
  4. Cookie共享问题

    • 默认同一服务器不同项目,是不会共享Cookie的
      • setPath(String path):设置cookie的获取范围。默认情况下,会去设置当前的虚拟目录
    • 不同服务器实现cookie共享
      • setDomain(String path):设置一级域名相同,那么多个服务器之间cookie可以共享
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@WebServlet("/demo04Cookie")
public class Demo04Cookie extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request,response);
}

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//创建Cookie
Cookie ck1 = new Cookie("msg", "setMaxAge");
//设置Cookie的存活时间
// ck1.setMaxAge(100);
// ck1.setMaxAge(-1);//默认值,会话关闭之后即消失
//删除当前cookie
//ck1.setMaxAge(0);

Cookie ck2 = new Cookie("hh", "奥利给");

ck2.setPath("/demo04Cookie");
//发送cookie
response.addCookie(ck1);
response.addCookie(ck2);
}
}

2.4 Cookie的特点和作用

特点

  1. Cookie存储数据在客户端浏览器
  2. 浏览器对于单个Cookie的大小有限制(4KB左右,不同浏览器不同),以及同一个域名下的总Cookie数量也有限制(20个,不同浏览器不同)

作用

  1. Cookie一般用于存储少量的、不敏感的数据
  2. 在不登录的情况下,完成服务器对客户端的身份识别(比方说百度在不登录的情况下,存储的一些个性化设置,当然这个也是可以通过本地存储来实现)

localStorage与Cookie的异同

2.5 案例-记录访问

需求

记住上一次访问时间

如果是初次访问,提示你好,欢迎首次访问

如果是二次访问,提示你好,欢迎回来,上次访问时间:xxx

分析:判断是否有lastTime该cookie,如果没有,则是初次访问。访问时,服务端将访问时间存储到lastTime的cookie中

实现

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
@WebServlet("/cookieTest")
public class CookieTest extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//设置响应体的消息体的数据格式以及编码
response.setContentType("text/html;charset=utf-8");

//设置有没有lastTime这个cookie标记
boolean flag=false;

//获取当前时间
String time = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss").format(new Date());
//直接存储time,会出错,因为有个空格。当然删掉是可以的,不过最好的办法是url编码
String time_en = URLEncoder.encode(time, StandardCharsets.UTF_8);
//获取cookie
Cookie[] cks = request.getCookies();
//二次访问
if(cks!=null&&cks.length>0){
for(Cookie ck:cks){
String name=ck.getName();
if("lastTime".equals(name)){
flag=true;

//响应数据
String value= URLDecoder.decode(ck.getValue(),"utf-8");
response.getWriter().write("<h1>欢迎回来,您上次访问时间"+value+"</h1>");
//重新设置cookie值
ck.setValue(time_en);
ck.setMaxAge(60*60*24*30);//cookie存储一个月
response.addCookie(ck);
break;
}
}
}
//初次访问
if(cks==null||cks.length==0|| !flag){
//响应数据
response.getWriter().write("<h1>您好,这是你的首次访问</h1>");
//设置cookie
Cookie lt = new Cookie("lastTime", time_en);
lt.setMaxAge(60*60*24*30);
response.addCookie(lt);
}
}
}

注意cookie存储中特殊字符的编解码即可

三、JSP

3.1 概念

JSP:Java Server Pages,java服务端页面

本质:一个特殊页面,既可以定义html标签,又可以定义java代码。本质就是一个Servlet

作用:简化Servlet书写

3.2 原理

  1. 客户端请求.jsp,服务端判断有无.jsp。无则404
  2. 找到.jsp,会将.jsp转换为.java文件
  3. 编译.java文件,生成.class字节码文件
  4. 由字节码文件提供访问

我们在CATALINA_BASE的路径下,能够找到集成到idea上面Tomcat的路径,在conf下面,能够找到服务器访问的源文件。像servlet生成的源码放到了WEB-INF的classes下面

.jsp文件经过编译之后,存储在CATALINA_BASE路径下的work中

可以看出,编译后.java文件继承了Tomcat中的HttpJspBase.java,我们进入Tomcat找到该类

HttpJspBase继承了HttpServlet,故jsp的本质就是一个Servlet

3.3 jsp脚本

jsp脚本:jsp定义Java代码的方式

  1. <% 代码 %>:定义在jsp转换后的java类(Servlet)的service方法中。service方法中可以定义什么,该脚本中就可以定义什么。
  2. <%! 代码 %>:定义在jsp转换后的java类(Servlet)的成员位置。(不过尽量不要再Servlet中定义成员变量,会引发线程安全问题)
  3. <%= 代码 %>:定义在jsp转换后的java类(Servlet)的service方法中。会输出在网页页面上,输出语句能输出啥,该脚本中就能定义啥
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>
</head>
<body>
<% System.out.println("Hello JSP"); %>
<%!
int i = 3;
%>
<%= i %>
<%= "我打你妈的" %>
<h1>Hello JSP</h1>
</body>
</html>

以上面这个例子为例,编译生成的index_jsp.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
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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
public final class index_jsp extends org.apache.jasper.runtime.HttpJspBase
implements org.apache.jasper.runtime.JspSourceDependent,
org.apache.jasper.runtime.JspSourceImports {


int i=3;

private static final javax.servlet.jsp.JspFactory _jspxFactory =
javax.servlet.jsp.JspFactory.getDefaultFactory();

private static java.util.Map<java.lang.String,java.lang.Long> _jspx_dependants;

private static final java.util.Set<java.lang.String> _jspx_imports_packages;

private static final java.util.Set<java.lang.String> _jspx_imports_classes;

static {
_jspx_imports_packages = new java.util.HashSet<>();
_jspx_imports_packages.add("javax.servlet");
_jspx_imports_packages.add("javax.servlet.http");
_jspx_imports_packages.add("javax.servlet.jsp");
_jspx_imports_classes = null;
}

private volatile javax.el.ExpressionFactory _el_expressionfactory;
private volatile org.apache.tomcat.InstanceManager _jsp_instancemanager;

public java.util.Map<java.lang.String,java.lang.Long> getDependants() {
return _jspx_dependants;
}

public java.util.Set<java.lang.String> getPackageImports() {
return _jspx_imports_packages;
}

public java.util.Set<java.lang.String> getClassImports() {
return _jspx_imports_classes;
}

public javax.el.ExpressionFactory _jsp_getExpressionFactory() {
if (_el_expressionfactory == null) {
synchronized (this) {
if (_el_expressionfactory == null) {
_el_expressionfactory = _jspxFactory.getJspApplicationContext(getServletConfig().getServletContext()).getExpressionFactory();
}
}
}
return _el_expressionfactory;
}

public org.apache.tomcat.InstanceManager _jsp_getInstanceManager() {
if (_jsp_instancemanager == null) {
synchronized (this) {
if (_jsp_instancemanager == null) {
_jsp_instancemanager = org.apache.jasper.runtime.InstanceManagerFactory.getInstanceManager(getServletConfig());
}
}
}
return _jsp_instancemanager;
}

public void _jspInit() {
}

public void _jspDestroy() {
}

public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response)
throws java.io.IOException, javax.servlet.ServletException {

if (!javax.servlet.DispatcherType.ERROR.equals(request.getDispatcherType())) {
final java.lang.String _jspx_method = request.getMethod();
if ("OPTIONS".equals(_jspx_method)) {
response.setHeader("Allow","GET, HEAD, POST, OPTIONS");
return;
}
if (!"GET".equals(_jspx_method) && !"POST".equals(_jspx_method) && !"HEAD".equals(_jspx_method)) {
response.setHeader("Allow","GET, HEAD, POST, OPTIONS");
response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, "JSP 只允许 GET、POST 或 HEAD。Jasper 还允许 OPTIONS");
return;
}
}

final javax.servlet.jsp.PageContext pageContext;
javax.servlet.http.HttpSession session = null;
final javax.servlet.ServletContext application;
final javax.servlet.ServletConfig config;
javax.servlet.jsp.JspWriter out = null;
final java.lang.Object page = this;
javax.servlet.jsp.JspWriter _jspx_out = null;
javax.servlet.jsp.PageContext _jspx_page_context = null;


try {
response.setContentType("text/html;charset=UTF-8");
pageContext = _jspxFactory.getPageContext(this, request, response,
null, true, 8192, true);
_jspx_page_context = pageContext;
application = pageContext.getServletContext();
config = pageContext.getServletConfig();
session = pageContext.getSession();
out = pageContext.getOut();
_jspx_out = out;

out.write("\n");
out.write("\n");
out.write("<html>\n");
out.write(" <head>\n");
out.write(" <title>$Title$</title>\n");
out.write(" </head>\n");
out.write(" <body>\n");
out.write(" ");
System.out.println("Hello JSP");
out.write('\n');
out.write(' ');
out.write(' ');
out.write('\n');
out.write(' ');
out.write(' ');
out.print( i );
out.write('\n');
out.write(' ');
out.write(' ');
out.print( "我打你妈的" );
out.write("\n");
out.write(" <h1>Hello JSP</h1>\n");
out.write(" </body>\n");
out.write("</html>\n");
} catch (java.lang.Throwable t) {
if (!(t instanceof javax.servlet.jsp.SkipPageException)){
out = _jspx_out;
if (out != null && out.getBufferSize() != 0)
try {
if (response.isCommitted()) {
out.flush();
} else {
out.clearBuffer();
}
} catch (java.io.IOException e) {}
if (_jspx_page_context != null) _jspx_page_context.handlePageException(t);
else throw new ServletException(t);
}
} finally {
_jspxFactory.releasePageContext(_jspx_page_context);
}
}
}

3.4 jsp内置对象

内置对象概念:在jsp页面中不需要获取和创建,可以直接使用的对象。

因为jsp编译后就是一个servlet类,其中某些代码直接放到了service方法中,所以,像request这种的,是在Servlet中已经定义好的,就可以直接使用。叫做内置对象。

具体可以参照上面jsp生成的java代码

jsp一共有9个内置对象(本次介绍三个,详细看下篇博客)

  • request
  • response
  • out:字符输出流对象,可以将数据输出到页面上。类似于response.getWriter()
    • out.write():按代码顺序输出
    • response.getWriter().write():直接先输出。在jsp中尽量不要用这个。

在Tomcat服务器做出响应之前,会先找response.getWriter()缓冲区的内容,再找out缓冲区中的内容,故response.getWriter().write()数据输出永远在out.write()之前

通过jsp,我们可以优化Cookie的案例

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
<%@ page import="java.text.SimpleDateFormat" %>
<%@ page import="java.util.Date" %>
<%@ page import="java.net.URLEncoder" %>
<%@ page import="java.nio.charset.StandardCharsets" %>
<%@ page import="java.net.URLDecoder" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%

//设置有没有lastTime这个cookie标记
boolean flag = false;

//获取当前时间
String time = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss").format(new Date());
//直接存储time,会出错,因为有个空格。当然删掉是可以的,不过最好的办法是url编码
String time_en = URLEncoder.encode(time, StandardCharsets.UTF_8);
//获取cookie
Cookie[] cks = request.getCookies();
//二次访问
if (cks != null && cks.length > 0) {
for (Cookie ck : cks) {
String name = ck.getName();
if ("lastTime".equals(name)) {
flag = true;

//响应数据
String value = URLDecoder.decode(ck.getValue(), "utf-8");
%>
<h1>欢迎回来,您上次访问时间<%=value%>
</h1>
<%
//重新设置cookie值
ck.setValue(time_en);
ck.setMaxAge(60 * 60 * 24 * 30);//cookie存储一个月
response.addCookie(ck);
break;
}
}
}
//初次访问
if (cks == null || cks.length == 0 || !flag) {
%>
<h1>您好,这是你的首次访问</h1>
<%
Cookie lt = new Cookie("lastTime", time_en);
lt.setMaxAge(60 * 60 * 24 * 30);
response.addCookie(lt);
}
%>
</body>
</html>

优点:简单、方便,不用重启服务器即可运行。

缺点:代码没有进行分离。后期很难维护。当然实际开发中,还是不会用这种模式的。

四、Session

概念:服务器端会话技术。在一次会话的多次请求间共享数据,将数据保存在服务器端的对象中(有一个对象就是HttpSession)

4.1 快速入门

  1. 获取HttpSession

    • request.getSession()
  2. HttpSession对象

    • Object getAttribute(String name)
    • void setAttribute(String name, Object value)
    • void removeAttribute(String name)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@WebServlet("/demo01Session")
public class Demo01Session extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//使用Session共享数据

//1.获取Session
HttpSession session = request.getSession();
//2.存储数据
session.setAttribute("msg","Hello Session");

}
}

访问上一个页面的时候,获取到session,并存储了数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@WebServlet("/demo02Session")
public class Demo02Session extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//使用Session共享数据

//1.获取Session
HttpSession session = request.getSession();

//2.获取数据
Object msg = session.getAttribute("msg");
System.out.println(msg);

}
}

访问demo02Session,即可获取到session中存储的数据。

如果客户端关闭之后,则再次获取数据,会获取到null

4.2 实现原理

Session是依赖于Cookie的!

初次访问服务器,获取Session,没有Cookie。会在内存中创建一个新的Session对象,该Session对象有唯一的id(jsp的话就是JSESSIONID;如果是php,会是PHPSESSID),然后将该id以set-cookie的形式返回给客户端。

二次访问服务器,请求头cookie中就会携带该Session对象的id,然后服务器根据id来判断获取的是哪个Session对象

4.3 Session细节

  1. 客户端关闭后,服务器不关闭,两次获取到的Session默认不是同一个
    • 如果需要相同,则可以创建Cookie,键为JSESSIONID,并且设置最大存活时间,让Cookie持久保存
  2. 客户端不关闭,服务器关闭后,两次获取到的Session不是同一个
    • 那么如何在服务器关闭后保持数据不丢失?
      • Session钝化
        • 在服务器正常关闭之前,将session对象存储到硬盘上
      • Session活化
        • 在服务器启动后,将session文件转化为内容中的session对象即可
  3. Session的销毁
    • 默认情况下,服务器关闭
    • session对象调用invalidate()
    • session默认失效时间为30分钟
      • 可以在web.xml进行选择性修改

Session的钝化和活化,在本地Tomcat中是可以自动完成的,在idea的集成环境下,是不可以的。在服务器正常关闭的时候,Tomcat会自动在work目录下生成SESSIONS.ser,当服务器再次启动时,会将该文件读取到内存并删除。javaweb之session序列化与反序列化

4.4 Session的特点

特点

  1. session用于存储一次会话的多次请求的数据,存在服务器端
  2. session可以存储任意类型,任意大小

4.5 Session与Cookie区别

区别

  1. Session存储在服务器端,Cookie存储在客户端
  2. Session没有数据大小限制,Cookie有大小限制
  3. 相对来说,Session比较安全,Cookie不太安全

4.6 案例-验证码

需求

  1. 访问带有验证码的登录页面login.jsp
  2. 用户输入用户名、密码以及验证码
    • 若用户名和密码输入有误,跳转登录页面,提示用户名或密码错误
    • 如果验证码输入有误,跳转登录页面,提示验证码错误
    • 如果全部正确,跳转主页success.jsp,提示用户名,欢迎你

分析

  1. 设置request编码
  2. 获取参数Map集合
  3. 获取验证码
  4. 将用户信息封装到User对象
  5. 判断程序生成的验证码和用户输入的验证码是否一致(从Session中获取程序生成的随机验证码)
    • 不一致
      • 给用户提示信息
      • 跳转登录页面:转发
    • 一致
      • 判断用户名和密码
        • 正确
        • 不正确

实现

login.jsp

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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录</title>
<style>
* {
margin: 0;
padding: 0;
}

.box {
background-color: #74C3B5;
color: #fff;
width:100vw;
height:100vh;
position:fixed;
}
.container {
width: 600px;
margin: 80px auto;
text-align: center;
}

.logo {
width: 250px;
height: 70px;
margin: auto;
border-radius: 5px;
margin-bottom: 40px;
font-size:44px;
}


input {
color: #fff;
width: 250px;
height: 40px;
/*opacity:.5;*/
transition: all ease .4s;
background-color: rgba(255, 255, 255, .2);
text-align: center;
font-size: 20px;
border: 2px solid rgba(255, 255, 255, .4);
outline: none;
border-radius: 5px;
}
img {
outline: 0;
border: 1px solid rgba(255, 255, 255, 0.4);
background-color: rgba(255, 255, 255, 0.2);
width: 250px;
height: 70px;
border-radius: 3px;
padding: 5px 8px;
margin: 0 auto 5px auto;
display: block;
text-align: center;
color: white;
opacity: .9;
box-sizing: border-box;
}

input:focus {
color: #75DFB7;
width: 300px;
background-color: #fff;
}

button {
width: 250px;
height: 40px;
border: none;
background-color: rgba(255, 255, 255, .9);
font-size: 20px;
color: #75DFB7;
cursor: pointer;
border-radius: 5px;
}

ul {
list-style: none;
}

li {
line-height: 50px;
}

form a {
text-decoration: none;
color: #fff;
}

input::placeholder {
color:#fff;
}
.bg-bubbles {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1;
pointer-events: none;
}
.bg-bubbles li {
position: absolute;
list-style: none;
display: block;
width: 40px;
height: 40px;
background-color: rgba(255, 255, 255, 0.15);
bottom: -160px;
-webkit-animation: square 25s infinite;
animation: square 25s infinite;
-webkit-transition-timing-function: linear;
transition-timing-function: linear;
}
.bg-bubbles li:nth-child(1) {
left: 10%;
}
.bg-bubbles li:nth-child(2) {
left: 20%;
width: 80px;
height: 80px;
-webkit-animation-delay: 2s;
animation-delay: 2s;
-webkit-animation-duration: 17s;
animation-duration: 17s;
}
.bg-bubbles li:nth-child(3) {
left: 25%;
-webkit-animation-delay: 4s;
animation-delay: 4s;
}
.bg-bubbles li:nth-child(4) {
left: 40%;
width: 60px;
height: 60px;
-webkit-animation-duration: 22s;
animation-duration: 22s;
background-color: rgba(255, 255, 255, 0.25);
}
.bg-bubbles li:nth-child(5) {
left: 70%;
}
.bg-bubbles li:nth-child(6) {
left: 80%;
width: 120px;
height: 120px;
-webkit-animation-delay: 3s;
animation-delay: 3s;
background-color: rgba(255, 255, 255, 0.2);
}
.bg-bubbles li:nth-child(7) {
left: 32%;
width: 160px;
height: 160px;
-webkit-animation-delay: 7s;
animation-delay: 7s;
}
.bg-bubbles li:nth-child(8) {
left: 55%;
width: 20px;
height: 20px;
-webkit-animation-delay: 15s;
animation-delay: 15s;
-webkit-animation-duration: 40s;
animation-duration: 40s;
}
.bg-bubbles li:nth-child(9) {
left: 25%;
width: 10px;
height: 10px;
-webkit-animation-delay: 2s;
animation-delay: 2s;
-webkit-animation-duration: 40s;
animation-duration: 40s;
background-color: rgba(255, 255, 255, 0.3);
}
.bg-bubbles li:nth-child(10) {
left: 90%;
width: 160px;
height: 160px;
-webkit-animation-delay: 11s;
animation-delay: 11s;
}
@-webkit-keyframes square {
0% {
-webkit-transform: translateY(0);
transform: translateY(0);
}
100% {
-webkit-transform: translateY(-700px) rotate(600deg);
transform: translateY(-700px) rotate(600deg);
}
}
@keyframes square {
0% {
-webkit-transform: translateY(0);
transform: translateY(0);
}
100% {
-webkit-transform: translateY(-700px) rotate(600deg);
transform: translateY(-700px) rotate(600deg);
}
}
error {
color:deeppink;
}
</style>
</head>
<body>
<%
String login_error = (String) request.getAttribute("login_error");
String cc_error = (String) request.getAttribute("cc_error");
%>
<div class="box">
<div class="container">
<div class="logo">
神话Q传
</div>
<%
if(login_error!=null){
out.write("<error>"+login_error+"</error>");
}
if(cc_error!=null){
out.write("<error>"+cc_error+"</error>");
}
%>
<form action="/session/loginServlet" method="post">
<ul>
<li><input type="text" name="username" placeholder="用户名" autocomplete="off"></li>
<li><input type="text" name="password" placeholder="密码" autocomplete="off"></li>
<li><input type="text" name="checkcode" placeholder="不区分大小写"></li>
<li><img id="checkcode" src="/session/checkCodeServlet" alt=""></li>
<li>
<button>登录</button>
</li>
</ul>
</form>
</div>
<ul class="bg-bubbles">
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
</div>
<script>
document.querySelector("#checkcode").onclick=function (){
let url = "/session/checkCodeServlet";
let date=new Date().getTime();
this.setAttribute("src", url+"?"+date)
}
</script>
</body>
</html>

CheckCodeServlet.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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
@WebServlet("/session/checkCodeServlet")
public class CheckCodeServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
int width=100;
int height=30;
//1.创建对象,在内存中画图(验证码图片对象)
BufferedImage image=new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
//2.美化图片
//2.1填充背景色
Graphics g=image.getGraphics();//绘图对象
g.setColor(new Color(255,255,255,255));
g.fillRect(0,0,width,height);
//2.2画边框
g.setColor(Color.RED);
g.drawRect(0,0,width-1,height-1);
//2.3写验证码
String str="ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz";
Random r=new Random();
//StringBuilder在处理多个字符串拼接的时候,效率要比+拼接要高很多。
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < 4; i++) {
//设置字体,绘制字符
g.setFont(new Font(Font.SANS_SERIF,Font.BOLD,20));
char c = str.charAt(r.nextInt(str.length()));
stringBuilder.append(c);
g.drawString(String.valueOf(c),width/5*(i+1),height/2+10);
}
//将验证码存入session
String checkcode=stringBuilder.toString();
request.getSession().setAttribute("checkcode",checkcode);
//2.4 画干扰线
g.setColor(Color.green);
for (int i = 0; i < 10; i++) {
int x1=r.nextInt(width);
int y1=r.nextInt(height);
int x2=r.nextInt(width);
int y2=r.nextInt(height);
g.drawLine(x1,y1,x2,y2);//画线
//画点
// g.drawOval(x1,y1,5,5);
// g.fillOval(x1,y1,5,5);
// g.drawOval(x2,y2,5,5);
// g.fillOval(x2,y2,5,5);
}
//3.将图片输出到页面展示
ImageIO.write(image,"jpg",response.getOutputStream());
}

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request,response);
}
}

LoginServlet.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
33
34
35
36
37
38
39
40
41
@WebServlet("/session/loginServlet")
public class LoginServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1. 设置request编码
request.setCharacterEncoding("utf-8");
HttpSession session = request.getSession();
// 2. 获取参数Map集合
// Map<String, String[]> map = request.getParameterMap();
String username = request.getParameter("username");
String password = request.getParameter("password");
String checkcode = request.getParameter("checkcode");
// 3. 获取验证码
String code = (String)session.getAttribute("checkcode");
//这是为了使验证码保持一次性使用
session.removeAttribute("checkcode");
//忽略大小写
if(code!=null&&code.equalsIgnoreCase(checkcode)){
if("胡列娜".equals(username)&&"fairy".equals(password)){
//存储信息
session.setAttribute("user",username);
//重定向
response.sendRedirect("/session/success.jsp");
}else{
//存储信息
request.setAttribute("login_error","用户名或者密码错误");
//转发
request.getRequestDispatcher("/session/login.jsp").forward(request,response);
}
}else{
//存储信息
request.setAttribute("cc_error","验证码错误");
//转发
request.getRequestDispatcher("/session/login.jsp").forward(request,response);
}
// 4. 将用户信息封装到User对象
}

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}

success.jsp

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
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>主页</title>
<style>
* {
margin: 0;
padding: 0;
}

.box {
background-color: #74C3B5;
color: #fff;
width:100vw;
height:100vh;
position:fixed;
}
</style>
</head>
<body>
<div class="box">
<%
String user= (String) request.getSession().getAttribute("user");
if(user!=null){
out.write(user+",欢迎您");
}else{
response.sendRedirect("/session/login.jsp");
}
%>
</div>
</body>
</html>

这个页面是我原来模仿一个网页游戏写的一个页面,今天无意间发现了那么久远的代码,就直接拿来用了。

问题

验证码重复使用的问题已经解决了。

还有一个问题,就是如果用户直接访问LoginServlet会报错,这个是直接将LoginServlet摆在了明面上,这样做是不太好的,实际开发中,可以异步请求LoginServlet,而LoginServlet只需返回结果即可。这样应该会相对更安全易用

发布:2020-06-23 09:12:05
修改:2023-10-29 10:24:53
链接:https://meethigher.top/blog/2020/cookie-and-session-2/
标签:http web jsp 
付款码 打赏 分享
Shift+Ctrl+1 可控制工具栏