用户登录实现
用户登录要求
如果用户在www.jt.com中完成完成了登录操作.则用户在其他的京淘项目的子系统中都可以实现免密登录.
SSO介绍
单点登录(SingleSignOn,SSO),就是通过用户的一次性鉴别登录。当用户在身份认证服务器上登录一次以后,即可获得访问单点登录系统中其他关联系统和应用软件的权限,同时这种实现是不需要管理员对用户的登录状态或其他信息进行修改的,这意味着在多个应用系统中,用户只需一次登录就可以访问所有相互信任的应用系统。这种方式减少了由登录产生的时间消耗,辅助了用户管理,是目前比较流行的 [1]
实现步骤:
1.当用户输入用户名和密码之后点击提交按钮.将数据传给JT-WEB服务器.
2.JT-WEB利用RPC方式访问JT-SSO 校验数据是否有效.
3.如果校验的数据准备无误,之后将用户信息保存到Redis中. key 使用uuid, value使用userJSON. 并且设定7天超时.
4.JT-SSO 将服务器数据返回给JT-WEB服务器.(如果用户名和密码错误的则返回为null即可).
5.JT-WEB服务器将数据保存到Cookie中.(要求实现Cookie共享).
用户登录页面分析
1).页面URL分析
2).页面参数分析
3).页面JS分析
编辑UserController
/** * 1.url地址:http://www.jt.com/user/doLogin?r=0.04360522021726099 * 2.参数: {username:_username,password:_password}, * 3.返回值结果: SysResult * * 需求1: 将cookie名称为 "JT_TICKET"数据输出到浏览器中,要求7天超时. * 并且实现"jt.com"数据共享 * * Cookie特点: * 1.浏览器中只能查看当前网址下的Cookie信息 * 2.doMain 表示cookie共享的策略 * doMain:www.jd.com 当前的Cookie数据只能在当前域名中使用 * doMain:.jd.com 当前Cookie是共享的可以在域名为jd.com结尾的域名中共享. * * * * */ @RequestMapping("/doLogin") @ResponseBody public SysResult doLogin(User user, HttpServletResponse response){ //完成用户登录操作 之后获取ticket密钥信息 String ticket = userService.doLogin(user); if(StringUtils.isEmpty(ticket)){ //如果为null,则说明用户名或密码有问题 return SysResult.fail(); } //1.创建Cookie对象 Cookie cookie = new Cookie("JT_TICKET",ticket); //2.设定cookie存活的时间 value=-1 当用户关闭会话时,cookie删除 //2.设定cookie存活的时间 value= 0 立即删除cookie //2.设定cookie存活的时间 value= >0 设定cookie超时时间 cookie.setMaxAge(7*24*60*60); //3.在jt.com的域名中实现数据共享. cookie.setDomain("jt.com"); cookie.setPath("/"); //一般情况下都是/ //4.将数据保存到浏览器中 response.addCookie(cookie); return SysResult.success(); }
编辑UserService
/** * 目的: 校验用户信息是否有效并且实现单点登录操作. * 步骤: * 1.校验用户名和密码是否正确(密码明文转化为密文) * 2.查询数据库检查是否有结果 * 3.如果有结果,则动态生成TICKET信息(uuid),将user对象转化为JSON * 4.将数据保存到redis中,并且设定超时时间. * 5.返回当前用户登录的密钥. * * @param user * @return */ @Override public String doLogin(User user) { String passwork = user.getPassword(); String md5Pass = DigestUtils.md5DigestAsHex(passwork.getBytes()); QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper.eq("username", user.getUsername()) .eq("password", md5Pass); //获取的是用户的全部记录 (包含涉密信息) User userDB = userMapper.selectOne(queryWrapper); //校验数据是否有效 if( userDB == null){ //用户名和密码错误 return null; } //如果程序执行到这里,说明用户名和密码正确的. 开启单点登录流程 String ticket = UUID.randomUUID() .toString().replace("-", ""); //脱敏处理 余额/密码/手机号/家庭地址 userDB.setPassword("123456你信不???"); String userJSON = ObjectMapperUtil.toJSON(userDB); //操作时保证原子性 jedisCluster.setex(ticket, 7*24*60*60, userJSON); return ticket; }
实现用户信息回显
业务需求
说明:当用户登录成功之后,需要在顶部实现数据的回显. 根据ticket获取用户用户的JSON信息.
编辑页面JS
var TT = JT = { checkLogin : function(){ //获取指定名称的cookie var _ticket = $.cookie("JT_TICKET"); if(!_ticket){ return ; //如果数据为null则直接return } //当dataType类型为jsonp时,jQuery就会自动在请求链接上增加一个callback的参数 $.ajax({ url : "http://sso.jt.com/user/query/" + _ticket, dataType : "jsonp", type : "GET", success : function(data){ if(data.status == 200){ //把json串转化为js对象 var _data = JSON.parse(data.data); var html =_data.username+",欢迎来到京淘!<a href="http://www.jt.com/user/logout.html" class="link-logout">[退出]</a>"; $("#loginbar").html(html); } } }); } }
编辑JT-SSO中的UserController
/** * 完成回显用户名称 * url地址: http://sso.jt.com/user/query/494fcc9ac98e49bc98baffd6d8fc6802?callback=jsonp1597999688824&_=1597999688872 * 参数: ticket信息 * 返回值: SysResult对象(userJSON) * */ @RequestMapping("/query/{ticket}") public JSONPObject findUserByTicket(@PathVariable String ticket,String callback){ String userJSON = jedisCluster.get(ticket); if(StringUtils.isEmpty(userJSON)){ //ticket有误.返回错误信息即可 return new JSONPObject(callback, SysResult.fail()); }else{ return new JSONPObject(callback, SysResult.success(userJSON)); } }
用户退出操作
/** * 实现用户退出操作 * url:http://www.jt.com/user/logout.html * 返回值: 重定向到系统首页. * 目的: 删除redis. 删除Cookie * 前提: 需要获取cookie的key和value * TICKET 为常量 * private static final String TICKET = null; */ @RequestMapping("/logout") public String logout(HttpServletRequest request,HttpServletResponse response){ String jtTicket = null; //1.如何获取cookie中的数据? Cookie[] cookies = request.getCookies(); //2.校验Cookie数据是否为null if(cookies !=null && cookies.length>0){ for(Cookie cookie : cookies){ if(TICKET.equalsIgnoreCase(cookie.getName())){ jtTicket = cookie.getValue(); //业务需要提前删除Cookie cookie.setMaxAge(0); cookie.setPath("/"); cookie.setDomain("jt.com"); response.addCookie(cookie); break; } } } //2.校验数据是否有效 if(!StringUtils.isEmpty(jtTicket)){ //如果数据不为null,则开始执行退出操作. jedisCluster.del(jtTicket); //根据key,删除Redis中的记录 //删除cookie. } return "redirect:/"; }
编辑工具类
package com.jt.util; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class CookieUtil { //1.新增cookie public static void addCookie(String name, String value, String path, String domain, int maxAge, HttpServletResponse response){ //校验自己完成 Cookie cookie = new Cookie(name,value); cookie.setPath(path); cookie.setDomain(domain); cookie.setMaxAge(maxAge); response.addCookie(cookie); } //2.删除Cookie 0 -1 用枚举类型优化一下 public static void deleteCookie(String name, String path, String domain, HttpServletResponse response){ //校验自己完成 Cookie cookie = new Cookie(name,""); cookie.setPath(path); cookie.setDomain(domain); cookie.setMaxAge(0); //后期维护使用枚举 response.addCookie(cookie); } //3.根据Cookie的name属性获取Cookie对象 public static Cookie getCookieByName(HttpServletRequest request,String name){ Cookie[] cookies = request.getCookies(); if(cookies !=null && cookies.length>0){ for(Cookie cookie : cookies){ if(name.equalsIgnoreCase(cookie.getName())){ return cookie; } } } return null; } }
商品详情展现业务说明
package com.jt.controller; import com.alibaba.dubbo.config.annotation.Reference; import com.jt.pojo.Item; import com.jt.pojo.ItemDesc; import com.jt.service.DubboItemService; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; //需要跳转页面 @Controller public class ItemController { @Reference private DubboItemService itemService; /** * 知识点:1.mvc页面跳转机制 2.restFul 3.jsp页面取值写法 4.dubbo * 需求:根据商品ID查询商品信息.(item/itemDesc) * url地址:http://www.jt.com/items/562379.html * 参数: 商品id * 页面取值要求: ${item.title } ${itemDesc.itemDesc } * 返回值: 页面逻辑名称 item */ @RequestMapping("/items/{itemId}") public String findItemById(@PathVariable Long itemId, Model model){ //1.远程访问获取商品信息 Item item = itemService.findItemById(itemId); //2.远程访问获取商品描述信息 ItemDesc itemDesc = itemService.findItemDescById(itemId); //3.将数据传到页面中 model.addAttribute("item",item); model.addAttribute("itemDesc",itemDesc); return "item"; } }
package com.jt.web.service; import com.alibaba.dubbo.config.annotation.Service; import com.jt.mapper.ItemDescMapper; import com.jt.mapper.ItemMapper; import com.jt.pojo.Item; import com.jt.pojo.ItemDesc; import com.jt.service.DubboItemService; import org.springframework.beans.factory.annotation.Autowired; @Service public class DubboItemServiceImpl implements DubboItemService { @Autowired private ItemMapper itemMapper; @Autowired private ItemDescMapper itemDescMapper; @Override @CacheFind(key = "ITEM_ID") public Item findItemById(Long itemId) { return itemMapper.selectById(itemId); } @Override @CacheFind(key = "ITEM_DESC_ID") public ItemDesc findItemDescById(Long itemId) { return itemDescMapper.selectById(itemId); } }
实现购物车
编辑Cart 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; }