文章目录
前言
第一节 微服务间用户信息传递问题
第二节 解决思路
第三节 实践操作(基于Feign)

1. 调用链路说明
2. shop-common改造
3. 开启注解
4. 启动服务器测试
第四节 dubbo传递用户信息
1. 调用链路说明
2. shop-common 改造
3. 测试

第五节 Feign和Dubbo跨服务传递用户信息
关于错误
前言
微服务间如何进行用户信息传递?只能依靠接口调用时显式通过参数传递吗?能否在传递过程中无感知呢?

本章代码已分享至Gitee: https://gitee.com/lengcz/springcloudalibaba01.git

第一节 微服务间用户信息传递问题
微服务由于跨服务了,我们如何在中间的某个服务知道当前的用户信息呢?
例如下面,用户下单的请求,用户的请求经过了 gateway>> order >> product,如果在order的时候,我们怎么样知道用户是谁?难道要请求接口传参时传递参数(用户身份信息)吗?


第二节 解决思路
请求流程

  1. 用户通过header传递用户身份信息,请求发送到gateway
  2. gateway 将请求路由到 order
  3. order调用product

我们知道一个微服务的请求有入口,也有出口(调用)其它微服务,请求进入时,可以通过filter进行拦截,
而调用其它请求时,我们可以将用户信息传递给被调用者(微服务),我们只需要通过AOP的思想,在入口处捕获用户信息,在出口时将用户信息传递出去,这样就保证了全链路都能获取到客户端的身份信息。当我们所有的微服务都在前后插入了接收和传出的操作,那么全链路就能够获取到用户信息了。

第三节 实践操作(基于Feign)
我们的用户信息传递是所有微服务的,因为所有微服务模块都需要进行用户信息传递,因为我们将实现定义在common模块

order,product,user均引用common

1. 调用链路说明
Feign的远程调用是Http的方式,所以我们只需要Filter 在入口取数据,在RequestInterceptor 出口时传递数据即可。

2. shop-common改造
2.1 引入依赖

<dependency><groupId>commons-lang</groupId><artifactId>commons-lang</artifactId><version>2.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>4.0.1</version><scope>provided</scope>
</dependency>
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

2.2 定义ThreadLocal进行线程共享

package com.lcz.userinfo;
public class UserInfoContext {private static ThreadLocal<UserInfo> userInfo = new ThreadLocal<>();public static UserInfo getUser() {return (UserInfo) userInfo.get();}public static void setUser(UserInfo user) {userInfo.set(user);}public static void remove(){userInfo.remove();}
}

2.3 定义用户实体

package com.lcz.userinfo;
import lombok.Data;
import java.io.Serializable;@Data
public class UserInfo implements Serializable {private Integer uid; //用户idprivate String username;//用户名
}

2.4 定义常量类

package com.lcz.userinfo;public class UserInfoConstant {public static final String DUBBO_USER_KEY="DUBBO_USER_INFO";public static final String Authorization="USER_INFO";
}

2.5 编写拦截器

package com.lcz.userinfo;import com.alibaba.fastjson.JSON;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import lombok.extern.slf4j.Slf4j;import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;@Slf4j
public class TransmitUserInfoFeighClientIntercepter implements RequestInterceptor {public TransmitUserInfoFeighClientIntercepter() {}@Overridepublic void apply(RequestTemplate requestTemplate) {//从应用上下文中取出user信息,放入Feign的请求头中UserInfo user = UserInfoContext.getUser();log.info("传递用户信息:"+JSON.toJSONString(user));if (user != null) {try {String userJson = JSON.toJSONString(user);requestTemplate.header(UserInfoConstant.Authorization,new String[]{URLDecoder.decode(userJson,"UTF-8")});} catch (UnsupportedEncodingException e) {log.error("用户信息设置错误",e);}finally {UserInfoContext.remove();}}}
}

2.6 编写过滤器

package com.lcz.userinfo;import com.alibaba.fastjson.JSON;
import com.lcz.pojo.User;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;@Slf4j
public class TransmitUserInfoFilter implements Filter {public TransmitUserInfoFilter() {}@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {this.initUserInfo((HttpServletRequest)request);chain.doFilter(request,response);}private void initUserInfo(HttpServletRequest request){String userJson = request.getHeader(UserInfoConstant.Authorization);log.info("接收用户信息:"+userJson);if (StringUtils.isNotBlank(userJson)) {try {userJson = URLDecoder.decode(userJson,"UTF-8");UserInfo userInfo = (UserInfo) JSON.parseObject(userJson,UserInfo.class);//将UserInfo放入上下文中UserInfoContext.setUser(userInfo);} catch (UnsupportedEncodingException e) {log.error("init userInfo error",e);}}}
}

2.7 编写注解实现类

package com.lcz.userinfo;import org.springframework.context.annotation.Bean;
public class EnableUserInfoTransmitterAutoConfiguration {public EnableUserInfoTransmitterAutoConfiguration() {}@Beanpublic TransmitUserInfoFeighClientIntercepter transmitUserInfo2FeighHttpHeader(){return new TransmitUserInfoFeighClientIntercepter();}@Beanpublic TransmitUserInfoFilter transmitUserInfoFromHttpHeader(){return new TransmitUserInfoFilter();}
}

2.8 . 编写Enable注解,实现注解式注入

package com.lcz.userinfo;import org.springframework.context.annotation.Import;import java.lang.annotation.*;@Documented
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Import({EnableUserInfoTransmitterAutoConfiguration.class})
public @interface EnableUserInfoTransmitter {
}

3. 开启注解
在order,product,user模块开启用户信息传递,使用@EnableUserInfoTransmitter


注意: common公共模块修改了代码,注意将common模块重新maven install发布到本地仓库

4. 启动服务器测试
回到我们的流程图,下单流程有这些微服务参与了,我们启动这些服务。只要我们在product中能够获取到用户信息,就证明用户信息传递成功了。

发起下单请求,由于我们的身份信息通过header传递,我们需要先生成一个格式正确的身份,再写入到header进行传递

