Cookie的由来(浏览器端的会话技术——保存数据技术)
Cookie的需求分析
场景:
现象:进入淘宝的登陆页面,显示了我上次登录的用户名
显示上次登录用户名这个功能,如何实现?
1、上一次登录的时候,肯定将用户名记录下来
2、肯定是在服务器处理登录的时候,将用户名记录下来
3、那么这个数据到底存在哪里了呢?
Cookie介绍&Cookie案例
Cookie快速入门(创建Cookie对象,发送给浏览器;服务器接收请求中cookie数据)
1、如何创建Cookie对象?查询构造函数
2、在哪里创建Cookie对象?根据之前的分析,记住用户名的动作发生在服务器端,所以在服务器创建Cookie
3、如何将cookie发送给浏览器?给浏览器发出响应的是response对象,将cookie交给response发送
Response API:
代码操作:
// 调用构造函数,创建Cookie对象
Cookie cookie = new Cookie("username", "hehe");
// 通过响应对象将cookie发送给浏览器
response.addCookie(cookie);
效果:
4、如何在浏览器查看cookie(第二种查看cookie的方式)?
ok了
那么接下来,记录在cookie中的数据,服务器如何使用?
1、浏览器将cookie发送给服务器需要我们有特别的操作吗?不需要,浏览器发送http请求会自动带上cookie
2、浏览器自动发送cookie给服务器,服务器如何获取到这个cookie?通过request对象获取
Request API:
代码演示:
// 获取cookie数组(因为在response发送cookie的时候,就可以发送多个)
Cookie[] cookies = request.getCookies();
// 循环判断cookie,与指定用户名一致,就取出
for(Cookie cookie : cookies) {
// cookie.getName() 获取cookie的名称
if(cookie.getName().equals("username")) {
// 取出cookie的值
String value = cookie.getValue();
System.out.println(value);
}
}
Cookie的生命周期
问题1: cookie可以记录数据,但是数据需要一直记录永远不删除吗?
答:数据不删除,数据量越来越大,影响存储空间,数据量越大网络传输cookie慢,服务器解析cookie,也是效率不高。
问题2:那么如何删除cookie数据?
答:我们在服务器创建cookie给浏览器,但是我们无法操作浏览器,因此,在cookie创建的时候设置cookie的生存时间,时间一到自动消失
如果不设置过期时间,默认是多少?
答:浏览会话结束时——浏览器关闭的时候
Cookie API:
代码演示:
// 调用构造函数,创建Cookie对象
Cookie cookie = new Cookie("username222", "haha");
// 设置生存时间,单位秒
cookie.setMaxAge(30);
// 通过响应对象将cookie发送给浏览器
response.addCookie(cookie);
效果:
数据确实发送了
30秒后:username222消失了
chrome://settings/content/cookies——打开查看谷歌浏览器cookie
Cookie的路径设置
现象:
第一张图:
第二张图:
问题:cookie认路!!!为什么cookie认路?
那么这个默认的操作,调用的是哪个API呢?
测试setPath方法一:同级别的不同目录是否可以获取cookie?
效果:获取不到cookie
访问demo2
测试setPath方法二:当前目录的子目录是否可以获取数据?
设置成子目录:
测试效果:
结论:setPath方法可以设置当前目录和旗下子目录servlet都获取cookie,一般setPath(“/”),表示当前项目所有目录都可以获取cookie
一般路径不去设置,或者,设置为“/”;
删除Cookie(马上消失)
删除cookie其实是发送一个新的cookie,设置生成时间为0,而且设置数据为空字符或则null,通过response对象发送之后,会覆盖之前的cookie
注意,删除cookie时,path必须一致,否则不会删除
这个过程就是: 服务器通知浏览器删除自己管理的cookie.
1、将cookie的name(key)保持一致,value 设置为 “”;
cookie = new Cookie(“username”,””)
2、设置存活时间为0,
cookie.setMaxAge(0)
3、路径要发送cookie时保持一致,没有路径不需要设置。
cookie.setPath(“/“);
4、将cookie发送给浏览器。
response.addCookie(cookie)
需求:服务器让浏览器删除缓存中的cookie:
cookie案例一:显示用户上次访问的时间
当用户访问某些web应用的时候,经常会显示出上次的访问时间。
例如:QQ登录成功后,会显示用户上次的登录时间。
画图分析:
代码演示:
// 分两次请求
//定义日期规则字符串
DateFormat format = new SimpleDateFormat("yyyy年MM月dd日HH:mm:ss");
String dateStr = format.format(new Date());
Cookie cookie = new Cookie("time", dateStr);
// 第一次将访问时间,保存到cookie
cookie.setMaxAge(60*60*24*7);
cookie.setPath("/");
response.addCookie(cookie);
// 第二次,获取上一次访问的cookie
Cookie[] cookies = request.getCookies();
if(cookies != null) {
for(Cookie cookie2 : cookies) {
// 判断当前的cookie是表示上一次的访问时间,才获取
if(cookie2.getName().equals("time")) {
// 将数据展示到页面
response.setCharacterEncoding("utf-8");
response.getWriter().write("<html><head><meta charset='utf-8' /></head><body>" + cookie2.getValue() + "</body></html>");
}
}
}
Session(服务器端的会话技术)
Cookie技术可以将用户的信息保存在各自的浏览器中,
优点:很明显实现了同一个用户不同请求中数据共享。
缺点:黑客可以利用脚本等手段窃取cookie中的重要数据,从而泄漏个人的隐私,存在巨大的安全隐患。
Cookie数据保存在用户电脑中,不是每个用户都是电脑高手,对数据安全措施,做的必然不完善。
诞生新技术:session,在服务器端保存用户的数据。(注意:session技术,还是依赖cookie技术)
会话:从第一次请求服务器开始,一直到关闭浏览器,这一段操作,称之为:会话。相当于平时打电话。
什么是session?(重点)
场景:
客户到KFC买东西,点了劲脆鸡腿堡、老北京鸡肉卷、可乐,服务员将三个菜品一次放入打包盒,然后让你带走。
如果使用Cookie解决需求:
使用三个cookie保存数据——但是有问题:cookie数量增多网络传输慢,解析慢。
最好的方式:
1、需要一个容器来存放数据
2、这个容器必然存在服务器
3、每个用户,需要分配一个容器
那么到底这个容器,在功能实现过程中,是怎么运行的?
这个技术,就是session:存储用户多次请求过程中的数据
Session技术原理图: 跟cookie有区别——cookie存数据(用户名称,用户密码:身份验证数据) session存数据(普通业务内容),
总结:
1、session是服务器开辟的一个用来存储数据的空间
2、服务器为每个浏览器单独开辟一个session
3、服务器根据浏览器发送过来的cookie,来确认当前浏览器使用哪个session
补充说明:
- 在WEB开发中,服务器可以为每个用户浏览器创建一个会话对象(session对象),注意:一个浏览器独占一个session对象(默认情况下)。因此,在需要保存用户数据时,服务器程序可以把用户数据写到用户浏览器独占的session中,当用户使用浏览器访问其它程序时,其它程序可以从用户的session中取出该用户的数据,为用户服务。
- Session和Cookie的主要区别在于:
Cookie是把用户的数据写给用户的浏览器。
Session技术把用户的数据写到用户独占的session中(服务器端)。
- Session对象由服务器创建,开发人员可以调用request对象的getSession方法得到session对象。
Session的快速入门
获取Session对象
学习第一步:获取session对象
人话:提供了一个容器,将多次请求中的数据,可以都存储在这个容器中。
接下来,按照面向对象的思路,查询构造函数,创建对象。但是查阅API发现,没有构造函数使用——注意:httpsession对象,有服务器自己创建,不需要程序员手动操作!!!
需要通过一种方式,获取httpSession对象,通过一个对象,他的方法,获取httpSession
通过request对象来获取session对象: 为什么必须通过request来获取session
第一个空参数:
人话:获取当前服务器中给浏览器开辟的Session容器,如果以前没有,创建一个新的给你,如果以前有了,将以前的Session给你
第二个带参数:
人话:如果这个函数参数为true和前面的getSession(),效果一致,如果参数为false,并且,以前没有Session对象,那么返回null。
代码演示:使用API
// 获取session对象的操作
HttpSession session = request.getSession();
System.out.println(session);
效果:
补充说明:
Session是基于用户的请求,而把用户的重要信息在服务器端针对这个用户(浏览器)创建了一个容器。
而这个Session容器是由web服务器(tomcat)帮助我们创建的,在程序中我们只能去获取到这个容器,然后给容器添加数据或者取出数据,或者删除数据,而我们是不能创建这个容器对象。
在Session中存取删数据(重点)
查询HttpSession对象中存数据,取数据,删数据的API
存数据:
人话:指定一个对象保存到会话中,name参数,数据的名称,value参数:具体的值
取数据:
人话:获取指定名称的数据,如果没有,返回null
删数据:
人话:删除Session容器中的数据,删除根据指定的名称(name参数),如果没有数据了,不做任何处理。
演示存取删操作:
package cn.igeek.web;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
public class SessionServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//演示获取session对象(容器,用来保存数据)
HttpSession session = request.getSession();
//org.apache.catalina.session.StandardSessionFacade@4838bd9d : 这个是httpsession接口的实现类,这个类由tomcat实现
System.out.println(session);
//设置数据的方法
/*session.setAttribute("addr", "法国");
//获取数据的方法
String addr = (String)session.getAttribute("addr");
System.out.println(addr);
//删除数据的方法
session.removeAttribute("addr");
String addr1 = (String)session.getAttribute("addr");
System.out.println(addr1);*/
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
记录sessionid的cookie
通过谷歌浏览器的工具查看,SessionId的cookie:
这个cookie明显是服务器创建的,那么是在哪里创建的呢?
测试前先清空cookie数据!
创建记录sessionid的cookie由tomcat中:org.apache.catalina.core.ApplicationSessionCookieConfig
测试:关闭浏览器之后,session对象,还是同一个吗?
第一次访问:
org.apache.catalina.session.StandardSessionFacade@5e7eb260
关闭浏览器,再次访问:
org.apache.catalina.session.StandardSessionFacade@4107e577
问题:为什么,关闭浏览器之后,session对象就换了一个?
结论:关闭浏览器之后,重新访问项目,被分配一个新的session对象,原因——用来寻找session对象的cookie已经不存在了,随着浏览器关闭消失了。
需求:关闭浏览器之后,还要之前的session,如何实现呢?
解决方案:自己创建一个cookie,要求被浏览器持久化保存起来(setMaxAge(10000))放便后期使用
代码演示自己手动持久化cookie:
要使用的API:
人话:获取session的id。
package cn.igeek.web;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
public class SessionServlet2 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
HttpSession session = request.getSession();
System.out.println(session);
//自己创建一个cookie,要求被浏览器持久化保存起来(setMaxAge(10000))放便后期使用
Cookie cookie = new Cookie("JSESSIONID", session.getId());
//活的久一点
//cookie.setMaxAge(10000);
cookie.setPath("/");
response.addCookie(cookie);
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
效果:
效果二:
总结:session容器的获取全部依赖于cookie,服务器自动解析cookie,根据cookie中jsessionid,获取指定的容器对象。
一般情况下,关闭浏览器之后,再次访问,是无法获取到Session中的数据的。
因此在服务器针对当前用户的第一次请求创建的唯一的Session容器对象,而在给这次请求的之后,服务器需要给用户响应数据,在响应的时候,服务器把当前Session容器对象的JSESSIONID以Cookie的形式发送给了浏览器。而这个Cookie并没有设置有效时间,那么这个Cookie就属于临时Cookie,在关闭浏览器之后,再次打开浏览器的时候,上次的Cookie已经消失了,用户虽然拿同一个浏览器访问服务器,可是这时没有JSESSIONID,服务器端的Session容器依然存在,但是没有JSESSIONID,服务器内部无法获取到这个session对象
把包含了JSESSIONID的Cookie在客户端持久化。
Session的生命周期(面试)
使用session存取数据,必须在session对象存活的时候,才可以使用,因此,有必要学习session生命周期(什么时候生,什么时候消失)
Session对象的创建时间:
当第一次调用request.getSession()的时候创建session容器.
如果第一次访问jsp页面,也会创建session容器
Session的销毁时间:
1、(自动消失)Session在服务器的存活时间,Session对象在服务器内部有默认的存活的时间,一般默认是30分钟。如果在30分钟内,用户没有再对当前这个服务器中的资源进行任何的访问操作,这时只要时间到达30分钟,服务器会自动的销毁这个session。
Session的存活时间我们可以在当前这个项目的web.xml中配置:
2、在程序执行中,手动销毁session容器, 使用 session.invalidate()(重点)
人话:销毁session对象,只是让这个对象无效,下次在来访问,给一个新的session对象。
两次调用同一个servlet测试,有销毁方法,获取到不同的session对象
3、不正常关闭服务器。直接点击控制台的红点,强制关闭tomcat
如果是正常关闭服务器,这时服务器内部会使用IO流中的序列化技术把这个Session对象保存在tomcat/work目录下面。Session文件:
Session应用:登陆验证码
什么是登陆验证吗?
防止黑客暴力破解用户的密码的一种技术。
什么是暴力破解用户的密码?
黑客这样的暴力破解,有什么办法解决呢?验证码
需求:在登录的时候,加上验证码校验
页面准备:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>login</title>
<script type="text/javascript">
function changeCode(){
//设置src属性的地址如果和前一次相同,默认从浏览器缓存的图片中获取数据,不再发请求到浏览器
//第一:地址不能变
//第二:必须让浏览器,察觉地址的一些内容,产生了变化
//第三:地址最后的参数,最好,每次都发生变化
//Date();获取当前的时间
//var date = new Date();
//获取当前时间的毫秒值
//var time = date.getMilliseconds()
document.getElementById("img").src = "/study/getCode?r="+new Date().getTime();
/**
为什么要使用验证码?
登陆功能——username password ——》登陆
用户名密码暴力破解:黑客有一个密码库
(一个数据库,存储了普通用户一般会使用的常见密码组合,甚至,将键盘上所有的字符组合通过遍历,一个一个存储到数据库)
当黑客知道你的用户名之后,(cookie,可能被黑客利用,获取用户名)
使用密码库中的密码,和用户的用户名组合,开始破解,用户的登陆
解决方案:限制黑客暴力破解密码,使用一个随机生成的验证码,用户登陆,必须,手动输入,验证码的内容,才允许执行登陆
*/
}
</script>
</head>
<body>
<form action="/study/login" method="post">
<table>
<tr><td>用户名:</td><td><input type="text" name="username"></td></tr>
<tr><td>密码:</td><td><input type="password" name="password"></td></tr>
<tr><td>验证码:</td><td><input type="text" name="yanzhengma"></td></tr>
<!-- 通过向服务器发送请求,从服务器获取验证码数据 -->
<tr><td></td><td><img id="img" src="/study/getCode" onclick="changeCode();"/><a href="javascript:;" onclick="changeCode();">换一换</a></td></tr>
<tr><td></td><td><input type="submit" value="登陆"></td></tr>
</table>
</form>
</body>
</html>
登陆流程分析:
CodeUtil
package cn.igeek.util;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import javax.imageio.ImageIO;
public class CodeUtil {
private static int width = 90;// 定义图片的width
private static int height = 20;// 定义图片的height
private static int codeCount = 4;// 定义图片上显示验证码的个数
private static int xx = 15;
private static int fontHeight = 18;
private static int codeY = 16;
private static char[] codeSequence = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
/**
* 生成一个map集合
* code为生成的验证码
* codePic为生成的验证码BufferedImage对象
* @return
*/
public static Map<String,Object> generateCodeAndPic() {
// 定义图像buffer
BufferedImage buffImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
// Graphics2D gd = buffImg.createGraphics();
// Graphics2D gd = (Graphics2D) buffImg.getGraphics();
Graphics gd = buffImg.getGraphics();
// 创建一个随机数生成器类
Random random = new Random();
// 将图像填充为白色
gd.setColor(Color.WHITE);
gd.fillRect(0, 0, width, height);
// 创建字体,字体的大小应该根据图片的高度来定。
Font font = new Font("Fixedsys", Font.BOLD, fontHeight);
// 设置字体。
gd.setFont(font);
// 画边框。
gd.setColor(Color.BLACK);
gd.drawRect(0, 0, width - 1, height - 1);
// 随机产生40条干扰线,使图象中的认证码不易被其它程序探测到。
gd.setColor(Color.BLACK);
for (int i = 0; i < 30; i++) {
int x = random.nextInt(width);
int y = random.nextInt(height);
int xl = random.nextInt(12);
int yl = random.nextInt(12);
gd.drawLine(x, y, x + xl, y + yl);
}
// randomCode用于保存随机产生的验证码,以便用户登录后进行验证。
StringBuffer randomCode = new StringBuffer();
int red = 0, green = 0, blue = 0;
// 随机产生codeCount数字的验证码。
for (int i = 0; i < codeCount; i++) {
// 得到随机产生的验证码数字。
String code = String.valueOf(codeSequence[random.nextInt(36)]);
// 产生随机的颜色分量来构造颜色值,这样输出的每位数字的颜色值都将不同。
red = random.nextInt(255);
green = random.nextInt(255);
blue = random.nextInt(255);
// 用随机产生的颜色将验证码绘制到图像中。
gd.setColor(new Color(red, green, blue));
gd.drawString(code, (i + 1) * xx, codeY);
// 将产生的四个随机数组合在一起。
randomCode.append(code);
}
Map<String,Object> map =new HashMap<String,Object>();
//存放验证码
map.put("code", randomCode);
//存放生成的验证码BufferedImage对象
map.put("codePic", buffImg);
return map;
}
public static void main(String[] args) throws Exception {
//创建文件输出流对象
OutputStream out = new FileOutputStream("D://img/"+System.currentTimeMillis()+".jpg");
Map<String,Object> map = CodeUtil.generateCodeAndPic();
ImageIO.write((RenderedImage) map.get("codePic"), "jpeg", out);
System.out.println("验证码的值为:"+map.get("code"));
}
}
CodeServlet
package cn.igeek.web;
import java.awt.image.RenderedImage;
import java.io.IOException;
import java.util.Map;
import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import cn.igeek.util.CodeUtil;
@WebServlet("/getCode")
public class CodeServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 调用工具类生成的验证码和验证码图片
Map<String, Object> codeMap = CodeUtil.generateCodeAndPic();
// 将四位数字的验证码保存到Session中。
HttpSession session = req.getSession();
session.setAttribute("code_session", codeMap.get("code").toString());
// 禁止图像缓存。
resp.setHeader("Pragma", "no-cache");
resp.setHeader("Cache-Control", "no-cache");
resp.setDateHeader("Expires", -1);
resp.setContentType("image/jpeg");
// 将图像输出到Servlet输出流中。
ServletOutputStream sos;
try {
sos = resp.getOutputStream();
ImageIO.write((RenderedImage) codeMap.get("codePic"), "jpeg", sos);
sos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
LoginServlet
package cn.igeek.web;
import java.io.IOException;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import cn.igeek.domain.User;
import cn.igeek.service.UserService;
public class LoginServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//用户请求中的验证码获取
String yanzhengma = request.getParameter("yanzhengma");
//与session中保存的验证码进行校验
String code_session = (String)request.getSession().getAttribute("code_session");
if(!code_session.equalsIgnoreCase(yanzhengma)){
//验证码错误,告诉用户,页面提示
request.setAttribute("msg", "验证码错误");
System.out.println("验证码错误");
request.getRequestDispatcher("/login.html").forward(request, response);
return;
}
//验证码正确,登录逻辑执行
//获取用户名和密码
String username = request.getParameter("username");
String password = request.getParameter("password");
//调用Service方法,登录用户
UserService userService = new UserService();
//ctrl+shift+O自动解决一些常见问题,如果解决不了,留着程序员解决
User user = new User();
user.setUsername(username);
user.setPassword(password);
User loginUser = userService.login(user);
if(loginUser == null){
request.setAttribute("msg", "用户名或则密码错误");
System.out.println("用户名或则密码错误");
request.getRequestDispatcher("/login.html").forward(request, response);
return;
}else{
System.out.println("登录成功");
//登陆成功,跳转主页
response.sendRedirect(request.getContextPath());
//不写return,而且后面,还要可以执行的代码,有可能发送一个错误:response对象已经提交了,无法再次提交
return;
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
UserService
package cn.igeek.service;
import cn.igeek.dao.UserDao;
import cn.igeek.domain.User;
public class UserService {
private UserDao userDao = new UserDao();
/**
* 用户登陆的方法
* @param user
* @return
* @throws Exception
*/
public User login(User user) {
return userDao.login(user);
}
public boolean register(User user) {
User findByname = userDao.findByName(user.getUsername());
if(findByname == null){
userDao.register(user);
return true;
}else{
return false;
}
}
}
UserDao
package cn.igeek.dao;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import cn.igeek.domain.User;
public class UserDao {
private Connection conn = null;
private QueryRunner qr = new QueryRunner();
public UserDao() {
try {
Class.forName("com.mysql.cj.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/stock?useUnicode=true&characterEncoding=utf-8&useSSL=FALSE&serverTimezone=UTC", "root", "123456");
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 处理用户登陆的方法
* @param user
* @return
* @throws Exception
*/
public User login(User user) {
String sql = "select * from user where username = ? and password = ?";
//为什么要抓取异常?
try {
return qr.query(conn, sql, new BeanHandler<User>(User.class), user.getUsername(),user.getPassword());
} catch (SQLException e) {
e.printStackTrace();
//对上面这些异常信息,做了一个中文注释
throw new RuntimeException("用户登陆失败");
}
//try catch finally throw throws
}
public User findByName(String username) {
String sql = "select * from user where username = ?";
try {
return qr.query(conn, sql, new BeanHandler<User>(User.class), username);
} catch (SQLException e) {
e.printStackTrace();
return null;
}
}
public void register(User user) {
String sql = "insert into user values(null,?,?,?,?,?,?,?,?,?)";
List<Object> list= new ArrayList<>();
list.add(user.getUsername());
list.add(user.getPassword());
list.add(user.getAge());
list.add(user.getSex());
list.add(user.getEmail());
list.add(user.getBirthday());
list.add(user.getHobby());
list.add(user.getAddress());
list.add(user.getDescription());
try {
qr.update(conn, sql, list.toArray());
} catch (SQLException e) {
e.printStackTrace();
}
}
}
JavaWEB中三种数据范围(三种容器)
Request ServletContext Session
三个对象都有:setAttribute getAttribute方法,都可以存取数据
回答面试官问题套路:先回答基本概念,返回,跟上使用案例。
问题:什么时候使用request对象,保存数据?
答:一次请求中需要使用的数据,就保存在request。举例:商品数据,就存入请求中。
问题:什么时候使用ServletContext 对象,保存数据?
答:全局使用的数据,整个项目需要使用的数据,就是,要存入ServletContext 。举例:在线人数,存入ServletContext 。
问题:什么时候使用session对象,保存数据?
答:一次会话(多次请求——都发生在一次开启浏览器和关闭浏览器过程中)中需要使用的数据,都存入session。
举例:点餐商品
加密解密
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.15</version>
</dependency>
public class Test {
public static void main(String[] args) {
try {
// System.out.println(URLEncoder.encode("我在igeek学习java", "UTF-8"));
// System.out.println(URLEncoder.encode("我在igeek学习java", "GBK"));
// System.out.println("====================================================");
// myUrlEncode("我在igeek学习java", "UTF-8");
// myUrlEncode("我在igeek学习java", "GBK");
/*
System.out.println(org.apache.tomcat.util.codec.binary.Base64.encodeBase64String("测试".getBytes(StandardCharsets.UTF_8)));
System.out.println(Base64.getEncoder().encodeToString("测试".getBytes(StandardCharsets.UTF_8)));*/
//不可逆加密 - MD5
String rs = DigestUtils.md5Hex("我在igeek学习java士大夫撒地方是的发送到发送到发发送到发送到阿斯蒂芬斯蒂芬asdfdsf");
rs = DigestUtils.md5Hex(rs + "igeekhome");
System.out.println("MD5->" + rs);
//不可逆加密 - SHA-1
rs = DigestUtils.sha1Hex("我在igeek学习java士大夫撒地方是的发送到发送到发发送到发送到阿斯蒂芬斯蒂芬asdfdsfsdfasdfasdfsadf撒旦法撒旦法说的");
System.out.println("sha1->" + rs);
//可逆加密 - 对称加密(AES)
// 原文:
String message = "我在igeek学习java啊啊发大水发顺丰达是打发士大夫撒地方手动阀";
String keyStr = "1234567890abcdef";
System.out.println("Message: " + message);
System.out.println("key:" + keyStr);
// 128位密钥 = 16 bytes Key:
byte[] key = keyStr.getBytes("UTF-8");
// 加密:
byte[] encrypted = encrypt(key, message.getBytes("UTF-8"));
System.out.println("对称加密——Encrypted: " + Base64.getEncoder().encodeToString(encrypted));
// 解密:
byte[] decrypted = decrypt(key, encrypted);
System.out.println("对称解密——Decrypted: " + new String(decrypted, "UTF-8"));
} catch (Exception e) {
e.printStackTrace();
}
}
public static String myUrlEncode(String s, String enc) throws Exception {
byte[] bytes = s.getBytes(enc);
System.out.println(Arrays.toString(bytes));
StringBuilder sb = new StringBuilder();
for(byte b : bytes) {
if((b >='0' && b<='9') || (b>='a' && b<='z') || (b>='A' && b <='Z')) {
sb.append((char)b);
} else {
sb.append("%");
sb.append(String.format("%02x", b).toUpperCase());
}
}
System.out.println(sb);
return null;
}
// 加密:
public static byte[] encrypt(byte[] key, byte[] input) throws GeneralSecurityException {
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
SecretKey keySpec = new SecretKeySpec(key, "AES");
cipher.init(Cipher.ENCRYPT_MODE, keySpec);
return cipher.doFinal(input);
}
// 解密:
public static byte[] decrypt(byte[] key, byte[] input) throws GeneralSecurityException {
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
SecretKey keySpec = new SecretKeySpec(key, "AES");
cipher.init(Cipher.DECRYPT_MODE, keySpec);
return cipher.doFinal(input);
}
}
public class EncodeCUtil {
// 加密:
public static String encrypt(String key, String input) {
String rs = null;
try {
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
SecretKey keySpec = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "AES");
cipher.init(Cipher.ENCRYPT_MODE, keySpec);
byte[] bytes = cipher.doFinal(input.getBytes(StandardCharsets.UTF_8));
rs = Base64.getEncoder().encodeToString(bytes);
} catch (Exception e) {
e.printStackTrace();
}
return rs;
}
// 解密:
public static String decrypt(String key, String input) {
String rs = null;
try {
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
SecretKey keySpec = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "AES");
cipher.init(Cipher.DECRYPT_MODE, keySpec);
byte[] bytes = cipher.doFinal(Base64.getDecoder().decode(input));
rs = new String(bytes, "UTF-8");
} catch (Exception e) {
e.printStackTrace();
}
return rs;
}
}
非对称加密
package com.woniu.springsecurity.util;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.IOUtils;
import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;
public class RSAUtil {
public static final String CHARSET = "UTF-8";
public static final String RSA_ALGORITHM = "RSA";
public static Map<String, String> createKeys(int keySize){
//为RSA算法创建一个KeyPairGenerator对象(KeyPairGenerator,密钥对生成器,用于生成公钥和私钥对)
KeyPairGenerator kpg;
try{
kpg = KeyPairGenerator.getInstance(RSA_ALGORITHM);
}catch(NoSuchAlgorithmException e){
throw new IllegalArgumentException("No such algorithm-->[" + RSA_ALGORITHM + "]");
}
//初始化KeyPairGenerator对象,密钥长度
kpg.initialize(keySize);
//生成密匙对
KeyPair keyPair = kpg.generateKeyPair();
//得到公钥
Key publicKey = keyPair.getPublic();
String publicKeyStr = Base64.encodeBase64URLSafeString(publicKey.getEncoded()); //返回一个publicKey经过二次加密后的字符串
//得到私钥
Key privateKey = keyPair.getPrivate();
String privateKeyStr = Base64.encodeBase64URLSafeString(privateKey.getEncoded()); //返回一个privateKey经过二次加密后的字符串
Map<String, String> keyPairMap = new HashMap<String, String>();
keyPairMap.put("publicKey", publicKeyStr);
keyPairMap.put("privateKey", privateKeyStr);
return keyPairMap;
}
/**
* 得到公钥
* @param publicKey 密钥字符串(经过base64编码)
* @throws Exception
*/
public static RSAPublicKey getPublicKey(String publicKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
//通过X509编码的Key指令获得公钥对象
KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(Base64.decodeBase64(publicKey));
RSAPublicKey key = (RSAPublicKey) keyFactory.generatePublic(x509KeySpec);
return key;
}
/**
* 得到私钥
* @param privateKey 密钥字符串(经过base64编码)
* @throws Exception
*/
public static RSAPrivateKey getPrivateKey(String privateKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
//通过PKCS#8编码的Key指令获得私钥对象
KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKey));
RSAPrivateKey key = (RSAPrivateKey) keyFactory.generatePrivate(pkcs8KeySpec);
return key;
}
/**
* 公钥加密
* @param data
* @param publicKey
* @return
*/
public static String publicEncrypt(String data, RSAPublicKey publicKey){
try{
Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
return Base64.encodeBase64URLSafeString(rsaSplitCodec(cipher, Cipher.ENCRYPT_MODE, data.getBytes(CHARSET), publicKey.getModulus().bitLength()));
}catch(Exception e){
throw new RuntimeException("加密字符串[" + data + "]时遇到异常", e);
}
}
/**
* 私钥解密
* @param data
* @param privateKey
* @return
*/
public static String privateDecrypt(String data, RSAPrivateKey privateKey){
try{
Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return new String(rsaSplitCodec(cipher, Cipher.DECRYPT_MODE, Base64.decodeBase64(data), privateKey.getModulus().bitLength()), CHARSET);
}catch(Exception e){
throw new RuntimeException("解密字符串[" + data + "]时遇到异常", e);
}
}
/**
* 私钥加密
* @param data
* @param privateKey
* @return
*/
public static String privateEncrypt(String data, RSAPrivateKey privateKey){
try{
Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
return Base64.encodeBase64URLSafeString(rsaSplitCodec(cipher, Cipher.ENCRYPT_MODE, data.getBytes(CHARSET), privateKey.getModulus().bitLength()));
}catch(Exception e){
throw new RuntimeException("加密字符串[" + data + "]时遇到异常", e);
}
}
/**
* 公钥解密
* @param data
* @param publicKey
* @return
*/
public static String publicDecrypt(String data, RSAPublicKey publicKey){
try{
Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, publicKey);
return new String(rsaSplitCodec(cipher, Cipher.DECRYPT_MODE, Base64.decodeBase64(data), publicKey.getModulus().bitLength()), CHARSET);
}catch(Exception e){
throw new RuntimeException("解密字符串[" + data + "]时遇到异常", e);
}
}
private static byte[] rsaSplitCodec(Cipher cipher, int opmode, byte[] datas, int keySize){
int maxBlock = 0;
if(opmode == Cipher.DECRYPT_MODE){
maxBlock = keySize / 8;
}else{
maxBlock = keySize / 8 - 11;
}
ByteArrayOutputStream out = new ByteArrayOutputStream();
int offSet = 0;
byte[] buff;
int i = 0;
try{
while(datas.length > offSet){
if(datas.length-offSet > maxBlock){
buff = cipher.doFinal(datas, offSet, maxBlock);
}else{
buff = cipher.doFinal(datas, offSet, datas.length-offSet);
}
out.write(buff, 0, buff.length);
i++;
offSet = i * maxBlock;
}
}catch(Exception e){
throw new RuntimeException("加解密阀值为["+maxBlock+"]的数据时发生异常", e);
}
byte[] resultDatas = out.toByteArray();
IOUtils.closeQuietly(out);
return resultDatas;
}
public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeySpecException {
Map<String, String> keyMap = RSAUtil.createKeys(1024);
String publicKey = keyMap.get("publicKey");
String privateKey = keyMap.get("privateKey");
System.out.println("公钥: \n\r" + publicKey);
System.out.println("私钥: \n\r" + privateKey);
// System.out.println("公钥加密——私钥解密");
// String str = "寒山月初℃";
// System.out.println("\r明文:\r\n" + str);
// System.out.println("\r明文大小:\r\n" + str.getBytes().length);
// String encodedData = RSAUtil.publicEncrypt(str, RSAUtil.getPublicKey(publicKey));
// System.out.println("密文:\r\n" + encodedData);
// String decodedData = RSAUtil.privateDecrypt(encodedData, RSAUtil.getPrivateKey(privateKey));
// System.out.println("解密后文字: \r\n" + decodedData);
System.out.println("私钥加密——公钥解密");
String str = "寒山月初℃";
System.out.println("\r明文:\r\n" + str);
System.out.println("\r明文大小:\r\n" + str.getBytes().length);
String encodedData = RSAUtil.privateEncrypt(str, RSAUtil.getPrivateKey(privateKey));
System.out.println("密文:\r\n" + encodedData);
String decodedData = RSAUtil.publicDecrypt(encodedData, RSAUtil.getPublicKey(publicKey));
System.out.println("解密后文字: \r\n" + decodedData);
}
}