应用上下文的核心类是threadLocal类来实现线程隔离的核心
package com.company.common.context;
/**
* 用户上下文支持
*/
public final class UserContextHolder {
private static final ThreadLocal<UserContext> CONTEXT_HOLDER = new ThreadLocal<>();
private UserContextHolder() {
}
public static UserContext getContext() {
// 获取线程私有内存信息
UserContext context = CONTEXT_HOLDER.get();
if (context == null) {
context = new UserContext();
CONTEXT_HOLDER.set(context);
}
return context;
}
public static void setContext(UserContext context) {
CONTEXT_HOLDER.set(context);
}
public static void clearContext() {
CONTEXT_HOLDER.remove();
}
}
package com.company.common.context;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserContext {
/**
* 登录用户ID
*/
private Long id;
/**
* 登录用户名称
*/
private String name;
}
应用上下文主要用来存储用户登录信息的token
存入token的地方在拦截器处
package com.company.common.iam;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.jwt.JWT;
import cn.hutool.jwt.JWTUtil;
import lombok.extern.slf4j.Slf4j;
import com.company.common.context.UserContext;
import com.company.common.context.UserContextHolder;
import com.company.common.exception.UnauthorizedException;
import com.company.common.memory.caches.AbstractMemoryStorage;
import com.company.common.memory.caches.RedisMemoryStorage;
import com.company.common.util.TokenUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.AsyncHandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.concurrent.TimeUnit;
@Slf4j
public class IamInterceptor implements AsyncHandlerInterceptor {
private final AbstractMemoryStorage memoryStorage;
public IamInterceptor(AbstractMemoryStorage memoryStorage) {
this.memoryStorage = memoryStorage;
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
log.info("request_url: {}", request.getRequestURI());
String token = TokenUtils.getRequestToken(request);
if (!StringUtils.hasText(token) || "undefined".equalsIgnoreCase(token)) {
throw new UnauthorizedException("认证失败");
}
JWT jwt = JWTUtil.parseToken(token);
JSONObject payloads = jwt.getPayloads();
Long id = payloads.getLong(TokenUtils.KEY_ID);
String userName = payloads.getStr(TokenUtils.KEY_USER_NAME);
String key = RedisMemoryStorage.LOGIN_TOKEN_KEY + SecureUtil.md5(userName + token);
String memToken = memoryStorage.get(key);
if (!token.equals(memToken)) {
throw new UnauthorizedException("认证失败");
}
UserContextHolder.setContext(new UserContext(id, userName));
memoryStorage.set(key, token, TokenUtils.TOKEN_TIMEOUT, TimeUnit.MILLISECONDS);
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
UserContextHolder.clearContext();
}
}
内存缓存用的redis
package com.company.common.memory.caches;
import com.alibaba.fastjson.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.util.StringUtils;
import java.util.concurrent.TimeUnit;
public class RedisMemoryStorage extends AbstractMemoryStorage {
public final static String IP_BLOCK_KEY = "IpBlock:";
public final static String REFERRAL_CODE_KEY = "ReferralCode:";
public final static String LOGIN_TOKEN_KEY = "LoginToken:";
public static final String WX_SESSION_ID = "wx_session_id";
public static final String MA_USER_TOKEN_KEY = "ma_user_token:";
public static final String MA_USER_ORDER_KEY = "ma_user_order:";
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Override
public void set(String key, String value) {
ValueOperations<String, String> ops = stringRedisTemplate.opsForValue();
ops.set(key, value);
}
@Override
public void set(String key, String value, long timeout, TimeUnit unit) {
ValueOperations<String, String> ops = stringRedisTemplate.opsForValue();
ops.set(key, value, timeout, unit);
}
@Override
public String get(String key) {
ValueOperations<String, String> ops = stringRedisTemplate.opsForValue();
return ops.get(key);
}
@Override
public <T> T get(String key, Class<T> clazz) {
ValueOperations<String, String> ops = stringRedisTemplate.opsForValue();
String s = ops.get(key);
if (!StringUtils.hasText(s)) {
return null;
}
return JSONObject.parseObject(s, clazz);
}
@Override
public Long getExpire(String key, TimeUnit unit) {
return stringRedisTemplate.getExpire(key, unit);
}
@Override
public void delete(String key) {
stringRedisTemplate.delete(key);
}
@Override
public Boolean hasKey(String key) {
return stringRedisTemplate.hasKey(key);
}
}
网友评论