    @Testpublic void testUser() throws UnsupportedEncodingException {UserInfo user=new UserInfo();user.setUid(111);user.setUsername("xiaowang");String str=  URLEncoder.encode(JSONObject.toJSONString(user),"UTF-8");System.out.println(str);}

生成的信息

  1. %7B%22uid%22%3A111%2C%22username%22%3A%22xiaowang%22%7D

将这个token(身份信息)放在header中发送下单请求


我们再product微服务的日志中,可以输出用户信息,表明了用户信息从api-gateway >> order >> product 这个流程调用中,用户信息传递成功了。

第四节 dubbo传递用户信息
在第三节,我们的改造只适合微服务之间基于feign的调用,才能传递用户信息到其它微服务。而事实上,我们现在很多微服务之间的调用是基于dubbo的,微服务之间如何通过dubbo 传递用户信息呢?
我们的解决思路依然一样的,需要微服务的前后进行插板,这样就能保证微服务通过dubbo调用时,也能实现跨服务传递用户身份了。

1. 调用链路说明
通过官方的代码架构调用链路图,我们可以知道dubbo远程调用都需要经过Filter(全类名org.apache.dubbo.rpc.Filter),因此我们只需要在消费方和生产方两边的Filter进行附带数据传输,即可实现信息的隐式传递。


我们利用org.apache.dubbo.rpc.Filter来拦截信息,并通过其传递用户信息,而接收方可以通过org.apache.dubbo.rpc.Filter接收信息

2. shop-common 改造
基于前面的代码,我们继续改造。

2.1 引入dubbo的依赖

<!--引入dubbo--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-dubbo</artifactId></dependency>

2.2 消费者传入用户信息

package com.lcz.userinfo;import com.alibaba.dubbo.common.extension.Activate;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.rpc.*;@Slf4j
@Activate(group = {UserInfoConstant.CONSUMER})
public class UserInfoConsumerFilter implements org.apache.dubbo.rpc.Filter {@Overridepublic Result invoke(Invoker<?> invoker, org.apache.dubbo.rpc.Invocation invocation) throws RpcException {
//        log.info("消费方:------");try{UserInfo userInfo = UserInfoContext.getUser();if (null == userInfo) {return invoker.invoke(invocation);}invocation.getObjectAttachments().put(UserInfoConstant.DUBBO_USER_KEY, userInfo);return invoker.invoke(invocation);}finally {UserInfoContext.remove();}}
}

2.3 生产者接收用户信息

package com.it.userinfo;import com.alibaba.dubbo.common.extension.Activate;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcException;@Slf4j
@Activate(group = CommonConstants.PROVIDER)
public class UserInfoProviderFilter implements org.apache.dubbo.rpc.Filter {@Overridepublic Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
//        log.info("生成方:------");Object userInfo = invocation.getObjectAttachment(UserInfoConstant.DUBBO_USER_KEY);if (null != userInfo) {UserInfoContext.setUser((UserInfo) userInfo);}return invoker.invoke(invocation);}
}

2.4 添加配置文件,META-INF/dubbo目录下,创建com.alibaba.dubbo.rpc.Filter文件,内容如下

  1. userInfoConsumerFilter=com.lcz.userinfo.UserInfoConsumerFilter
  2. userInfoProviderFilter=com.lcz.userinfo.UserInfoProviderFilter

2.3. 测试


第五节 Feign和Dubbo跨服务传递用户信息
基于我们上面的改造,各微服务不管使用Feign调用还是Dubbo调用,都可以实现用户信息的跨服务传递,解决了无法获取用户身份的问题。让跨服务之间,可以直接使用UserInfoContext.getUser(),具有单体应用的信息获取能力

UserInfo userInfo = UserInfoContext.getUser(); //获取用户信息

同时,直接传递用户身份信息,基于这个用户信息的传递思想,我们可以实现链路跟踪(Sleuth),也可以在gateway网关封装好某些内容,传递一些request信息,比如客户端IP,用户的权限。

当然了,上面的demo直接传递用户信息是不具有防伪性,我们可能使用JWT相关的技术进行鉴权等处理,在穿越网关时,将其转换为明文可操作的UserInfo,这样就保证了传递的用户信息的安全可靠性。

关于错误
如果打包common时,提示repackage failed: Unable to find main class
可以配置

<build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><mainClass>none</mainClass>     <!-- 取消查找本项目下的Main方法:为了解决Unable to find main class的问题 --><classifier>execute</classifier>    <!-- 为了解决依赖模块找不到此模块中的类或属性 --></configuration><executions><execution><goals><goal>repackage</goal></goals></execution></executions></plugin></plugins>
</build>

微服务间用户信息共享机制相关推荐

