|
文章目录🌞SunFrame:SpringBoot的轻量级开发框架(个人开源项目推荐)🌟亮点功能📦springcloud模块概览常用工具🔗更多信息1.设计1.链路流程2.详细设计2.网关过滤器获取唯一标识放到Header1.LoginFilter.java2.SubjectCategoryController.java测试3.进行测试1.用户登录2.携带token访问后端接口3.基于ThreadLocal实现上下文传递1.目录结构2.ThreadLocal的工具类LoginContextHolder.java3.拦截器将网关传递的信息放到LoginInterceptor.java4.配置类将自定义拦截器注册GlobalConfig.java5.LoginUtil.java6.测试SubjectCategoryController.java4.OpenFeign的使用1.目录结构1.sun-club-auth2.sun-club-auth-api引入依赖3.UserFeignService.java将controller层的接口暴露4.AuthUserDTO.java从controller剪切到api层5.Result.java和ResultCodeEnum.java从common包中剪切过来6.sun-club-auth-application-controller引入api层的依赖,DTO和Result都使用api层的7.sun-club-subject微服务调用sun-club-auth微服务1.sun-club-infra引入sun-club-auth微服务的api包2.在sun-club-infra模块的UserRpc.java进行rpc调用3.创建接受信息的entity,UserInfo.java4.在启动类加注解,启动Feign5.测试TestFeignController.java6.测试5.OpenFeign拦截器打通微服务上下文(由调用方来写)1.FeignRequestInterceptor.java将唯一标识放到Header中传递到其他微服务2.FeignConfiguration.java将拦截器注入容器3.在被调用方使用过滤器将用户唯一标识存到ThreadLocal(跟前面写过的一样)1.目录结构2.ThreadLocal的工具类LoginContextHolder.java3.自定义拦截器获取Header中的唯一标识,并放到ThreadLocal中LoginInterceptor.java4.将拦截器注入容器GlobalConfig.java5.UserController.java测试获取调用方传来的唯一标识4.用户上下文整体流程测试1.网关过滤器将唯一标识从redis中取出,放到Header,传到微服务模块2.Feign的调用微服务的登录拦截器,将唯一标识放到ThreadLocal中3.在调用Feign之前,进入Feign拦截器,将唯一标识放到Header中,传递给被调用方4.被调用方的登录拦截器,读取Header中的唯一标识,放到ThreadLocal中5.被调用方从ThreadLocal中可以获取到唯一标识6.本地缓存guava使用1.SubjectCategoryDomainServiceImpl.java2.测试3.本地缓存工具类1.ListLocalCacheUtil.java2.使用方式1.依赖注入2.使用3.测试🌞SunFrame:SpringBoot的轻量级开发框架(个人开源项目推荐)轻松高效的现代化开发体验SunFrame是我个人开源的一款基于SpringBoot的轻量级框架,专为中小型企业设计。它提供了一种快速、简单且易于扩展的开发方式。我们的开发文档记录了整个项目从0到1的任何细节,实属不易,请给我们一个Star!🌟您的支持是我们持续改进的动力。🌟亮点功能组件化开发:灵活选择,简化流程。高性能:通过异步日志和Redis缓存提升性能。易扩展:支持多种数据库和消息队列。📦springcloud模块概览Nacos服务:高效的服务注册与发现。Feign远程调用:简化服务间通信。强大网关:路由与限流。常用工具日志管理:异步处理与链路追踪。Redis集成:支持分布式锁与缓存。Swagger文档:便捷的API入口。测试支持:SpringBoot-Test集成。EasyCode:自定义EasyCode模板引擎,一键生成CRUD。🔗更多信息开源地址:GiteeSunFrame详细文档:语雀文档1.设计1.链路流程2.详细设计2.网关过滤器获取唯一标识放到Header1.LoginFilter.javapackagecom.sunxiansheng.club.gateway.filter;importcn.dev33.satoken.stp.SaTokenInfo;importcn.dev33.satoken.stp.StpUtil;importcom.alibaba.cloud.commons.lang.StringUtils;importlombok.SneakyThrows;importlombok.extern.slf4j.Slf4j;importorg.springframework.cloud.gateway.filter.GatewayFilterChain;importorg.springframework.cloud.gateway.filter.GlobalFilter;importorg.springframework.http.server.reactive.ServerHttpRequest;importorg.springframework.stereotype.Component;importorg.springframework.web.server.ServerWebExchange;importreactor.core.publisher.Mono;/***Description:网关过滤器,在用户登录之后再次请求网关就会携带token,可以通过网关过滤器通过redis获取用户的唯一标识然后放到Header中传递到后端*过滤器的优先级是比配置文件中配置的路由要低*@Authorsun*@Create2024/6/1515:28*@Version1.0*/@Component@Slf4jpublicclassLoginFilterimplementsGlobalFilter{@Override@SneakyThrowspublicMonofilter(ServerWebExchangeexchange,GatewayFilterChainchain){//获取请求ServerHttpRequestrequest=exchange.getRequest();//获取一个很全的东西ServerHttpRequest.Buildermutate=request.mutate();//获取urlStringurl=request.getURI().getPath();log.info("网关过滤器:用户请求的url:"+url);//如果发现是用户登录的请求,直接放行,注意先走的网关配置,所以前缀会被删除if(url.equals("/user/doLogin")){returnchain.filter(exchange);}//通过sa-token框架获取用户的tokenInfo,可以通过这个对象来获取用户信息SaTokenInfotokenInfo=StpUtil.getTokenInfo();//根据token获取用户的唯一标识StringloginId=(String)tokenInfo.getLoginId();if(StringUtils.isBlank(loginId)){log.info("网关过滤器获取用户唯一标识失败!");}//如果到这了,就说明已经获取到了用户的唯一标识,将其放到Header中mutate.header("loginId",loginId);//将携带了用户唯一标识的request发送到其他微服务模块returnchain.filter(exchange.mutate().request(mutate.build()).build());}}123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051522.SubjectCategoryController.java测试3.进行测试1.用户登录2.携带token访问后端接口3.基于ThreadLocal实现上下文传递1.目录结构2.ThreadLocal的工具类LoginContextHolder.javapackagecom.sunxiansheng.subject.application.context;importjava.util.Map;importjava.util.Objects;importjava.util.concurrent.ConcurrentHashMap;/***Description:上下文对象(ThreadLocal)*@Authorsun*@Create2024/6/1516:27*@Version1.0*/publicclassLoginContextHolder{//这个ThreadLocal持有一个MapprivatestaticfinalInheritableThreadLocal>THREAD_LOCAL=newInheritableThreadLocal();/***为ThreadLocal持有的Map设值*@paramkey*@paramval*/publicstaticvoidset(Stringkey,Objectval){Mapmap=getThreadLocalMap();map.put(key,val);}/***从ThreadLocal持有的Map取值*@paramkey*@return*/publicstaticObjectget(Stringkey){Mapmap=THREAD_LOCAL.get();returnmap.get(key);}/***清除ThreadLocal*/publicstaticvoidremove(){THREAD_LOCAL.remove();}/***初始化一个ThreadLocal持有的Map,要保证这个Map是单例的*@return*/publicstaticMapgetThreadLocalMap(){//获取到ThreadLocal的MapMapmap=THREAD_LOCAL.get();//如果是空的再创建一个Map,然后放进去if(Objects.isNull(map)){map=newConcurrentHashMap();THREAD_LOCAL.set(map);}//放到ThreadLocal中returnmap;}//以下为获取用户信息的方法publicstaticStringgetLoginId(){return(String)getThreadLocalMap().get("loginId");}}12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667683.拦截器将网关传递的信息放到LoginInterceptor.javapackagecom.sunxiansheng.subject.application.interceptor;importcom.sunxiansheng.subject.application.context.LoginContextHolder;importorg.springframework.web.servlet.HandlerInterceptor;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletResponse;/***Description:处理用户上下文的拦截器*@Authorsun*@Create2024/6/1516:20*@Version1.0*/publicclassLoginInterceptorimplementsHandlerInterceptor{/***当请求到这里了,就说明,网关已经将用户的loginId放到了Header里了*@paramrequest*@paramresponse*@paramhandler*@return*@throwsException*/@OverridepublicbooleanpreHandle(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler)throwsException{StringloginId=request.getHeader("loginId");//将loginId放到ThreadLocal里面LoginContextHolder.set("loginId",loginId);returntrue;}/***在操作结束后清除ThreadLocal,因为如果线程复用并且没清除,这个ThreadLocal还会存在,造成数据污染*@paramrequest*@paramresponse*@paramhandler*@paramex*@throwsException*/@OverridepublicvoidafterCompletion(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler,Exceptionex)throwsException{LoginContextHolder.remove();}}123456789101112131415161718192021222324252627282930313233343536373839404142434445464.配置类将自定义拦截器注册GlobalConfig.java5.LoginUtil.javapackagecom.sunxiansheng.subject.application.util;importcom.sunxiansheng.subject.application.context.LoginContextHolder;/***Description:用户登录的util*@Authorsun*@Create2024/6/1517:12*@Version1.0*/publicclassLoginUtil{/*获取loginId*/publicstaticStringgetLoginId(){returnLoginContextHolder.getLoginId();}}123456789101112131415161718196.测试SubjectCategoryController.java4.OpenFeign的使用1.目录结构1.sun-club-auth2.sun-club-auth-api引入依赖org.springframework.cloud3.0.7org.springframework.cloud3.0.6org.projectlombok1.18.16123456789101112131415161718193.UserFeignService.java将controller层的接口暴露packagecom.sunxiansheng.auth.api;importcom.sunxiansheng.auth.entity.AuthUserDTO;importcom.sunxiansheng.auth.entity.Result;importorg.springframework.cloud.openfeign.FeignClient;importorg.springframework.web.bind.annotation.RequestBody;importorg.springframework.web.bind.annotation.RequestMapping;/***Description:用户服务feign*@Authorsun*@Create2024/6/1613:20*@Version1.0*/@FeignClient("sub-club-auth")//服务名publicinterfaceUserFeignService{@RequestMapping("/user/getUserInfo")publicResult1.0-SNAPSHOTcompile12345677.sun-club-subject微服务调用sun-club-auth微服务1.sun-club-infra引入sun-club-auth微服务的api包com.sun.club1.0-SNAPSHOT1234562.在sun-club-infra模块的UserRpc.java进行rpc调用packagecom.sunxiansheng.subject.infra.rpc;importcom.sunxiansheng.auth.api.UserFeignService;importcom.sunxiansheng.auth.entity.AuthUserDTO;importcom.sunxiansheng.auth.entity.Result;importcom.sunxiansheng.subject.infra.eneity.UserInfo;importorg.springframework.stereotype.Component;importjavax.annotation.Resource;/***Description:*@Authorsun*@Create2024/6/1613:45*@Version1.0*/@ComponentpublicclassUserRpc{//直接注入FeignService@ResourceprivateUserFeignServiceuserFeignService;/***根据用户名来获取用户信息*@paramuserName*@return*/publicUserInfogetUserInfo(StringuserName){AuthUserDTOauthUserDTO=newAuthUserDTO();authUserDTO.setUserName(userName);//向调用方法一样远程调用auth微服务的接口并得到结果Result
|
|