springboot-No8 不用容器解决分布式session

分布式session

如果我们的服务部署到了多台的服务器上面,那么我们的请求可能是落在不同的机器上的

这个时候如何获取到这个用户的session呢,这就要我们的分布式session来解决问题了.


解决分布式session可以采用  现有的容器 来进行这个session的同步。

这里我们就介绍不适用容器我们自己进行分布式session的处理


实现思想

在用户登录系统成功之后呢,给这个用户的session生成一个token,然后用这个token作为key将session存入到redis中

并且将token返回给客户端存储在cookie中

用户之后的请求都将携带这个token,传入给服务器,服务器使用token取出用户的session信息

从而达到分布式session的处理


生成token的方法

使用java的工具类   UUID

   UUID.randomUUID()

这样我们的token有了


登录处理

假设我们的登录信息在这个 LoginVo

LoginVo

public class LoginVo {

	@NotNull
	@Mobile
	private String mobile;

	@NotNull
	@Length(min = 32)
	private String password;

	public String getMobile() {
		return mobile;
	}

	public void setMobile(String mobile) {
		this.mobile = mobile;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	@Override
	public String toString() {
		return "LoginVo [mobile=" + mobile + ", password=" + password + "]";
	}
}

接着我们上节内容,UserService中加入如下的方法。

下面的代码是接续着我前面的spring-boot系列的。


UserService

public boolean login(HttpServletResponse response, LoginVo loginVo);

public User getByToken(HttpServletResponse response, String token) ;

void addCookie(HttpServletResponse response, String token, User user)


addCookie实现

现将 user存入到redis中

然后生成 Cookie

然后将Cookie 加入到response

	public void addCookie(HttpServletResponse response, String token, User user) throws Exception {
		RedisServiceProxy proxy = new RedisServiceProxy(UserKey.TOKEN,this.redisService);
		proxy.setBean(token, user);
		Cookie cookie = new Cookie(ConstValue.COOKI_NAME_TOKEN, token);
		cookie.setMaxAge(UserKey.TOKEN.expireSeconds());
		cookie.setPath("/");
		response.addCookie(cookie);
	}

login实现

判断用户是否存在

用户存在,生成token

调用

addCookie

getByToken实现

使用token从redis中获取到用户信息

如果获取到信息,那么延长cookie的时间


UserService.java

package miaosha.service;

import javax.servlet.http.HttpServletResponse;

import miaosha.dao.domain.User;
import miaosha.vo.login.LoginVo;


public interface UserService {

	public User findUserById(Long id) ;
	
	/**
	 * 根据session的token获取到user
	 * @param response
	 * @param token
	 * @return
	 * @throws Exception 
	 */
	public User getByToken(HttpServletResponse response, String token) throws Exception ;
	
	public int insert(User user);
	
	public boolean login(HttpServletResponse response, LoginVo loginVo) throws Exception ;

	void addCookie(HttpServletResponse response, String token, User user) throws Exception;
}

UserServiceImpl.java

package miaosha.service.impl;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import miaosha.dao.UserDao;
import miaosha.dao.domain.User;
import miaosha.exception.ApplicationException;
import miaosha.redis.key.space.UserKey;
import miaosha.redis.service.RedisService;
import miaosha.redis.service.proxy.RedisServiceProxy;
import miaosha.service.UserService;
import miaosha.util.ConstValue;
import miaosha.util.Md5Util;
import miaosha.util.UUIDUtil;
import miaosha.vo.login.LoginVo;

/**
 * service
 * 
 * @author kaifeng1
 *
 */
@Service("userService")
public class UserServiceImpl implements UserService {

	@Autowired
	private UserDao userDao;

	@Autowired
	RedisService redisService;

	public User findUserById(Long id) {
		return this.userDao.findUserById(id);
	}

	@Transactional
	public int insert(User user) {
		return this.userDao.insert(user);
	}

	/**
	 * 登录系统
	 * @throws Exception 
	 */
	public boolean login(HttpServletResponse response, LoginVo loginVo) throws Exception {
		if (loginVo == null) {
			throw ApplicationException.SERVER_ERROR;
		}
		String mobile = loginVo.getMobile();
		String formPass = loginVo.getPassword();
		User user = this.findUserById(Long.parseLong(mobile));
		if (user == null) {
			throw ApplicationException.MOBILE_NOT_EXIST;
		}
		// 验证密码
		String dbPass = user.getPassword();
		String saltDB = user.getSalt();
		String calcPass = Md5Util.formPassToDBPass(formPass, saltDB);
		if (!calcPass.equals(dbPass)) {
			throw ApplicationException.PASSWORD_ERROR;
		}
		// 生成cookie
		String token = UUIDUtil.uuid();
		addCookie(response, token, user);
		return true;
	}