  1. JWT(解决前后端分离和微服务的用户会话跟踪问题)

    这里写目录标题 JWT:解决前后端分离和微服务的用户会话跟踪问题 与传统sessio验证的区别: 基于 token 的鉴权机制 JWT的主要引用场景及优点 JWT的构成: JWT搭建 案例: JWT: ...

  2. 再见 Feign!推荐一款微服务间调用神器,跟 SpringCloud 绝配!

    在微服务项目中,如果我们想实现服务间调用,一般会选择Feign.之前介绍过一款HTTP客户端工具Retrofit,配合SpringBoot非常好用!其实Retrofit不仅支持普通的HTTP调用,还能 ...

  3. Istio:一个用于微服务间通信的服务网格开源项目

    http://www.infoq.com/cn/news/2017/05/istio Istio:一个用于微服务间通信的服务网格开源项目 Google.IBM和Lyft开源了微服务管理.保护和监控框架 ...

  4. dbnetlib不存在或拒绝访问_idou老师教你学Istio 16:如何用 Istio 实现微服务间的访问控制...

    本文由华为云容器Istio团队撰稿,未经允许谢绝转载. 摘要 使用 Istio 可以很方便地实现微服务间的访问控制.本文演示了使用 Denier 适配器和黑白名单两种方法. 使用场景 有时需要对微服务 ...

  5. 实践《如何使用Seata保证Dubbo微服务间的一致性》

    原文在这里:如何使用Seata保证Dubbo微服务间的一致性. 从这里下载sample代码,master分支,最新的代码提交日期是2019.9.16,修订号cd10c5a.本文代码在模块dubbo里. ...

  6. 微服务间的调用和应用内调用有啥区别

    摘要 目前大部分的系统架构都是微服务架构,就算没有注册中心.服务管理,也肯定是多个服务,单体服务比较少了. 大家平时需要在应用内调用rpc接口也比较多,那么有没有思考过微服务之间的调用和应用内直接调用 ...

  7. 专家呼吁建安全漏洞信息共享机制并强化管控

    近日,由中国网络空间安全协会主办,中国网络空间安全协会网络空间安全法律与公共政策专业委员会.北京邮电大学互联网治理与法律研究中心.公安部第三研究所网络安全法律研究中心.西安交通大学信息安全法律研究中心 ...

  8. Spring Boot微服务间文件返回实现

    Feign接口获取文件流问题_Java_wyazyf的博客-CSDN博客 https://blog.csdn.net/wyazyf/article/details/93200033 Spring Bo ...

  9. 通过RestTemplate进行微服务间(通过服务名)的调用

    样例: @Slf4j @RestController @RequestMapping(path = "/test") public class TestController {@A ...

最新文章

  1. datatable复制一行数据到本表
  2. RocketMQ:消息ACK机制源码解析
  3. 机器学习之决策树(下)
  4. Delphi XE7的Splash 功能
  5. python 字符串函数 center_Python字符串处理
  6. (已更新)短视频去水印解析客户端小程序源代码
  7. mysql to double_double todouble
  8. JQAjax读页面里面的方法
  9. STM32中 利用PWM控制步进电机,ARR与PSC值的设定
  10. Git 版本回退方法
  11. 【python】math函数库介绍及其例题
  12. Db2 insert got DSNISGRT:500A abend
  13. 量化交易有因子动物园 深度学习里有模型动物园(ModelZoo)又叫模型市场基于深度学习的增量学习,迁移学习等技术发展而来【调研】
  14. python支持复数以及相关的运算吗_python怎么实现复数运算
  15. 华虹技通华为鸿蒙,浩丰科技(300419)个股分析_牛叉诊股_同花顺财经
  16. 前端笔试—编译和体系结构
  17. Volume数据存储详解
  18. CORTEX-M 系列调试下载总览
  19. 痞子衡嵌入式:聊聊i.MXRT1170上串行NOR Flash双程序可交替启动设计
  20. 总分 Score Inflation(洛谷)

热门文章

  1. python readline循环读取_Python 文件 readline() 方法
  2. php文字怎么居中,html文字和图片怎么居中?居中代码是什么
  3. 智能车就是计算机,无人车就是机器人
  4. 将oracle数据库中指定的多张表结构导入word
  5. 火星人培训python
  6. 电脑杀毒后不能上网的解决方法
  7. C++文本文件,二进制文件,write(),read(),map容器,seekg(),seekp(),tellg(),tellp()函数
  8. docker 存出,载入镜像
  9. ICCV 2021|复杂场景目标检测——科大讯飞等提出X光安检场景下危险品检测基准(已开源)...
  10. matlab连续型随机变量,一维连续型随机变量及其概率密度[精选].ppt