登陆---显示用户名:
1.UserServlet 程序中保存用户登录的信息
/**
* 登录的功能
* @param request
* @param response
* @throws ServletException
* @throws IOException
*/
protected void login(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
//登录的代码
//获取请求参数
String username = request.getParameter("username");
String password = request.getParameter("password");
//调用service登录
User login = userService.login(new User(null, username, password, null));
if(login == null){
//登录失败
//把错误的信息,和回显的表单信息,保存到Request域
request.setAttribute("msg","用户名或密码错误!");
request.setAttribute("username",username);
System.out.println("登录失败");
request.getRequestDispatcher("/pages/user/login.jsp").forward(request, response);
}else{
//登录成功
//保存用户登录之后的信息到Session域中
request.getSession().setAttribute("user",login);
//跳转到login_success.jsp
request.getRequestDispatcher("/pages/user/login_success.jsp").forward(request, response);
}
}
2.修改 login_succuess_menu.jsp
<div>
<span>欢迎<span class="um_span">${sessionScope.user.username}</span>光临尚硅谷书城</span>
<a href="pages/order/order.jsp">我的订单</a>
<a href="index.jsp">注销</a>
<a href="index.jsp">返回</a>
</div>
3.修改首页 index.jsp 页面的菜单
<%--如果用户还没有登录,显示 【登录 和注册的菜单】 --%>
<c:if test="${empty sessionScope.user}">
<a href="pages/user/login.jsp">登录</a> |
<a href="pages/user/regist.jsp">注册</a>
</c:if>
<%--如果已经登录,则显示 登录 成功之后的用户信息。--%>
<c:if test="${not empty sessionScope.user}">
<span>欢迎<span class="um_span">${sessionScope.user.username}</span>光临尚硅谷书城</span>
<a href="pages/order/order.jsp">我的订单</a>
<a href="userServlet?action=logout">注销</a>
</c:if>
登出---注销用户:
1、销毁 Session 中用户登录的信息(或者销毁 Session)。
2、重定向到首页(或登录页面)。
UserServlet 程序中添加 logout 方法:
/**注销
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
protected void logout(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1、销毁 Session 中用户登录的信息(或者销毁 Session)
req.getSession().invalidate();
// 2、重定向到首页(或登录页面)。
resp.sendRedirect(req.getContextPath());
}
修改【注销】的菜单地址:
<a href="userServlet?action=logout">注销</a>
表单重复提交之-----验证码:
表单重复提交有三种常见的情况:
一:提交完表单。服务器使用请求转来进行页面跳转。这个时候,用户按下功能键 F5,就会发起最后一次的请求。 造成表单重复提交问题。解决方法:使用重定向来进行跳转。
此时提交表单,按F5,就会已知提交。
修改为重定向:
二:用户正常提交服务器,但是由于网络延迟等原因,迟迟未收到服务器的响应,这个时候,用户以为提交失败, 就会着急,然后多点了几次提交操作,也会造成表单重复提交。使用验证码解决。
三:用户正常提交服务器。服务器也没有延迟,但是提交完成后,用户回退浏览器。重新提交。也会造成表单重复提交。使用验证码解决。
验证码解决:
原理
谷歌 kaptcha 图片验证码的使用:
谷歌验证码 kaptcha 使用步骤如下:
1、导入谷歌验证码的 jar 包 kaptcha-2.3.2.jar
2、在 web.xml 中去配置用于生成验证码的 Servlet 程序
<servlet>
<servlet-name>KaptchaServlet</servlet-name>
<servlet-class>com.google.code.kaptcha.servlet.KaptchaServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>KaptchaServlet</servlet-name>
<url-pattern>/kaptcha.jpg</url-pattern>
</servlet-mapping>
3、在表单中使用 img 标签去显示验证码图片并使用。
<form action="http://localhost:8080/tmp/registServlet" method="get">
用户名:<input type="text" name="username" > <br>
验证码:<input type="text" style="width: 80px;" name="code">
<img src="http://localhost:8080/tmp/kaptcha.jpg" alt="" style="width: 100px; height: 28px;"> <br>
<input type="submit" value="登录">
</form>
4、在服务器获取谷歌生成的验证码和客户端发送过来的验证码比较使用。
服务器在一次会话范围内,多次获取的Session对象是同一个。
因为Session 技术,底层其实是基于 Cookie 技术来实现的。
浏览器首次访问服务器时,发现cookie中,并没有SESSION这个字段,于是服务器就创建了一个session对象,并用一个id对应这个session对象,然后,将该id设置到cookie中,然后传给浏览器。
当浏览器再次访问服务器时,会将上次的JESSIONID的值传给服务器,服务器就可以根据这个值,获取上一次创建的Session对象,这就保证了,在一次会话中,服务器获取的是同一个sessiono数据。
*request.getSession()
第一次调用是:创建 Session 会话
之后调用都是:获取前面创建好的 Session 会话对象。
第一次点击提交时,获取验证码后,就会删除Session中的验证码,之后储蓄上面的“二三”的情况的时候,此时session域中没有验证码,就不会重复提交了。
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
IOException {
// 获取 Session 中的验证码
String token = (String) req.getSession().getAttribute(KAPTCHA_SESSION_KEY);
// 删除 Session 中的验证码
req.getSession().removeAttribute(KAPTCHA_SESSION_KEY);
String code = req.getParameter("code");
// 获取用户名
String username = req.getParameter("username");
if (token != null && token.equalsIgnoreCase(code)) {
System.out.println("保存到数据库:" + username);
resp.sendRedirect(req.getContextPath() + "/ok.jsp");
} else {
System.out.println("请不要重复提交表单");
}
}
切换验证码:
// 给验证码的图片,绑定单击事件
$("#code_img").click(function () {
// 在事件响应的 function 函数中有一个 this 对象。这个 this 对象,是当前正在响应事件的 dom 对象
// src 属性表示验证码 img 标签的 图片路径。它可读,可写
// alert(this.src);
this.src = "${basePath}kaptcha.jpg?d=" + new Date();
});
在书城中加入验证码:
<label>验证码:</label>
<input class="itxt" type="text" name="code" style="width: 80px;" id="code"/>
<img alt="" src="kaptcha.jpg" style="float: right; margin-right: 40px; width:110px;height:30px;">
/**
* 注册的功能
*
* @param request
* @param response
* @throws ServletException
* @throws IOException
*/
protected void regist(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//注册的代码
// 获取 Session 中的验证码
String token = (String) request.getSession().getAttribute(KAPTCHA_SESSION_KEY);
// 删除 Session 中的验证码
request.getSession().removeAttribute(KAPTCHA_SESSION_KEY);
//获取请求参数
String username = request.getParameter("username");
String password = request.getParameter("password");
String email = request.getParameter("email");
String code = request.getParameter("code");
User user = WebUtils.copyParamToBean(request.getParameterMap(), new User());
//检验验证码是否正确
if (token != null && token.equalsIgnoreCase(code)) {
//正确
//检查用户名是否可用
if (userService.existsUsername(username)) {
//不可用 跳转回注册页面
System.out.println("用户名已存在");
//把回显信息保存到request域中
request.setAttribute("msg", "用户名已存在!");
request.setAttribute("username", username);
request.setAttribute("email", email);
request.getRequestDispatcher("/pages/user/regist.jsp").forward(request, response);
} else {
//可用 调用service保存到数据库
userService.registUser(new User(null, username, password, email));
//跳转到注册成功页面
request.getRequestDispatcher("/pages/user/regist_success.jsp").forward(request, response);
}
} else {
//不正确
//把回显信息保存到request域中
request.setAttribute("msg", "验证码错误!");
request.setAttribute("username", username);
request.setAttribute("email", email);
//跳转回注册界面 请求转发
System.out.println("验证码错误");
request.getRequestDispatcher("/pages/user/regist.jsp").forward(request, response);
}
}
验证码的切换:
//验证码的切换
// 给验证码的图片,绑定单击事件
$("#code_img").click(function () {
// 在事件响应的 function 函数中有一个 this 对象。这个 this 对象,是当前正在响应事件的 dom 对象
// src 属性表示验证码 img 标签的 图片路径。它可读,可写
// alert(this.src);
this.src = "${basePath}kaptcha.jpg?d=" + new Date();
});
购物车模块分析:
采用session版本实现:
购物车模型:
/**
* 购物车商品项
*/
public class CartItem {
private Integer id;
private String name;
private Integer count;
private BigDecimal price;
private BigDecimal totalPrice;
}
购物车对象:
/**
* 购物车对象
*/
public class Cart {
//在方法内部定义
// private Integer totalCount;
// private BigDecimal totalPrice;
/**
* key 是商品编号,
* value,是商品信息
*/
private Map<Integer,CartItem> items = new LinkedHashMap<Integer,CartItem>();
/**
* 添加商品项
*
* @param cartItem
*/
public void addItem(CartItem cartItem) {
// 先查看购物车中是否已经添加过此商品,如果已添加,则数量累加,总金额更新,如果没有添加过,直接放到集合中即可
CartItem item = items.get(cartItem.getId());
if (item == null) {
// 之前没添加过此商品
//放进集合
items.put(cartItem.getId(), cartItem);
} else {
// 已经 添加过的情况
item.setCount( item.getCount() + 1 ); // 数量 累加
item.setTotalPrice( item.getPrice().multiply(new BigDecimal( item.getCount() )) ); // 更新总金额
}
}
/**
* 删除商品项
*/
public void deleteItem(Integer id) {
items.remove(id);
}
/**
* 清空购物车
*/
public void clear() {
items.clear();
}
/**
* 修改商品数量
*/
public void updateCount(Integer id,Integer count) {
// 先查看购物车中是否有此商品。如果有,修改商品数量,更新总金额
CartItem cartItem = items.get(id);
if (cartItem != null) {
cartItem.setCount(count);// 修改商品数量
cartItem.setTotalPrice( cartItem.getPrice().multiply(new
BigDecimal( cartItem.getCount() )) ); // 更新总金额
}
}
public Integer getTotalCount() {
Integer totalCount = 0;
//遍历Map
for (Map.Entry<Integer,CartItem>entry : items.entrySet()) {
//获取每项数据的数量
totalCount += entry.getValue().getCount();
}
return totalCount;
}
public BigDecimal getTotalPrice() {
BigDecimal totalPrice = new BigDecimal(0);
//遍历Map
for (Map.Entry<Integer,CartItem>entry : items.entrySet()) {
//累加,获取总价格
totalPrice = totalPrice.add(entry.getValue().getTotalPrice());
}
return totalPrice;
}
public Map<Integer, CartItem> getItems() {
return items;
}
public void setItems(Map<Integer, CartItem> items) {
this.items = items;
}
@Override
public String toString() {
return "Cart{" +
"totalCount=" + getTotalCount() +
", totalPrice=" + getTotalPrice() +
", items=" + items +
'}';
}
}
加入购物车功能的实现:
CartServlet 程序中的代码:
private BookService bookService = new BookServiceImpl();
/**
* 加入购物车
* @param request
* @param response
* @throws ServletException
* @throws IOException
*/
protected void addItem(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
// 获取请求的参数 商品编号
int id = WebUtils.parseInt(request.getParameter("id"), 0);
// 调用 bookService.queryBookById(id):Book 通过Id查找得到图书的信息
Book book = bookService.queryBookById(id);
// 把图书信息,转换成为 CartItem 商品项
CartItem cartItem = new CartItem(book.getId(),book.getName(),1,book.getPrice(),book.getPrice());
// 调用 Cart.addItem(CartItem);添加商品项
//购物车是放进session中的,从Session中获取购物车
//没有才创建,有直接添加
Cart cart = (Cart) request.getSession().getAttribute("cart");
if (cart == null) {
cart = new Cart();
request.getSession().setAttribute("cart",cart);
}
cart.addItem(cartItem);
System.out.println(cart);
//得到浏览器当前的访问路径
System.out.println("请求头 Referer 的值:" + request.getHeader("Referer"));
// 重定向回原来商品所在的地址页面
response.sendRedirect(request.getHeader("Referer"));
}
jsp代码:
<Script type="text/javascript">
$(function () {
// 给加入购物车按钮绑定单击事件
$("button.addToCart").click(function () {
/**
* 在事件响应的function函数 中,有一个this对象,这个this对象,是当前正在响应事件的dom对象
* @type {jQuery}
* attr 设置和获取指定元素的值
*/
var bookId = $(this).attr("bookId");
location.href = "http://localhost:8080/book/cartServlet?action=addItem&id=" + bookId;
});
});
</Script>
<div class="book_add">
<button bookId="${book.id}" class="addToCart">加入购物车</button>
</div>
图解说明,如何跳回添加商品的页面:
1.购物车的展示:
</head>
<body>
<div id="header">
<img class="logo_img" alt="" src="static/img/logo.gif" >
<span class="wel_word">购物车</span>
<%--静态包含,登录 成功之后的菜单 --%>
<%@ include file="/pages/common/login_success_menu.jsp"%>
</div>
<div id="main">
<table>
<tr>
<td>商品名称</td>
<td>数量</td>
<td>单价</td>
<td>金额</td>
<td>操作</td>
</tr>
<c:if test="${empty sessionScope.cart.items}">
<%--如果购物车空的情况--%>
<tr>
<td colspan="5"><a href="index.jsp">亲,当前购物车为空!快跟小伙伴们去浏览商品吧!!!</a>
</td>
</tr>
</c:if>
<c:if test="${not empty sessionScope.cart.items}">
<%--如果购物车非空的情况--%>
<c:forEach items="${sessionScope.cart.items}" var="entry">
<tr>
<td>${entry.value.name}</td>
<td>${entry.value.count}</td>
<td>${entry.value.price}</td>
<td>${entry.value.totalPrice}</td>
<td><a href="#">删除</a></td>
</tr>
</c:forEach>
</c:if>
</table>
<%--如果购物车非空才输出页面的内容--%>
<c:if test="${not empty sessionScope.cart.items}">
<div class="cart_info">
<span class="cart_span">购物车中共有<span
class="b_count">${sessionScope.cart.totalCount}</span>件商品</span>
<span class="cart_span">总金额<span
class="b_price">${sessionScope.cart.totalPrice}</span>元</span>
<span class="cart_span"><a href="#">清空购物车</a></span>
<span class="cart_span"><a href="pages/cart/checkout.jsp">去结账</a></span>
</div>
</c:if>
</div>
<%--静态包含页脚内容--%>
<%@include file="/pages/common/footer.jsp"%>
</body>
2.删除购物车商品项:
CartServlet 程序:
/**
* 删除商品项
* @param request
* @param response
* @throws ServletException
* @throws IOException
*/
protected void deleteItem(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
//获取请求参数,商品编号
int id = WebUtils.parseInt(request.getParameter("id"),0);
//从session中取到购物车对象
Cart cart = (Cart) request.getSession().getAttribute("cart");
//有购物车
if (cart != null){
//删除商品信息
cart.deleteItem(id);
//重定向回到购物车页面
response.sendRedirect(request.getHeader("Referer"));
}
}
购物车/pages/cart/cart.jsp 页面的代码:
删除的请求地址:
<td><a class="deleteItem" href="cartServlet?action=deleteItem&id=${entry.value.id}">删除</a></td>
删除的确认提示操作:
// 给 【删除】绑定单击事件
$("a.deleteItem").click(function () {
return confirm("你确定要删除【" + $(this).parent().parent().find("td:first").text() +"】吗?")
});
3.清空购物车:
CartServlet 程序:
/**
* 清空购物车
* @param request
* @param response
* @throws ServletException
* @throws IOException
*/
protected void clear(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
//获取购物车对象
Cart cart = (Cart) request.getSession().getAttribute("cart");
//清空购物车
if (cart != null){
cart.clear();
//重定向回到购物车页面
response.sendRedirect(request.getHeader("Referer"));
}
}
cart.jsp 页面的内容 给清空购物车添加请求地址,和添加 id 属性:
<span class="cart_span"><a id="clearCart" href="cartServlet?action=clear">清空购物车</a></span>
清空的确认提示操作:
// 给清空购物车绑定单击事件
$("#clearCart").click(function () {
return confirm("你确定要清空购物车吗?");
})
4.修改购物车商品数量:
CartServlet 程序:
/**
* 修改商品数量
* @param request
* @param response
* @throws ServletException
* @throws IOException
*/
protected void updateCount(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
//获取请求参数
int id = WebUtils.parseInt(request.getParameter("id"),0);
int count = WebUtils.parseInt(request.getParameter("count"),1);
//获取cart购物车对象
Cart cart = (Cart) request.getSession().getAttribute("cart");
//修改商品数量
if (cart != null) {
cart.updateCount(id, count);
//请求重定向
response.sendRedirect(request.getHeader("Referer"));
}
}
修改 pages/cart/cart.jsp 购物车页面:
<td>
<input class="updateCount" style="width: 80px;"
bookId="${entry.value.id}"
type="text" value="${entry.value.count}">
</td>
修改商品数量 js 代码:
// 给输入框绑定 onchange内容发生改变事件
$(".updateCount").change(function () {
// 获取商品名称
var name = $(this).parent().parent().find("td:first").text();
//attr 设置和获取指定元素的值
var id = $(this).attr('bookId');
// 获取商品数量
var count = this.value;
if ( confirm("你确定要将【" + name + "】商品修改数量为:" + count + " 吗?") ) {
//发起请求。给服务器保存修改
location.href = "http://localhost:8080/book/cartServlet?action=updateCount&count="+count+"&id="+id;
} else {
// defaultValue属性是表单项Dom对象的属性。它表示默认的value属性值。
this.value = this.defaultValue;
}
5.首页,购物车数据回显:
在addItem方法中,添加商品到购物车的时候,保存最后一个添加的商品名称,保存到session中:
// 最后一个添加的商品名称
request.getSession().setAttribute("lastName", cartItem.getName());
在 pages/client/index.jsp 页面中输出购物车信息:
<div style="text-align: center">
<c:if test="${empty sessionScope.cart.items}">
<%--购物车为空的输出--%>
<span> </span>
<div>
<span style="color: red">当前购物车为空</span>
</div>
</c:if>
<c:if test="${not empty sessionScope.cart.items}">
<%--购物车非空的输出--%>
<span>您的购物车中有 ${sessionScope.cart.totalCount} 件商品</span>
<div>
您刚刚将<span style="color: red">${sessionScope.lastName}</span>加入到了购物车中
</div>
</c:if>
</div>