蚊子的项目整合笔记(一) - 用户登录实现

用户登录实现

单点登录原理

需求分析

说明:采用将Session保存到一台服务器的做法,在大型网站构建中,是不合理的.因为Session数据不同的服务器之间不能共享.会导致用户在访问网站时,会有多次的校验.如果该服务器中没有用户信息,则会出现用户重复登录的问题.
在这里插入图片描述
想法:

  1. 只要用户登陆,添加标识符
    url:www.jt.com/items/1222.html?sessionId=11111
    URL重写技术:
  2. IP_hash 1.负载不均服务器宕机直接影响用户的体验

原理介绍

在这里插入图片描述
实现步骤:

  1. 用户输入用户名和密码通过前台服务器进行用户登陆操作.
  2. 当前台接收到用户的登陆请求后,将数据传递到单点登录系统.之后进行校验.
  3. 根据用户名和密码进行数据校验,如果用户名和密码都不正确,直接告知(201)用户用户名或者密码错误.
    如果用户名和密码正确的,则通过加密算法(md5hash)将key进行加密,通过将用户信息转化为JSON数据保存到redis中
  4. 将加密后台的秘钥(TOKEN)返回给用户,将用户token经过前台中转,最终将token数据保存到用户的浏览器的Cookie中.
  5. 登录后需要实现单点登录效果

单点登录介绍

单点登录(Single Sign On),简称为 SSO,是目前比较流行的企业业务整合的解决方案之一。SSO的定义是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。

单点登录实现

页面分析

1.页面的url
在这里插入图片描述
2.分析页面js
在这里插入图片描述

接口文档定义

1.2.2 接口文档定义

请求方法 POST
URL http://sso.jt.com/user/login
参数 u 用户名 ,p 密码

示例 http://sso.jt.com/user/login
u: chenchen
p: 123456

返回值 {
status: 200
msg: “OK”
data:” e10adc3949ba59abbe56e057f20f883e” //登录成功,返回ticket
}
备注 登录完成,返回ticket,前台系统写入cookie
ticket算法 唯一标识每个用户:动态唯一
安全:混淆md5加密
md5(“JT_TICKET_” + System.currentTime + username)

编辑前台Controller

