利用ThreadLocal保存登录Session信息

ThreadLocal(线程本地变量)通常理解为“采用了空间换时间的设计思想,主要用来实现在多线程环境下的线程安全和保存线程上下文中的变量”。在实际的项目开发中(比如2C APP程序的服务器端程序),通常在APP调用服务端API接口的时候,需要token(登录)验证并且在具体的方法中可能会使用到当前登录账户的更多信息(比如当前登录账户的用户ID)。以前的做法,我们喜欢把(通过token获取)用户登录信息放在http请求的request请求对象中,但是这样做是耦合性很强,比较相同的代码重复使用。通过了解ThreadLocal的特性后,这种情况使用ThreadLocal来实现会很合适。下面我们将讲解具体的使用方法。

一、利用ThreadLocal实现中间类来保存登录信息

package com.example.demo.common.session;

/**
 * 利用线程本地变量来保存登录信息
 * 
 * @author Horace
 *
 */
public class SessionCache {

	private static ThreadLocal threadLocal = new ThreadLocal<>();

	public static  void put(T t) {
		threadLocal.set(t);
	}

	@SuppressWarnings("unchecked")
	public static  T get() {
		return (T) threadLocal.get();
	}

	public static void remove() {
		threadLocal.remove();
	}

}

二、在程序登录(token)认证拦截器中实现保存和删除功能

package com.example.demo.interceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.lang.Nullable;
import org.springframework.web.servlet.HandlerInterceptor;

import com.example.demo.common.session.SessionCache;
import com.example.demo.common.session.UserSession;

public class LoginAuthInterceptor implements HandlerInterceptor {

	private static final Logger logger = LoggerFactory.getLogger(LoginAuthInterceptor.class);

	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		String token = request.getHeader("token");

		// 根据token获取登录信息
		UserSession session = new UserSession();
		session.setAccountId("test");
		session.setName("测试");
		SessionCache.put(session);
		logger.info("请求方法方法前拦截");
		return true;
	}

	@Override
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
			@Nullable Exception ex) throws Exception {
		SessionCache.remove();
		logger.info("请求方法方法后处理");
	}

}

三、在需要当前登录信息的方法中使用

package com.example.demo.controller;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.example.demo.common.session.SessionCache;
import com.example.demo.common.session.UserSession;

@RestController
public class TestController {

	private static final Logger logger = LoggerFactory.getLogger(TestController.class);

	@RequestMapping(value = "/test/session")
	public String testSession(TestDto dto) {
		UserSession session = SessionCache.get();
		String userId = session.getAccountId();
		logger.info("当前登录用户ID为{}", userId);
		return "success:" + userId;
	}

}

总结:通过上面的简单的3个步骤,就可以用代码优雅的实现登录信息的管理。当然在上面的代码中ThreadLocal中并不是保存的单独的一个参数,而是用泛型指定了一个Session接口(体现面向接口编程),这样实现可以根据不同的登录角色(比如用户端、服务人员端等)保存不同的登录信息。

你可能感兴趣的:(工作积累)