后端开发之Redis篇----分布式会话

分布式会话

会话session代表的是客户端与服务器的一次交互过程,这个过程可以是连续也可以是时断时续。曾经在Servlet时代(jsp),一旦用户与服务端交互,服务器tomcat就会为用户创建一个session,同时前段会有一个jsessionid,每次交互都会携带。如此一来,服务器只要在接到用户请求时候,就可以拿到jsessonid,并根据这个ID在内存中找到对应的会话session,当拿到session会话后,那么我们就可以操作会话了。会话存活期间,我们就能认为用户一直处于正在使用着网站的状态,一旦session过期是,那么久可以认为用户已经离开网站,停止交互了。用户的身份信息,我们也是通过session来判断的,在session中可以保存不同用户的信息。

HTTP请求时无状态的,用户想服务器发起多个请求,服务器并不会知道这多次请求都是来自同一个用户的,这个就是无状态的。cookie的出现就是为了有状态的记录用户。常见的,ios与服务器交互,安卓与服务器交互,他们都是用过发起http请求来调用接口数据的,每次及哦啊胡服务器都不会拿到客户端的状态,但是我们可以通过手段去处理,比如每次用户发起请求的时候携带一个userid或者user-token,如此一来,就能让服务器分局用户id或者token来获得相应的数据。每个用户的下一次请求都能被服务器识别来自同一个用户。

tomcat中的会话,就是有状态的,一旦用户和服务器交互,就有会话,会话保存了用户的信息,这样用户就“有状态”了,服务器会和每个客户端都保持这样的一层关系,这个由容器来管理,这个session会话是保存到内存空间中的,如此一来,当不同的用户访问服务器,那么久能通过会话知道谁是谁了。tomcat会话的出现也是为了让http请求变得有状态。如果用户不在和服务器交互,那么会话超时则小时,结束了他的生命周期。

生成token

使用场景,一般在用户注册以及登录时为用户创建一个token,因为一般这都是用户进入交互过程的第一步

public UserVO convertUserVO(Users user) {

    //使用uuid生成一个随机的唯一的序列码,然后根据userId保存到redis中去
	String uniqueToken = UUID.randomUUID().toString().tirm();
    redisTemplate.opsForValue().set(REDIS_USER_TOKEN + ":" + userId, uniqueToken);

    //一般在我们完成注册或者登录后,在验证完用户名和密码后都会返回一个包含用户信息的user对象
    //而同时我们需要生成一个user——token和user对象一同返回到方法调用方,因为这个token后面是要设置到
    //cookie里面的
	UserVO userVO = new UserVO();
	BeanUtils.copyProperities(user, userVO);
	userVO.setUserToken(uniqueToken );
	
	return userVO;
}

会话拦截器

我们在controller包下创建一个interceptor的子包,创建一个类来定义我们的拦截器

public class UserTokenInterceptor implements HandlerInterceptor {

	@Autowired
	private RedisOperator redisOperator;

	public static final String REDIS_USER_TOKEN = "redis_user_token";

	//在请求到达controller前的阶段
	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    
    //这需要后端和前段协调一下,让前段将cookie中的信息解析到header中在向后端发起请求
	String userToken = request.getHeader("headerUserToken");
	String userId = request.getHeader("headerUserId");

	if (StringUtils.isBlank(userToken) || StringUtils.isBlank(userId)) {
		returnErrorResponse(response, JSOMResult.errorMsg("请登录");
		return false;
	} else {
		String token = RedisOperator.get(REDIS_USER_TOKEN  + ":" + userId);
		//这就等于在我们服务器中没有该用户的session,或者其session已经超时了
		if (StringUtils.isBlank(token)) {
			returnErrorResponse(response, JSOMResult.errorMsg("请登录");
		    return false} else {
			//服务器中的session信息和用户cookie中的不同,这样可能是因为用户异地登录了的原因
			if (!token.equals(userToken)) {
				returnErrorResponse(response, JSOMResult.errorMsg("请重新登录");
		        return false;
		    }
		}
    }
    return true;
   }
   
   private void returnErrorResponse(HttpServletResponse response, JSONResult result) {
   		outputStream = null;
   		try{
   			response.setCharacterEncoding("utf-8");
   			response.setContentType("text/json");
   			out = response.getOutputStream();
   			out.write(JsonUtils.objectToJson(result).getBytes("utf-8");
   		} catch (IOException e) {
   			e.printStackTrace();
   		} finally {
   			try{
   				if (out != null) {
   					out.close();
   				}
   			} catch (IOException e) {
   				e.printStackTrace();
   			}
   		}
   	}

	//下面省略了postHandler和afterComplete两个方法的内容,因为我们暂时用不上
} 			

下面是配置类的内容,如果是使用SpringMVC的话,就直接写一个xml配置文件
在controller包创建一个config的子包,在里面创建一个WebMvcConfig类

/**
* 这个类是要实现WebMvcConfigurer接口的,里面有很多方法,可以自行选择,包括静态资源的映射也是用他
* 因为拦截器相应注解来自动注入容器,所以这里用@Bean来生成
* 在拦截器添加器中用注册器先调用这个Bean方法生成
*/
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
	
	@Bean
	public UserTokenInterceptor userTokenInterceptor() {
		return new UserTokenInterceptor();
	}

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
    	registry.addInterceptor(userTokenInterceptor())
    		.addPathPattern("/shopcart/add")
    		.addPathPattern("/shopcart/*")
    		.addPathPattern("/address/list")
    		.addPathPattern("/address/add")
    		.addPathPattern("/address/*")
    		.addPathPattern("/orders/*")
    		.addPathPattern("/userInfo/*")
    		.addPathPattern("/myorders/*")
    		.excludePathPattern("/myorders/deliver")
    		.excludePathPattern("/orders/notifOrderPaid");

		WebMvcConfigurer.super.addInterceptors(registry);
	}
}
发布了118 篇原创文章 · 获赞 16 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/weixin_39702831/article/details/104817040