/**
	 * 实现用户登陆操作
	 * @param user
	 * @return
	 * 核心:将用户token秘钥写入浏览器cookie中
	 * 
	 * cookie.setMaxAge(3600 * 24 * 7);  //7天超时
	 * cookie.setMaxAge(0); 			 //立即删除
	 * cookie.setMaxAge(-1); 			 //当前会话关闭后删除
	 * 
	 */
	@RequestMapping("/doLogin")
	@ResponseBody	
	Public SysResult findUserByUP(User user, HttpServletResponse response){
		try {
			String token = userService.findUserByUP(user);
			if(StringUtils.isEmpty(token)){
				returnSysResult.build(201,"用户名或密码错误");
			}
			//如果程序执行到这里表示token一定不为null,将数据写入cookie
			Cookie cookie = new Cookie("JT_TICKET", token);
			cookie.setMaxAge(3600 * 24 * 7);  //7天超时
			cookie.setPath("/");			//表示cookie的所有者
			response.addCookie(cookie);       //将cookie写入浏览器
			return SysResult.oK();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return SysResult.build(201,"用户名和密码错误");
	}

编辑前台Service

/**
	 * 1.定义url
	 * 2.封装参数  Map
	 * 3.发起请求之后校验用户返回值是否正确
	 * 	获取token数据
	 * 4.将正确的数据返回
	 */
	@Override
	public String findUserByUP(User user) {
		String token = null;
		String url = "http://sso.jt.com/user/login";
		Map<String,String> params = new HashMap<>();
		params.put("username",user.getUsername());
		//根据需求实现应该加密后传输需求不明多问???
		String md5Pass = DigestUtils.md5Hex(user.getPassword());
		params.put("password", md5Pass);
		//发起http请求
		String resultJSON = httpClient.doPost(url, params);
		try {
			SysResult sysResult = objectMapper.readValue(resultJSON,SysResult.class);
			if(sysResult.getStatus() == 200){
				token = (String) sysResult.getData();
			}
		} catch (Exception e) {
			e.printStackTrace();
			thrownewRuntimeException();
		}
		return token;
	}

编辑后台Controller

@RequestMapping("/login")
	@ResponseBody
	Public SysResult findUserByUP(User user){
		try {
			String token = userService.findUserByUP(user);
			if(!StringUtils.isEmpty(token))
				return SysResult.oK(token);
		} catch (Exception e) {
			e.printStackTrace();
		}
		Return SysResult.build(201,"用户名或密码错误");
	}

编辑后台Service

/**
	 * 1.根据用户名和密码查询数据库
	 * 	如果结果为null  表示用户名或密码错误 throw 
	 * 2.如果用户信息不为null
	 * 	 1.生成加密token
	 * 	 2.将user对象转化为JSON
	 * 	 3.将数据token:json存入redis集群中
	 * 	 4.将token数据返回
	 */
	@Override
	public String findUserByUP(User user) {
		User userDB = userMapper.findUserByUP(user);
		if(userDB == null){
			throw new RuntimeException();
		}
		String token = DigestUtils.md5Hex("JT_TICKET_"+System.currentTimeMillis()+user.getUsername());
		try {
			String userJSON = objectMapper.writeValueAsString(userDB);
			jedisCluster.setex(token,3600 * 7 * 24,userJSON);
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException();
		}
		return token;
	}

登录测试

在这里插入图片描述

用户信息回显

页面分析

  1. 说明:用户信息回显时根据token数据进行数据的回显.
    在这里插入图片描述
  2. 页面JS在这里插入图片描述

编辑后台Controller

	//根据token数据查询用户信息
	@RequestMapping("/query/{token}")
	@ResponseBody
	public MappingJacksonValue findUserByToken(
			@PathVariable String token,Stringcallback){
		String userJSON = jedisCluster.get(token);
		MappingJacksonValue jacksonValue = null;
		if(StringUtils.isEmpty(userJSON)){
			//如果缓存数据没有则直接201返回即可
			jacksonValue =  newMappingJacksonValue(SysResult.build(201,"用户查询失败"));
		}else{
			jacksonValue =  newMappingJacksonValue(SysResult.oK(userJSON));
		}
		jacksonValue.setJsonpFunction(callback);
		return jacksonValue;
	}

登录面试题

  1. 如何保证用户登陆顺延7天.
    当用户根据token检索数据时,重新设定超时时间7天
    如何需要修改cookie数据,应该从前台Controller-后台Controller
  2. 如何保证同时只有一个用户登陆.
    用户mac信息http请求能否获取???不可以!!
    通过硬件设备获取用户mac交换机/路由器路由器名称一定要隐藏
    设定mac拦截
    输入mac
    获取用户IP地址可以将IP当做一个校验的规则进行判断.
    Md5(Username):IP|userJSON

实现用户登出

思考

实现用户登出操作,需要将浏览器中的cookie删除/删除redis中缓存数据.

登出的实现

/**
	 * 1.通过JT_TICKET或者token数据
	 * 2.根据token删除redis缓存数据
	 * 3.将cookie删除
	 * @return
	 */
	@RequestMapping("/logout")
	public String logout(HttpServletRequest request,HttpServletResponse response){
		//1.如何获取JT_TICKET值???
		Cookie[] cookies = request.getCookies();
		String token = null;
		for (Cookie cookie :cookies) {
			if("JT_TICKET".equals(cookie.getName())){
				token = cookie.getValue();
				break;
			}
		}
		if(!StringUtils.isEmpty(token)){
			//表示token数据不为null
			jedisCluster.del(token);
			//删除cookie
			Cookie cookie = new Cookie("JT_TICKET","");
			cookie.setPath("/");
			cookie.setMaxAge(0);
			response.addCookie(cookie);
		}
		//重定向到商城主页
		return"redirect:/index.html";
	}

实现购物车

创建项目

根据jt-sso中的配置文件创建jt-cart

  1. 项目创建
    在这里插入图片描述
  2. 配置nginx
server {
		listen		80;
		server_name  cart.jt.com;

		location / {
			proxy_pass http://localhost:8094;
			proxy_connect_timeout       3;  
			proxy_read_timeout          3;  
			proxy_send_timeout          3; 
		}
	}

导入配置文件

  1. 修改web.xml在这里插入图片描述
  2. 修改Spring配置文件
    在这里插入图片描述
  3. 修改Mycat配置文件
    在这里插入图片描述
  4. 修改映射文件路径
    在这里插入图片描述

编辑POJO对象

在这里插入图片描述

实现购物车展现

实现购物车页面跳转

url:http://www.jt.com/cart/show.html

接口文档定义

请求方法 GET
URL http://cart.jt.com/cart/query/{userId}
参数 userId用户ID
示例 http://cart.jt.com/cart/query/1

返回值 {
status: 200 //200 成功,201 没有查到
msg: “OK” //返回信息消息
data:
[1]
0:
{
created: 1418092628000
updated: 1418092628000
id:1
userId:1
itemId:39
itemTitle: “java核心技术”
itemImage: http://image.jt.com/images/2015/06/11/20150309118.jpg
itemPrice: 87200
num:1
}
}

编辑后台Controller

@Controller
@RequestMapping("/cart")
public class CartController {
	
	@Autowired
	private CartService cartService;
	
	//根据用户Id查询购物车信息
	@RequestMapping("/query/{userId}")
	@ResponseBody
	public SysResult findCartByUserId(@PathVariable Long userId){
		try {
			List<Cart> cartList = cartService.findCartByUserId(userId);
			return SysResult.oK(cartList);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return SysResult.build(201,"购物车数据查询失败");
	}
}

编辑后台Service

@Service
public class CartServiceImpl implements CartService {
	
	@Autowired
	private CartMapper cartMapper;

	@Override
	public List<Cart> findCartByUserId(Long userId) {
		Cart cart = newCart();
		cart.setUserId(userId);
		return cartMapper.select(cart);
	}
}

后台测试

在这里插入图片描述

编辑前台Controller

//跳转到购物车展现页面
	@RequestMapping("/show")
	public String show(Model model){
		//根据userId查询购物车信息
		Long userId = 7L;
		List<Cart> cartList = cartService.findCartByUserId(userId);
		//将cartList数据保存到request对象中
		model.addAttribute("cartList", cartList);
		return"cart";
	}

编辑前台Service

@Override
	public List<Cart> findCartByUserId(Long userId) {
		String url = "http://cart.jt.com/cart/query/"+userId;
		String resultJSON = httpClient.doGet(url);
		List<Cart> cartList = null;
		try {
			SysResult sysResult = objectMapper.readValue(resultJSON, SysResult.class);
			cartList = (List<Cart>) sysResult.getData();
			
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException();
		}
		return cartList;
	}

新增购物车

页面分析

  1. url地址
    在这里插入图片描述
  2. 页面js
<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>

编辑前台Controller

//实现购物车新增
	/**
	 * http://www.jt.com/cart/add/562379.html
	 */
	@RequestMapping("/add/{itemId}")
	public String saveCart(@PathVariable Long itemId,Cartcart){
		Long userId = 7L;
		cart.setItemId(itemId);
		cart.setUserId(userId);
		cartService.saveCart(cart);
		//重定向到购物车展现页面
		return "redirect:/cart/show.html";
	}

编辑前台Service

/**
	 * 思考:如果一个对象中有100个属性问怎么传参
	 */
	@Override
	public void saveCart(Cart cart) {
		String url = "http://cart.jt.com/cart/save";
		Map<String,String> params = new HashMap<>();
		params.put("userId",cart.getUserId()+"");
		params.put("itemId",cart.getItemId()+"");
		params.put("itemTitle",cart.getItemTitle());
		params.put("itemImage",cart.getItemImage());
		params.put("itemPrice",cart.getItemPrice()+"");
		params.put("num",cart.getNum()+"");
		
		httpClient.doPost(url, params);
		
		//理论上需要对返回值进行处理,如果201报错需要告知用户js暂时没有实现
	}

编辑后台Controller

//实现购物车入库
	@RequestMapping("/save")
	publicSysResultsaveCart(Cart cart){
		try {
			cartService.saveCart(cart);
			returnSysResult.oK();
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		returnSysResult.build(201, "购物车新增失败");
	}

编辑Service

/**
	 * 1.根据userId和itemId判断数据库中是否有该购物信息
	 *  有:
	 *  	应该数据库num + 新的num做更新操作
	 *  没有数据:
	 *  	应该插入数据库
	 */
	@Override
	publicvoidsaveCart(Cart cart) {
		Cart cartDB = cartMapper.findCartByUI(cart);
		if(cartDB == null){
			cart.setCreated(new Date());
			cart.setUpdated(cart.getCreated());
			cartMapper.insert(cart);
		}else{
			intnum = cartDB.getNum() + cart.getNum();
			cartDB.setNum(num);
			cartDB.setUpdated(newDate());
			cartMapper.updateByPrimaryKeySelective(cartDB);
		}
	}

测试

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值