1 课程计划
昨天:
单点登录系统
1、创建工程
2、可以被多种客户端调用,门户系统、手机app、微信商城。
3、实现接口
a) 数据校验接口
b) 注册接口
c) 登录接口
d) 根据token查询用户接口
e) 安全退出(作业)
今天的内容:
1、登录、注册功能的实现。
2、门户系统整合登录、注册功能
3、当用户下订单时需要用户登录,使用拦截器实现用户登录。登录成功后跳转到用户要访问的页面。
4、购物车的实现
a) 添加商品
b) 删除商品
c) 修改商品数量
2 注册功能的实现
登录和注册的功能都放到单点登录系统中完成,供其他系统调用。
2.1 向sso系统中添加页面
需要对静态资源做映射。需要修改springmvc.xml
2.2 注册功能实现
1、进行注册之前先进行数据的有效性验证。
a) 用户名不能重复
b) 确认密码和密码文本框的内容要一致。
c) 用户名、密码不能为空。
d) 手机不能为空 并且不能重复。
2、校验完成后注册。可以调用sso系统的注册接口完成注册。
2.2.1 打开注册页面
@Controller @RequestMapping("/page") public class PageController {
@RequestMapping("/register") public String showRegister() { return"register"; } } |
2.2.2 数据校验
2.2.3 提交注册
3 登录功能的实现
3.1.1 打开登录页面
使用一个Controller跳转到登录页面。
@RequestMapping("/login") public String showLogin() { return "login"; } |
3.1.2 数据校验
校验用户名密码必须输入。
3.1.3 用户登录
用户点击登录按钮把用户名和密码表单提交给登录接口,接收返回结果判断是否登录成功。
3.1.4 登录页面回调url
回调url应该是通过一个参数传递给显示登录页面的Controller。参数名为:redirect
需要把回调的url传递给jsp页面。当登录成功后,js的逻辑中判断是否有回调的rul,如果有就跳转到此url,如果没有就跳转到商城首页。
4 使用拦截器实现用户登录
4.1 门户系统整合sso
在门户系统点击登录连接跳转到登录页面。登录成功后,跳转到门户系统的首页,在门户系统中需要从cookie中 把token取出来。所以必须在登录成功后把token写入cookie。并且cookie的值必须在系统之间能共享。
4.1.1 Cookie共享:
1、Domain:必须是相同的。
例如有多个域名:
Sso.taotao.com
Search.taotao.com
需要设置domain为:.taotao.com
2、设置path:/
4.1.2 工具类
如果是localhost不要设置domain。直接设置path就可以了。
工具类可以放到taotao-common中。
4.1.3 在登录接口中添加写cookie的逻辑
4.1.4 首页取cookie信息
从cookie中取token,在页面中根据token取用户信息,调用sso系统的服务来完成。需要使用jsonp调用。
登录成功:
4.2 模拟拦截url
需求:当访问商品详情页面时强制用户登录。(当有订单系统后就改为订单系统的url。)
4.2.1 创建拦截器
1、需要实现HandlerInterceptor接口。
2、实现拦截逻辑
3、需要在springmvc.xml中配置。
4.2.2 用户登录Service
功能:根据token换取用户信息,需要调用sso系统的服务。返回TbUser对象。如果没有就返回null。
@Service public class UserServiceImpl implements UserService {
@Value("${SSO_BASE_URL}") private String SSO_BASE_URL; @Value("${SSO_USER_TOKEN}") private String SSO_USER_TOKEN;
@Override public TbUser getUserByToken(String token) { try { //调用sso系统的服务,根据token取用户信息 String json = HttpClientUtil.doGet(SSO_BASE_URL + SSO_USER_TOKEN + token); //把json转换成TaotaoREsult TaotaoResult result = TaotaoResult.formatToPojo(json, TbUser.class); if (result.getStatus() == 200) { TbUser user = (TbUser) result.getData(); return user; }
} catch (Exception e) { e.printStackTrace(); } return null; }
} |
4.2.3 拦截器实现
public class LoginInterceptor implements HandlerInterceptor {
@Autowired private UserServiceImpl userService;
@Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { //在Handler执行之前处理 //判断用户是否登录 //从cookie中取token String token = CookieUtils.getCookieValue(request, "TT_TOKEN"); //根据token换取用户信息,调用sso系统的接口。 TbUser user = userService.getUserByToken(token); //取不到用户信息 if (null == user) { //跳转到登录页面,把用户请求的url作为参数传递给登录页面。 response.sendRedirect(userService.SSO_BASE_URL + userService.SSO_PAGE_LOGIN + "?redirect=" + request.getRequestURL()); //返回false return false; } //取到用户信息,放行 //返回值决定handler是否执行。true:执行,false:不执行。 return true; }
@Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { // handler执行之后,返回ModelAndView之前
}
@Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { // 返回ModelAndView之后。 //响应用户之后。
}
} |
4.2.4 Springmvc.xml
<!-- 拦截器配置 --> <mvc:interceptors> <mvc:interceptor> <!-- 拦截订单类请求 --> <mvc:mapping path="/item/**"/> <bean class="com.taotao.portal.interceptor.LoginInterceptor"/> </mvc:interceptor> </mvc:interceptors> |
5 购物车的实现
5.1 分析
淘淘:购物车在用户不登陆的情况下也可以使用购物车。需要把购物车的商品信息写入cookie中。所有对购物车的操作都是操作cookie。有效 的降低数据库的压力。
缺点:换一台电脑后购物车的商品不能同步。
实现的工程:taotao-protal中实现购物车功能。只需要调用商品信息的服务,除此之外不需要和其他系统交互。
5.2 添加购物车商品
5.2.1 流程分析:
在商品详情页面点击“加入购物车”按钮提交一个请求吧商品id传递给Controller,Controller接收id,Controller调用Service根据商品id查询商品基本信息。把商品写入cookie中,加入cookie之前先从cookie中把购物车的商品取出来判断当前购物车商品列表中是否有此商品,如果有数量加一,如果没有添加一个商品,数量为1。展示给用户购物车列表。
5.2.2 Service层实现
功能:接收一个商品id,数量(默认为1),根据商品id查询商品信息。调用taotao-rest的服务。把商品添加到购物车,先把购物车商品列表取出来,判断列表中是否有此商品,如果有就增加数量就可以了。如果没有把此商品添加到商品列表。返回添加成功Taotaoresult。
创建购物车商品POJO:
public class CartItem {
private long id; private String title; private Integer num; private long price; private String image; } |
@Service public class CartServiceImpl implements CartService{
@Value("${REST_BASE_URL}") private String REST_BASE_URL; @Value("${ITME_INFO_URL}") private String ITME_INFO_URL;
/** * 添加购物车商品 * <p>Title: addCartItem</p> * <p>Description: </p> * @param itemId * @param num * @return * @see com.taotao.portal.service.CartService#addCartItem(long, int) */ @Override public TaotaoResult addCartItem(long itemId, int num, HttpServletRequest request, HttpServletResponse response) { //取商品信息 CartItem cartItem = null; //取购物车商品列表 List<CartItem> itemList = getCartItemList(request); //判断购物车商品列表中是否存在此商品 for (CartItem cItem : itemList) { //如果存在此商品 if (cItem.getId() == itemId) { //增加商品数量 cItem.setNum(cItem.getNum() + num); cartItem = cItem; break; } } if (cartItem == null) { cartItem = new CartItem(); //根据商品id查询商品基本信息。 String json = HttpClientUtil.doGet(REST_BASE_URL + ITME_INFO_URL + itemId); //把json转换成java对象 TaotaoResult taotaoResult = TaotaoResult.formatToPojo(json, TbItem.class); if (taotaoResult.getStatus() == 200) { TbItem item = (TbItem) taotaoResult.getData(); cartItem.setId(item.getId()); cartItem.setTitle(item.getTitle()); cartItem.setImage(item.getImage() == null ? "":item.getImage().split(",")[0]); cartItem.setNum(num); cartItem.setPrice(item.getPrice()); } //添加到购物车列表 itemList.add(cartItem); } //把购物车列表写入cookie CookieUtils.setCookie(request, response, "TT_CART", JsonUtils.objectToJson(itemList), true);
return TaotaoResult.ok(); } /** * 从cookie中取商品列表 * <p>Title: getCartItemList</p> * <p>Description: </p> * @return */ private List<CartItem> getCartItemList(HttpServletRequest request) { //从cookie中取商品列表 String cartJson = CookieUtils.getCookieValue(request, "TT_CART", true); if (cartJson == null) { return new ArrayList<>(); } //把json转换成商品列表 try { List<CartItem> list = JsonUtils.jsonToList(cartJson, CartItem.class); return list; } catch (Exception e) { e.printStackTrace(); } return new ArrayList<>(); }
} |
5.2.3 Controller
接收商品的id和商品的数量,商品数量如果为空默认为1.调用Service向购物车添加商品。
展示添加成功页面。
请求的url:http://localhost:8082/cart/add/144141542125084.html
@Controller @RequestMapping("/cart") public class CartController {
@Autowired private CartService cartService;
@RequestMapping("/add/{itemId}") public String addCartItem(@PathVariable Long itemId, @RequestParam(defaultValue="1")Integer num, HttpServletRequest request, HttpServletResponse response) { TaotaoResult result = cartService.addCartItem(itemId, num, request, response); return "cartSuccess"; }
} |
5.3 展示购物车列表
需要展示购物车页面,没有参数,只需要从cookie中把商品列表取出来,展示到页面即可。
5.3.1 Service
从cookie中把购物车列表取出来,没有参数,返回购物车中的商品列表。
@Override public List<CartItem> getCartItemList(HttpServletRequest request, HttpServletResponse response) { List<CartItem> itemList = getCartItemList(request); return itemList; } |
5.3.2 Controller
调用Service取购物车商品列表,把购物车商品传递给jsp。在购物车页面展示商品列表。
请求的url:http://localhost:8082/cart/cart.html
返回jsp页面:cart.jsp
@RequestMapping("/cart") public String showCart(HttpServletRequest request, HttpServletResponse response, Model model) { List<CartItem> list = cartService.getCartItemList(request, response); model.addAttribute("cartList", list); return "cart"; } |
5.4 购物车商品数量修改
作业:直接修改购物车数量。
响应修改商品数量的事件:
5.5 删除购物车商品
在购物车页面中点击删除连接,接收要删除的商品id,从cookie中把商品找到删除,重新写入cookie,重新展示购物车页面。
5.5.1 Service
/** * 删除购物车商品 * <p>Title: deleteCartItem</p> * <p>Description: </p> * @param itemId * @return * @see com.taotao.portal.service.CartService#deleteCartItem(long) */ @Override public TaotaoResult deleteCartItem(long itemId, HttpServletRequest request, HttpServletResponse response) { //从cookie中取购物车商品列表 List<CartItem> itemList = getCartItemList(request); //从列表中找到此商品 for (CartItem cartItem : itemList) { if (cartItem.getId() == itemId) { itemList.remove(cartItem); break; }
} //把购物车列表重新写入cookie CookieUtils.setCookie(request, response, "TT_CART", JsonUtils.objectToJson(itemList), true);
return TaotaoResult.ok(); } |
5.5.2 Controller
接收商品id,调用Service删除购物车商品,返回购物车页面。
请求的url:/cart/delete/${cart.id}.html
@RequestMapping("/delete/{itemId}") public String deleteCartItem(@PathVariable Long itemId, HttpServletRequest request, HttpServletResponse response) { cartService.deleteCartItem(itemId, request, response); return"redirect:/cart/cart.html"; } |
6 购物车存在的问题
1、更换设备购物车商品不能同步
a) 不能把购物车商品保存到数据库
b) 要求用户登录才能同步信息
c) 可以把购物车信息保存到redis中,key就是用户,value就是购物车列表
d) 购物车商品合并的问题。
2、提交订单后购物车商品需要清空。