cgb2010-京淘我的项目Day19
- 用户登录实现
==========
1.1 传统登录存在的问题
问题阐明: 依照如下的形式进行设计,用户须要在不同的服务器中进行屡次登录操作.用户体验较差.
1.2 登录操作优化
常识铺垫:
Session: 在一个会话内,能够实现数据的共享 范畴大 公共的共享数据个别会保留到Session中.
Request: 在一个申请内,实现数据的共享. 范畴小
上述的对象都是服务器端对象. 保留在服务器中. 如果服务器变动了,或者敞开/宕机了 则对象全副生效.
Cookie: Cookie是在客户端**实现数据共享**的一种机制, 同时能够保留服务器端传回来的数据(**业务须要**)
token策略: 动静生成一个密钥.
1.3 SSO单点登录设计
1.3.1 SSO介绍
单点登录(SingleSignOn,SSO),就是通过用户的一次性甄别登录。当用户在身份认证服务器上登录一次当前,即可取得拜访单点登录零碎中其余关联系统和应用软件的权限,同时这种实现是不须要管理员对用户的登录状态或其余信息进行批改的,这意味着在多个利用零碎中,用户只需一次登录就能够拜访所有相互信任的利用零碎。这种形式缩小了由登录产生的工夫耗费,辅助了用户治理,是目前比拟风行的 [1]
1.3.2 单点登录业务流程
1.3.3 页面剖析
1.url剖析
2.参数阐明
3.页面JS
1.3.4 编辑UserController
`/** * * 业务需要: 实现用户登录操作 * URL地址: http://www.jt.com/user/doLogin?r=0.6659570464851978 * 申请参数: 用户名和明码 * 返回值: SysResult对象 * * 知识点解说: * Cookie: 在客户端保留服务器数据,在客户端实现数据共享. * cookie.setMaxAge(); cookie生命周期 * cookie.setMaxAge(0); 立刻删除cookie * cookie.setMaxAge(100); 设定100秒有效期 100秒之后主动删除 * cookie.setMaxAge(-1); 敞开会话后删除 * 2.设定path cookie的权限设定 * cookie.setPath("/") 个别条件下设定为/ 通用 * 权限:根目录及其子目录无效 * cookie.setPath("/user") * 权限:/user目录下无效 * 3.设定Cookie资源共享 * cookie特点: 本人的域名下,只能看到本人的Cookie. 默认条件下不能共享的 * cookie.setDomain("jt.com"); 只有在xxx.jt.com的域名中实现数据共享 */ @RequestMapping("/doLogin") @ResponseBody public SysResult doLogin(User user, HttpServletResponse response){ String ticket = dubboUserService.doLogin(user); if(!StringUtils.hasLength(ticket)){ return SysResult.fail(); } Cookie cookie = new Cookie("JT_TICKET", ticket); cookie.setMaxAge(7*24*60*60); //设定7天无效 cookie.setPath("/"); //申请在根目录中都能够获取cookie cookie.setDomain("jt.com"); response.addCookie(cookie); return SysResult.success(); }
1.3.5 编辑UserService
`/** * 业务阐明: 实现用户单点登录操作 * 1.依据用户名和明码查询数据库 * @param user * @return */ @Override public String doLogin(User user) {//username/password不为null //密文加密 String md5Pass = DigestUtils.md5DigestAsHex(user.getPassword().getBytes()); user.setPassword(md5Pass); //条件结构器 依据对象中不为null的属性充当where条件 查问的是user的全副信息 User userDB = userMapper.selectOne(new QueryWrapper(user)); String ticket = null; if(userDB !=null){ //用户名和明码正确 ticket = UUID.randomUUID().toString().replace("-", ""); //数据安全性 没有方法失去保障 要对敏感数据进行脱敏解决 userDB.setPassword("123456你猜猜?"); String json = ObjectMapperUtil.toJSON(userDB); jedisCluster.setex(ticket, 7*24*60*60, json); } return ticket; }
1.3.6 页面成果展示
1.4 用户信息回显
1.4.1 业务阐明
如果用户登录之后,应该在零碎首页中展示用户名称.
实现思路:
1.跨域实现 Ajax申请 依据密钥信息动静获取用户信息.
2.httpClient形式实现. 调用层级较多.
3.利用Dubbo框架实现.
1.4.2 页面剖析
1.页面URL剖析
2.页面JS剖析
1.4.3编辑JT-SSO Controller
`/** * 业务需要: * 依据用户ticket信息,查问用户信息 * 1.url地址:http://sso.jt.com/user/query/8d5fc189ccde43f7a6b6bf4aecd9eb0e?callback=jsonp1613793443098&_=1613793443147 * 2.申请参数: ticket信息 * 3.返回值后果:SysResult对象 * 留神: JSONP形式进行跨域申请. callback(JSON) */ @RequestMapping("/query/{ticket}") public JSONPObject findUserByTicket(String callback,@PathVariable String ticket){ //利用ticket从redis中动静获取数据 String json = jedisCluster.get(ticket); //User user = ObjectMapperUtil.toObj(json, User.class); if(StringUtils.hasLength(json)){ return new JSONPObject(callback, SysResult.success(json)); } return new JSONPObject(callback,SysResult.fail()); }
1.5 封装CookieUtil API
`package com.jt.util;import javax.servlet.http.Cookie;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;//工具API 次要负责 新增cookie 删除cookie 依据key获取cookie 获取cookie的值public class CookieUtil { public static void addCookie(HttpServletResponse response,String name, String value, int maxAge, String path, String domain){ Cookie cookie = new Cookie(name, value); cookie.setMaxAge(maxAge); cookie.setPath(path); cookie.setDomain(domain); response.addCookie(cookie); } public static void delCookie(HttpServletResponse response,String name,String path, String domain){ addCookie(response, name, "", 0, path, domain); } public static Cookie getCookie(HttpServletRequest request,String name){ Cookie[] cookies = request.getCookies(); if(cookies !=null && cookies.length >0){ for (Cookie cookie : cookies){ if(name.equals(cookie.getName())){ return cookie; } } } return null; } public static String getCookieValue(HttpServletRequest request,String name){ Cookie cookie = getCookie(request, name); return cookie==null?null:cookie.getValue(); }}
1.5 用户退出操作
1.5.1 业务阐明
当用户点击退出按钮时,应该重定向到零碎首页. 应该删除Cookie 删除Redis中的数据…
1.5.2 编辑UserController
`/** * 实现用户退出操作 * url地址: http://www.jt.com/user/logout.html * 返回值: 重定向到零碎首页 */ @RequestMapping("/logout") public String logout(HttpServletRequest request,HttpServletResponse response){ String ticket = CookieUtil.getCookieValue(request, "JT_TICKET"); if(StringUtils.hasLength(ticket)){ //删除redis jedisCluster.del(ticket); //删除cookie CookieUtil.delCookie(response, "JT_TICKET", "/", "jt.com"); } return "redirect:/"; //代表缺省值 }
2.购物车业务实现
2.1 编辑POJO对象
`@TableName("tb_cart")@Data@Accessors(chain = true)public class Cart extends BasePojo{ @TableId(type = IdType.AUTO) private Long id; private Long userId; private Long itemId; private String itemTitle; private String itemImage; private Long itemPrice; private Integer num;}
2.2 创立购物车我的项目
2.2.1 增加继承/依赖/插件
`<dependencies> <dependency> <groupId>com.jt</groupId> <artifactId>jt-common</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies> <!--增加插件 有main办法时 须要增加插件--> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <excludes> <exclude> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </exclude> </excludes> </configuration> </plugin> </plugins> </build>
2.2.2 业务构造
2.3 展示购物车列表
2.3.1 业务阐明
阐明: 当用户点击购物车按钮时,应该跳转到购物车展示页面cart.jsp
2.3.2 编辑CartController
`package com.jt.controller;import com.alibaba.dubbo.config.annotation.Reference;import com.jt.pojo.Cart;import com.jt.service.DubboCartService;import org.springframework.stereotype.Controller;import org.springframework.ui.Model;import org.springframework.web.bind.annotation.RequestMapping;import java.util.List;@Controller@RequestMapping("/cart")public class CartController { @Reference(check = false) private DubboCartService cartService; /** * 展示购物车列表信息 依据userId查问购物车记录 * url: http://www.jt.com/cart/show.html * 参数: 依据userId查问购物车数据信息 * 返回值: 购物车展示页面 * 页面取值形式: ${cartList} */ @RequestMapping("/show") public String show(Model model){ long userId = 7L; //临时写死 前期保护 List<Cart> cartList = cartService.findCartListByUserId(userId); //利用model对象将数据保留到request对象中 model.addAttribute("cartList",cartList); return "cart"; }}
2.3.2 编辑CartService
`package com.jt.service;import com.alibaba.dubbo.config.annotation.Service;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;import com.jt.mapper.CartMapper;import com.jt.pojo.Cart;import org.springframework.beans.factory.annotation.Autowired;import java.util.List;@Service(timeout = 3000)public class DubboCartServiceImpl implements DubboCartService{ @Autowired private CartMapper cartMapper; @Override public List<Cart> findCartListByUserId(long userId) { QueryWrapper queryWrapper = new QueryWrapper(); queryWrapper.eq("user_id", userId); return cartMapper.selectList(queryWrapper); }}
2.4 购物车新增操作
2.4.1 页面剖析
1.页面构造剖析
2.退出购物车按钮
`<a class="btn-append " id="InitCartUrl" onclick="addCart();" clstag="shangpin|keycount|product|initcarturl">退出购物车<b></b></a>
3.点击事件
`//利用post传值 function addCart(){ var url = "http://www.jt.com/cart/add/${item.id}.html"; document.forms[0].action = url; //js设置提交链接 document.forms[0].submit(); //js表单提交 }
4.form表单剖析
`<form id="cartForm" method="post"> <input class="text" id="buy-num" name="num" value="1" onkeyup="setAmount.modify('#buy-num');"/> <input type="hidden" class="text" name="itemTitle" value="${item.title }"/> <input type="hidden" class="text" name="itemImage" value="${item.images[0]}"/> <input type="hidden" class="text" name="itemPrice" value="${item.price}"/> </form>
2.4.2 编辑CartController
`/** * 业务阐明: 实现用户购物车数据新增 * url: http://www.jt.com/cart/add/1474391990.html * 参数: 购物车表单提交 Cart对象 * 返回值: 重定向到跳转到购物车展示页面 * * 扩大内容: 如果restFul的参数,与对象的属性名称统一,则能够间接赋值 */ @RequestMapping("/add/{itemId}") public String addCart(Cart cart){ long userId = 7; cart.setUserId(userId); cartService.addCart(cart); return "redirect:/cart/show.html"; }
2.4.3 编辑CartService
/** * 难点: 用户如果反复加购? 只做数量的更新 * 业务操作: * 1.依据userId/itemId查询数据库查看是否加购. * 有值: 曾经加购 则只更新数量 * 没有值: 第一次加购 则间接入库即可. * @param cart */ @Override public void addCart(Cart cart) { QueryWrapper<Cart> queryWrapper = new QueryWrapper<>(); queryWrapper.eq("user_id", cart.getUserId()); queryWrapper.eq("item_id", cart.getItemId()); Cart cartDB = cartMapper.selectOne(queryWrapper); if(cartDB == null){ //阐明用户第一次加购 cartMapper.insert(cart); }else{ //阐明用户之间增加过该商品 数量的更新 int num = cart.getNum() + cartDB.getNum(); Cart temp = new Cart(); temp.setNum(num).setId(cartDB.getId()); //依据id 更新对象中不为null的数据... cartMapper.updateById(temp); } }