微服务下设计一个注解标识是否需要登录

需求

现在我们是微服务系统,需要设计一个注解 **@RequiredLogin ,**当标识这个注解时表示系统需要登录才能继续操作。

实现思路

首先,需要明确我们要拦截的是从浏览器过来的请求,服务之间的互相调用是不需要拦截的(比如 Feign 调用)。下图是一些交互情况。

我们在网关的全局过滤器中添加一个标识 X (表示他是从浏览器过来的),然后在每次 Feign 远程调用之前标识一下 Y(表示是内部远程调用),最后在每个服务的拦截器(这个可以抽出通用拦截器配置)中拦截请求头中的信息进行判断即可。

代码实现

定义注解

java 复制代码
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequireLogin {
}

网关的全局过滤器

java 复制代码
/**
 * 定义全局过滤器,功能如下:
 * 1.把客户端真实IP通过请求同的方式传递给微服务
 * 2.在请求头中添加FEIGN_REQUEST的请求头,值为0,标记请求不是Feign调用,而是客户端调用
 * 3.刷新Token的有效时间
 */
@Component
public class CommonFilter implements GlobalFilter {
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        /**
         * pre拦截逻辑
         * 在请求去到微服务之前,做了两个处理
         * 1.把客户端真实IP通过请求同的方式传递给微服务
         * 2.在请求头中添加FEIGN_REQUEST的请求头,值为0,标记请求不是Feign调用,而是客户端调用
         */
        ServerHttpRequest request = exchange.getRequest().mutate().
                header(CommonConstants.REAL_IP,exchange.getRequest().getRemoteAddress().getHostString()).
                header(CommonConstants.FEIGN_REQUEST_KEY,CommonConstants.FEIGN_REQUEST_FALSE).
                build();
        return chain.filter(exchange.mutate().request(request).build()).then(Mono.fromRunnable(()->{
            /**
             * post拦截逻辑
             * 在请求执行完微服务之后,需要刷新token在redis的时间
             * 判断token不为空 && Redis还存在这个token对于的key,这时候需要延长Redis中对应key的有效时间.
             */
            String token,redisKey;
            if(!StringUtils.isEmpty(token = exchange.getRequest().getHeaders().getFirst(CommonConstants.TOKEN_NAME))
                    && redisTemplate.hasKey(redisKey = CommonRedisKey.USER_TOKEN.getRealKey(token))){
                redisTemplate.expire(redisKey, CommonRedisKey.USER_TOKEN.getExpireTime(), CommonRedisKey.USER_TOKEN.getUnit());
            }
        }));
    }
}

Feign 远程调用的前置拦截

java 复制代码
/**
 * Feign调用时添加标记为此时是通过Feign进行调用(不需要登录)
 */
public class FeignRequestInterceptor implements RequestInterceptor {
    @Override
    public void apply(RequestTemplate template) {
        template.header(CommonConstants.FEIGN_REQUEST_KEY,CommonConstants.FEIGN_REQUEST_TRUE);
    }
}

每个请求都要走的通用登录拦截器

java 复制代码
public class RequireLoginInterceptor implements HandlerInterceptor {
    private StringRedisTemplate redisTemplate;
    public RequireLoginInterceptor(StringRedisTemplate redisTemplate){
        this.redisTemplate = redisTemplate;
    }
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if(handler instanceof HandlerMethod){
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            String feignRequest = request.getHeader(CommonConstants.FEIGN_REQUEST_KEY);
            //如果是feign请求,直接放行
            if(!StringUtils.isEmpty(feignRequest) && CommonConstants.FEIGN_REQUEST_TRUE.equals(feignRequest)){
                return true;
            }
            //如果不是Feign请求,判断是否有贴RequireLogin注解
            if(handlerMethod.getMethodAnnotation(RequireLogin.class)!=null){
                response.setContentType("application/json;charset=utf-8");
                String token = request.getHeader(CommonConstants.TOKEN_NAME);
                if(StringUtils.isEmpty(token)){
                    response.getWriter().write(JSON.toJSONString(Result.error(CommonCodeMsg.TOKEN_INVALID)));
                    return false;
                }
                String phone = JSON.parseObject(redisTemplate.opsForValue().get(CommonRedisKey.USER_TOKEN.getRealKey(token)),String.class);
                if(phone==null){
                    response.getWriter().write(JSON.toJSONString(Result.error(CommonCodeMsg.TOKEN_INVALID)));
                    return false;
                }
            }
        }
        return true;
    }
}
相关推荐
七星静香9 分钟前
laravel chunkById 分块查询 使用时的问题
java·前端·laravel
W Y9 分钟前
【架构-37】Spark和Flink
架构·flink·spark
Jacob程序员10 分钟前
java导出word文件(手绘)
java·开发语言·word
ZHOUPUYU10 分钟前
IntelliJ IDEA超详细下载安装教程(附安装包)
java·ide·intellij-idea
stewie614 分钟前
在IDEA中使用Git
java·git
Elaine20239129 分钟前
06 网络编程基础
java·网络
Gemini199530 分钟前
分布式和微服务的区别
分布式·微服务·架构
G丶AEOM30 分钟前
分布式——BASE理论
java·分布式·八股
落落鱼201331 分钟前
tp接口 入口文件 500 错误原因
java·开发语言
想要打 Acm 的小周同学呀32 分钟前
LRU缓存算法
java·算法·缓存