	/**
	 * 将用户保存在redis中
	 */
	public void addCookie(HttpServletResponse response, String token, User user) throws Exception {
		RedisServiceProxy proxy = new RedisServiceProxy(UserKey.TOKEN,this.redisService);
		proxy.setBean(token, user);
		Cookie cookie = new Cookie(ConstValue.COOKI_NAME_TOKEN, token);
		cookie.setMaxAge(UserKey.TOKEN.expireSeconds());
		cookie.setPath("/");
		response.addCookie(cookie);
	}

	public User getByToken(HttpServletResponse response, String token) throws Exception {
		if (StringUtils.isEmpty(token)) {
			return null;
		}
		RedisServiceProxy proxy = new RedisServiceProxy(UserKey.TOKEN,this.redisService);
		User user = proxy.getBean(token, User.class);
		if (user != null) {
			//延长cookie
			addCookie(response, token, user);
		}
		return user;
	}

}


Controller中获取token

一旦上面的login成功之后,那么cookie就保存到了response中了

下一次的请求来的时候,这个cookie就成了request的内容了

因此我们可以从 request中获取到这个cookie的

我们可以使用  @CookieValue 来获取

可以使用@RequestParam获取


例如下面的方法:

@RequestMapping("/toListOldSample")
	public String toListOldSample(HttpServletResponse response,Model model,@CookieValue(value=ConstValue.COOKI_NAME_TOKEN,required=false) String cookieUUid,@RequestParam(value=ConstValue.COOKI_NAME_TOKEN , required=false) String paramUuid) throws Exception {
		String uuid = null ;
		if(cookieUUid != null ) {
			uuid = cookieUUid ;
		} else if(paramUuid != null) {
			uuid = paramUuid ;
		} else {
			//uuid丢失了重新到登录页
			return "login";
		}
		User user = this.userService.getByToken(response, uuid);
		model.addAttribute("user", user);
		return "goods_list";
	}



上面这种获取的缺陷

使用上面这种获取的方法是可以拿到token的,然后我们可以查询到客户信息的

但是呢在客户登录系统的这段时间内,每一个请求都要求客户的信息的

我们不能给每一个方法都增加上面的那种请求参数吧?

而且上面那个方法内部 是 耦合了 这个 如何通过token获取到客户信息的详细的逻辑的

一旦这里的逻辑改动了,那么影响的范围还是比较大的。

那么如何处理这种问题呢?

我们知道,controller的方法我们是可以 自由的增加  Request,Response,Model的

为什么我们只要设定了这个参数,就是有值的呢?

肯定是框架传给我们的,框架是如何做的呢?

我们看下节的介绍.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在基于Spring Boot的分布式架构中,可以采用以下几种方式来解决分布式环境下的Session共享问题: 1. 使用Session复制:在传统的集群环境中,可以将Session对象复制到所有服务器节点上,使得每个节点都拥有完整的Session数据。这种方式可以通过在负载均衡器后面配置会话复制或使用专门的会话复制技术实现。 2. 使用Session持久化:将Session对象的数据存储到外部共享存储中,如数据库或缓存系统(如Redis)。所有服务器节点都可以从共享存储中读取和写入Session数据。这种方式需要确保共享存储的高可用性和性能。 3. 使用无状态Session:将Session数据从服务器端移至客户端,在每个请求中包含所有必要的会话信息,如使用JWT(JSON Web Token)或类似的机制。这样服务器端就无需存储会话信息,从而实现无状态的分布式架构。 4. 使用分布式缓存:利用分布式缓存系统(如Redis)来存储Session数据,并通过在请求中传递唯一标识符(如Session ID)来进行访问。每个服务器节点都可以从分布式缓存中读取和写入Session数据。 5. 使用第三方解决方案:还可以使用一些第三方解决方案来处理分布式环境下的Session共享问题,如Spring Session、Apache Shiro、Hazelcast等。 选择合适的解决方案取决于具体的业务需求和系统架构,并需要综合考虑性能、可伸缩性、复杂性和一致性等因素。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值