之前写得一篇cookie与session区别
一、会话技术
会话:一次会话中包含多次请求和响应。
一次会话:浏览器第一次给服务器资源发送请求,会话建立,直到有一方断开为止。
功能:在一次会话的范围内的多次请求间,共享数据
方式
- 客户端会话技术:cookie
- 服务器端会话技术:session
有关http协议的知识,移步到这里
二、Cookie
概念:客户端会话技术,将数据保存到客户端
2.1 快速入门
快速入门
- 创建cookie对象,绑定数据
- Cookie(String name, String value)
- 发送cookie
- response.addCookie(Cookie cookie)
- 获取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=new Cookie("msg","hello"); 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[] cookies = request.getCookies(); 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细节
一次可以发送多个Cookie
- 可以创建多个对象,使用response调用多次addCookie方法发送cookie即可
Cookie在浏览器中保存时间
- 默认情况下,当浏览器关闭后,Cookie数据被销毁
- 设置Cookie生命周期:持久化存储
- setMaxAge(int seconds)
- 正数:将Cookie数据写到硬盘的文件中,持久化存储。正数代表cookie的存活时间。比如30,在30s之后,这个Cookie文件将被删除掉
- 负数:默认值,存储在内存中,浏览器关闭,就销毁
- 零:删除Cookie信息
Cookie存储中文
- Tomcat8之前,Cookie中不能直接存储中文数据。需要将中文数据转码,即url编码(%E3,即%跟两个十六进制的数字表示一个字节,有多少个字节,就会有多少%)
- Tomcat8之后,Cookie支持中文数据,但是特殊字符还是不支持,比方说空格。需要用url编码
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 ck1 = new Cookie("msg", "setMaxAge");
Cookie ck2 = new Cookie("hh", "奥利给");
ck2.setPath("/demo04Cookie"); response.addCookie(ck1); response.addCookie(ck2); } }
|
2.4 Cookie的特点和作用
特点
- Cookie存储数据在客户端浏览器
- 浏览器对于单个Cookie的大小有限制(4KB左右,不同浏览器不同),以及同一个域名下的总Cookie数量也有限制(20个,不同浏览器不同)
作用
- Cookie一般用于存储少量的、不敏感的数据
- 在不登录的情况下,完成服务器对客户端的身份识别(比方说百度在不登录的情况下,存储的一些个性化设置,当然这个也是可以通过本地存储来实现)
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");
boolean flag=false;
String time = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss").format(new Date()); String time_en = URLEncoder.encode(time, StandardCharsets.UTF_8); 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>"); ck.setValue(time_en); ck.setMaxAge(60*60*24*30); response.addCookie(ck); break; } } } if(cks==null||cks.length==0|| !flag){ response.getWriter().write("<h1>您好,这是你的首次访问</h1>"); 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 原理
- 客户端请求.jsp,服务端判断有无.jsp。无则404
- 找到.jsp,会将.jsp转换为.java文件
- 编译.java文件,生成.class字节码文件
- 由字节码文件提供访问
我们在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代码的方式
- <% 代码 %>:定义在jsp转换后的java类(Servlet)的service方法中。service方法中可以定义什么,该脚本中就可以定义什么。
- <%! 代码 %>:定义在jsp转换后的java类(Servlet)的成员位置。(不过尽量不要再Servlet中定义成员变量,会引发线程安全问题)
- <%= 代码 %>:定义在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> <%
boolean flag = false;
String time = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss").format(new Date()); String time_en = URLEncoder.encode(time, StandardCharsets.UTF_8); 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> <% ck.setValue(time_en); ck.setMaxAge(60 * 60 * 24 * 30); 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 快速入门
获取HttpSession
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 {
HttpSession session = request.getSession(); 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 {
HttpSession session = request.getSession();
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细节
- 客户端关闭后,服务器不关闭,两次获取到的Session默认不是同一个
- 如果需要相同,则可以创建Cookie,键为JSESSIONID,并且设置最大存活时间,让Cookie持久保存
- 客户端不关闭,服务器关闭后,两次获取到的Session不是同一个
- 那么如何在服务器关闭后保持数据不丢失?
- Session钝化
- 在服务器正常关闭之前,将session对象存储到硬盘上
- Session活化
- 在服务器启动后,将session文件转化为内容中的session对象即可
- Session的销毁
- 默认情况下,服务器关闭
- session对象调用invalidate()
- session默认失效时间为30分钟
Session的钝化和活化,在本地Tomcat中是可以自动完成的,在idea的集成环境下,是不可以的。在服务器正常关闭的时候,Tomcat会自动在work目录下生成SESSIONS.ser,当服务器再次启动时,会将该文件读取到内存并删除。javaweb之session序列化与反序列化
4.4 Session的特点
特点
- session用于存储一次会话的多次请求的数据,存在服务器端
- session可以存储任意类型,任意大小
4.5 Session与Cookie区别
区别
- Session存储在服务器端,Cookie存储在客户端
- Session没有数据大小限制,Cookie有大小限制
- 相对来说,Session比较安全,Cookie不太安全
4.6 案例-验证码
需求
- 访问带有验证码的登录页面login.jsp
- 用户输入用户名、密码以及验证码
- 若用户名和密码输入有误,跳转登录页面,提示用户名或密码错误
- 如果验证码输入有误,跳转登录页面,提示验证码错误
- 如果全部正确,跳转主页success.jsp,提示用户名,欢迎你
分析
- 设置request编码
- 获取参数Map集合
- 获取验证码
- 将用户信息封装到User对象
- 判断程序生成的验证码和用户输入的验证码是否一致(从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; 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; BufferedImage image=new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB); Graphics g=image.getGraphics(); g.setColor(new Color(255,255,255,255)); g.fillRect(0,0,width,height); g.setColor(Color.RED); g.drawRect(0,0,width-1,height-1); String str="ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz"; Random r=new Random(); 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); } String checkcode=stringBuilder.toString(); request.getSession().setAttribute("checkcode",checkcode); 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);
} 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 {
request.setCharacterEncoding("utf-8"); HttpSession session = request.getSession();
String username = request.getParameter("username"); String password = request.getParameter("password"); String checkcode = request.getParameter("checkcode");
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); }
}
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只需返回结果即可。这样应该会相对更安全易用