能够完成网关统一鉴权的功能
能够完成认证用户列表查询
能够熟悉app端用户认证审核流程
能够完成app用户审核代码开发
不同的微服务一般会有不同的网络地址,而外部客户端可能需要调用多个服务的接口才能完成一个业务需求,如果让客户端直接与各个微服务通信,会有以下的问题:
以上这些问题可以借助网关解决。
网关是介于客户端和服务器端之间的中间层,所有的外部请求都会先经过 网关这一层。也就是说,API 的实现方面更多的考虑业务逻辑,而安全、性能、监控可以交由 网关来做,这样既提高业务灵活性又不缺安全性,典型的架构图如图所示:
优点如下:
总结:微服务网关就是一个系统,通过暴露该微服务网关系统,方便我们进行相关的鉴权,安全控制,日志统一处理,易于监控的相关功能。
实现微服务网关的技术有很多,
我们使用gateway这个网关技术,无缝衔接到基于spring cloud的微服务开发中来。
gateway官网:
https://spring.io/projects/spring-cloud-gateway
(1)创建itheima-leadnews-gateway-admin微服务
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>itheima-leadnews-gateway</artifactId> <groupId>com.itheima</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>itheima-leadnews-gateway-admin</artifactId> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <dependency> <groupId>com.itheima</groupId> <artifactId>itheima-leadnews-common</artifactId> <version>1.0-SNAPSHOT</version> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </exclusion> </exclusions> </dependency> </dependencies> </project>
启动类:
package com.itheima; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; /** * @author ljh * @version 1.0 * @date 2021/2/23 10:21 * @description 标题 * @package com.itheima */ @SpringBootApplication @EnableDiscoveryClient public class GatewayAdminApplication { public static void main(String[] args) { SpringApplication.run(GatewayAdminApplication.class,args); } }
application.yml
spring: profiles: active: dev --- server: port: 6001 spring: application: name: leadnews-admin-gateway profiles: dev cloud: nacos: server-addr: 192.168.211.136:8848 discovery: server-addr: ${spring.cloud.nacos.server-addr} gateway: globalcors: cors-configurations: '[/**]': # 匹配所有请求 allowedOrigins: "*" #跨域处理 允许所有的域 allowedHeaders: "*" allowedMethods: # 支持的方法 - GET - POST - PUT - DELETE routes: # 平台管理 - id: admin uri: lb://leadnews-admin predicates: - Path=/admin/** filters: - StripPrefix= 1 --- server: port: 6001 spring: application: name: leadnews-admin-gateway profiles: test cloud: nacos: server-addr: 192.168.211.136:8848 discovery: server-addr: ${spring.cloud.nacos.server-addr} gateway: globalcors: cors-configurations: '[/**]': # 匹配所有请求 allowedOrigins: "*" #跨域处理 允许所有的域 allowedHeaders: "*" allowedMethods: # 支持的方法 - GET - POST - PUT - DELETE routes: # 平台管理 - id: admin uri: lb://leadnews-admin predicates: - Path=/admin/** filters: - StripPrefix= 1 --- server: port: 6001 spring: application: name: leadnews-admin-gateway profiles: pro cloud: nacos: server-addr: 192.168.211.136:8848 discovery: server-addr: ${spring.cloud.nacos.server-addr} gateway: globalcors: cors-configurations: '[/**]': # 匹配所有请求 allowedOrigins: "*" #跨域处理 允许所有的域 allowedHeaders: "*" allowedMethods: # 支持的方法 - GET - POST - PUT - DELETE routes: # 平台管理 - id: admin uri: lb://leadnews-admin predicates: - Path=/admin/** filters: - StripPrefix= 1
重点解释:
- Path=/admin/** 表示以/admin开头的路径全部路由到admin微服务中 例如:http://localhost:6001/admin/xxx/yyy --->路由到http://9001/xxx/yyy
有了网关之后,我们应当从网关开始访问,并通过网关实现权限校验等功能,结构图和流程图如下
思路分析:
在网关微服务中新建全局过滤器:
(1)编写全局过滤器
package com.itheima.gatewayadmin.filter; import com.itheima.common.constants.SystemConstants; import com.itheima.common.util.AppJwtUtil; import org.checkerframework.checker.units.qual.A; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.core.Ordered; import org.springframework.http.HttpStatus; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; /** * @author ljh * @version 1.0 * @date 2021/8/1 09:06 * @description 标题 * @package com.itheima.gatewayadmin.filter */ @Component public class AuthorizeFilter implements GlobalFilter, Ordered { @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { //+ 1.先获取请求和响应对象 ServerHttpRequest request = exchange.getRequest(); ServerHttpResponse response = exchange.getResponse(); // /get /a post /a //+ 2.判断当前的请求 是否 是登录的请求 如果是 ,则放行 String path = request.getURI().getPath(); if(path.startsWith("/admin/admin/login")){ return chain.filter(exchange); } //+ 3.获取页面传递过来的请求头中的令牌数据 如果获取不到 返回错误(401) String token = request.getHeaders().getFirst("token"); if(StringUtils.isEmpty(token)){ response.setStatusCode(HttpStatus.UNAUTHORIZED); return response.setComplete();//完成响应 返回 后面就不执行了 } //+ 4.校验令牌 校验失败 返回错误401 //通过jwt 来校验 if(SystemConstants.JWT_OK!=AppJwtUtil.verifyToken(token)){ response.setStatusCode(HttpStatus.UNAUTHORIZED); return response.setComplete();//完成响应 返回 后面就不执行了 } //+ 5.校验成功 放行 return chain.filter(exchange); } //过滤器的执行的优先级的设置 值越低 优先级越高 @Override public int getOrder() { return 0; } }
测试:注意,从网关开始访问:
先登录:
携带token再进行访问:
APP端用户在app端进行实名认证
平台后台管理系统可以查看实名认证的信息,便于进行审核。
当用户在app前端进行了实名认证请求之后会自动往ap_user_realname表中加入数据
我们的需求是:
需要实现平台的用户实名认证的列表信息查询,如下图:
(1)新建微服务工程:itheima-leadnews-service-user和对应api工程
itheima-leadnews-service-user工程的pom.xml:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>itheima-leadnews-service</artifactId> <groupId>com.itheima</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>itheima-leadnews-service-user</artifactId> <dependencies> <dependency> <groupId>com.itheima</groupId> <artifactId>itheima-leadnews-user-api</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>com.itheima</groupId> <artifactId>itheima-leadnews-common-db</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>com.itheima</groupId> <artifactId>itheima-leadnews-core-controller</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies> </project>
(2)创建启动类
package com.itheima; import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.context.annotation.Bean; /** * @author ljh * @version 1.0 * @date 2021/2/23 16:45 * @description 标题 * @package com.itheima */ @SpringBootApplication @EnableDiscoveryClient @MapperScan(basePackages = "com.itheima.user.mapper") public class UserApplication { public static void main(String[] args) { SpringApplication.run(UserApplication.class,args); } @Bean public PaginationInterceptor paginationInterceptor() { return new PaginationInterceptor(); } }
(3)在resources下新建application.yml
spring: profiles: active: dev --- server: port: 9002 spring: application: name: leadnews-user profiles: dev datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://192.168.211.136:3306/leadnews_user?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=&serverTimezone=Asia/Shanghai username: root password: 123456 cloud: nacos: server-addr: 192.168.211.136:8848 discovery: server-addr: ${spring.cloud.nacos.server-addr} # 设置Mapper接口所对应的XML文件位置,如果你在Mapper接口中有自定义方法,需要进行该配置 mybatis-plus: mapper-locations: classpath*:mapper/*.xml # 设置别名包扫描路径,通过该属性可以给包中的类注册别名 type-aliases-package: com.itheima.user.pojo --- server: port: 9002 spring: application: name: leadnews-user profiles: pro datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://192.168.211.136:3306/leadnews_user?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=&serverTimezone=Asia/Shanghai username: root password: 123456 cloud: nacos: server-addr: 192.168.211.136:8848 discovery: server-addr: ${spring.cloud.nacos.server-addr} # 设置Mapper接口所对应的XML文件位置,如果你在Mapper接口中有自定义方法,需要进行该配置 mybatis-plus: mapper-locations: classpath*:mapper/*.xml # 设置别名包扫描路径,通过该属性可以给包中的类注册别名 type-aliases-package: com.itheima.user.pojo --- server: port: 9002 spring: application: name: leadnews-user profiles: test datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://192.168.211.136:3306/leadnews_user?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai username: root password: 123456 cloud: nacos: server-addr: 192.168.211.136:8848 discovery: server-addr: ${spring.cloud.nacos.server-addr} # 设置Mapper接口所对应的XML文件位置,如果你在Mapper接口中有自定义方法,需要进行该配置 mybatis-plus: mapper-locations: classpath*:mapper/*.xml # 设置别名包扫描路径,通过该属性可以给包中的类注册别名 type-aliases-package: com.itheima.user.pojo
(4)使用代码生成器生成相关的controller service mapper pojo
执行生成动作,生成如下:
copy代码到对应工程中,该操作不在文档中演示了。copy之后查询的功能就已经实现了。
此时用户实名认证列表查询功能已经完成。启动服务并测试如下
当用户提交了实名认证之后,认证信息便已经存储到了数据库表中。我们平台需要进行审核。
审核流程说明如下:
如图刚才的流程对应的微服务的数据库为如下图所示:
上图解释:
1.当平台管理审核人员进行点击审核通过的按钮之后 2.用户微服务接收到请求进行数据操作 实名认证状态 3.并同时通过Feign调用实现自媒体微服务的业务操作 创建自媒体账号 4.并同时通过Feign调用实现文章微服务创建作者信息。
操作步骤说明:
1.搭建 自媒体微服务 和 文章微服务 2.实现审核通过 用户微服务 修改状态 3.创建feign 分别远程调用 自媒体和文章
(1)创建自媒体微服务
pom.xml:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>itheima-leadnews-service</artifactId> <groupId>com.itheima</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>itheima-leadnews-service-wemedia</artifactId> <dependencies> <dependency> <groupId>com.itheima</groupId> <artifactId>itheima-leadnews-common-db</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>com.itheima</groupId> <artifactId>itheima-leadnews-wemedia-api</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>com.itheima</groupId> <artifactId>itheima-leadnews-core-controller</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies> </project>
(2)创建启动类和yml及相关配置
package com.itheima; import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.context.annotation.Bean; /** * @author ljh * @version 1.0 * @date 2021/2/25 15:22 * @description 标题 * @package com.itheima */ @SpringBootApplication @EnableDiscoveryClient @MapperScan(basePackages = "com.itheima.media.mapper") public class MediaApplication { public static void main(String[] args) { SpringApplication.run(MediaApplication.class, args); } @Bean public PaginationInterceptor paginationInterceptor() { return new PaginationInterceptor(); } }
application.yml
spring: profiles: active: dev --- server: port: 9004 spring: profiles: dev application: name: leadnews-wemedia cloud: nacos: discovery: server-addr: 192.168.211.136:8848 datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://192.168.211.136:3306/leadnews_wemedia?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=&serverTimezone=Asia/Shanghai username: root password: 123456 # 设置Mapper接口所对应的XML文件位置,如果你在Mapper接口中有自定义方法,需要进行该配置 mybatis-plus: mapper-locations: classpath*:mapper/*.xml # 设置别名包扫描路径,通过该属性可以给包中的类注册别名 type-aliases-package: com.itheima.media.pojo --- server: port: 9004 spring: profiles: test application: name: leadnews-wemedia cloud: nacos: discovery: server-addr: 192.168.211.136:8848 datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://192.168.211.136:3306/leadnews_wemedia?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=&serverTimezone=Asia/Shanghai username: root password: 123456 # 设置Mapper接口所对应的XML文件位置,如果你在Mapper接口中有自定义方法,需要进行该配置 mybatis-plus: mapper-locations: classpath*:mapper/*.xml # 设置别名包扫描路径,通过该属性可以给包中的类注册别名 type-aliases-package: com.itheima.media.pojo --- server: port: 9004 spring: profiles: pro application: name: leadnews-wemedia cloud: nacos: discovery: server-addr: 192.168.211.136:8848 datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://192.168.211.136:3306/leadnews_wemedia?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=&serverTimezone=Asia/Shanghai username: root password: 123456 # 设置Mapper接口所对应的XML文件位置,如果你在Mapper接口中有自定义方法,需要进行该配置 mybatis-plus: mapper-locations: classpath*:mapper/*.xml # 设置别名包扫描路径,通过该属性可以给包中的类注册别名 type-aliases-package: com.itheima.media.pojo
(3)使用代码生成器生成相关代码放到对应位置,copy步骤略,最终效果如下
(1)创建文章微服务
pom.xml:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>itheima-leadnews-service</artifactId> <groupId>com.itheima</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>itheima-leadnews-service-article</artifactId> <dependencies> <dependency> <groupId>com.itheima</groupId> <artifactId>itheima-leadnews-article-api</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>com.itheima</groupId> <artifactId>itheima-leadnews-common-db</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>com.itheima</groupId> <artifactId>itheima-leadnews-core-controller</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies> </project>
(2)新建启动类和yaml配置
package com.itheima; import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.context.annotation.Bean; /** * @author ljh * @version 1.0 * @date 2021/2/25 15:48 * @description 标题 * @package com.itheima */ @SpringBootApplication @EnableDiscoveryClient @MapperScan(basePackages = "com.itheima.article.mapper") public class ArticleApplication { public static void main(String[] args) { SpringApplication.run(ArticleApplication.class, args); } @Bean public PaginationInterceptor paginationInterceptor() { return new PaginationInterceptor(); } }
spring: profiles: active: dev --- server: port: 9003 spring: application: name: leadnews-article profiles: dev datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://192.168.211.136:3306/leadnews_article?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=&serverTimezone=Asia/Shanghai username: root password: 123456 cloud: nacos: server-addr: 192.168.211.136:8848 discovery: server-addr: ${spring.cloud.nacos.server-addr} # 设置Mapper接口所对应的XML文件位置,如果你在Mapper接口中有自定义方法,需要进行该配置 mybatis-plus: mapper-locations: classpath*:mapper/*.xml # 设置别名包扫描路径,通过该属性可以给包中的类注册别名 type-aliases-package: com.itheima.article.pojo --- server: port: 9003 spring: application: name: leadnews-user profiles: pro datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://192.168.211.136:3306/leadnews_article?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=&serverTimezone=Asia/Shanghai username: root password: 123456 cloud: nacos: server-addr: 192.168.211.136:8848 discovery: server-addr: ${spring.cloud.nacos.server-addr} # 设置Mapper接口所对应的XML文件位置,如果你在Mapper接口中有自定义方法,需要进行该配置 mybatis-plus: mapper-locations: classpath*:mapper/*.xml # 设置别名包扫描路径,通过该属性可以给包中的类注册别名 type-aliases-package: com.itheima.article.pojo --- server: port: 9003 spring: application: name: leadnews-user profiles: test datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://192.168.211.136:3306/leadnews_article?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai username: root password: 123456 cloud: nacos: server-addr: 192.168.211.136:8848 discovery: server-addr: ${spring.cloud.nacos.server-addr} # 设置Mapper接口所对应的XML文件位置,如果你在Mapper接口中有自定义方法,需要进行该配置 mybatis-plus: mapper-locations: classpath*:mapper/*.xml # 设置别名包扫描路径,通过该属性可以给包中的类注册别名 type-aliases-package: com.itheima.article.pojo
(3)代码生成器生成代码放到对应位置,copy步骤省略 ,最终效果如下
通过:
点击通过之后,直接发送请求到后台 后台接收到请求之后更新状态即可,我们可以定义一个controller 和service dao 接收该请求的处理即可。
请求:/apUserRealname/pass/{id} PUT 参数:id 用户实名认证的记录的ID 返回值:Result 返回成功与否即可
驳回:
点击提交之后,直接发送请求 携带拒绝的原因信息到后台 后台接收到请求之后更新状态即可。
我们可以定义一个controller 和service dao 接收该请求的处理即可。
表结构如下:
(1)编写controller
//审核通过 @PutMapping("/pass/{id}") public Result pass(@PathVariable(name="id") Integer id){ apUserRealnameService.pass(id); return Result.ok(); } //驳回 @PutMapping("/reject/{id}") public Result reject(@PathVariable(name="id")Integer id,@RequestParam(required = true,name="reason") String reason){ apUserRealnameService.reject(id,reason); return Result.ok(); }
(2)编写业务代码
实现驳回功能:
业务接口:
public interface ApUserRealnameService extends IService<ApUserRealname> { void reject(Integer id, String reason); }
实现类:
@Service public class ApUserRealnameServiceImpl extends ServiceImpl<ApUserRealnameMapper, ApUserRealname> implements ApUserRealnameService { @Autowired private ApUserRealnameMapper apUserRealnameMapper; @Override public void reject(Integer id, String reason) { ApUserRealname apUserRealname = new ApUserRealname(); apUserRealname.setId(id); apUserRealname.setReason(reason); //更新时间 apUserRealname.setUpdatedTime(LocalDateTime.now()); //设置状态为审核失败 apUserRealname.setStatus(BusinessConstants.ApUserRealnameConstants.SHENHE_FAILE); apUserRealnameMapper.updateById(apUserRealname); } }
创建常量接口:
public interface BusinessConstants { //实名认证相关 public static class ApUserRealnameConstants{ //创建中 public static final Integer SHENHE_ING=0; //待审核 public static final Integer SHENHE_WARTING=1; //审核失败 public static final Integer SHENHE_FAILE=2; //审核通过 public static final Integer SHENHE_SUCESS=9; } }
常量接口 用于 做业务常量 由于有许多的业务,所以在接口中添加静态内部类进行设置。用来区分不同的业务。
实现审核通过功能:
业务接口:
public interface ApUserRealnameService extends IService<ApUserRealname> { //审核通过 void pass(Integer id); //略 }
实现类:
@Autowired private ApUserRealnameMapper apUserRealnameMapper; @Override public void pass(Integer id) { ApUserRealname entity = new ApUserRealname(); entity.setId(id); //审核通过 entity.setStatus(BusinessConstants.ApUserRealnameConstants.SHENHE_SUCESS); apUserRealnameMapper.updateById(entity); //todo feign远程调用 自媒体服务 创建自媒体账号 //todo feign远程调用 文章微服务 创建作者账号 }
相关表如下:
自媒体表:
作者表:
操作步骤如下:
(1) 添加依赖 (2) 创建feign接口 (3) 实现feign接口(业务实现) (4) 调用Feign接口实现创建自媒体账号
(1) 添加依赖
由于feign 每一个都要用到,以及还需要用到common中的result类,所以在itheima-leadnews-api下添加依赖如下:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>itheima-leadnews</artifactId> <groupId>com.itheima</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>itheima-leadnews-api</artifactId> <packaging>pom</packaging> <description>所有feign pojo所在父工程</description> <modules> <module>itheima-leadnews-admin-api</module> <module>itheima-leadnews-user-api</module> <module>itheima-leadnews-wemedia-api</module> <module>itheima-leadnews-article-api</module> </modules> <dependencies> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <dependency> <groupId>com.itheima</groupId> <artifactId>itheima-leadnews-common</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies> </project>
(2) 创建feign接口(位置如图)
@FeignClient(name="leadnews-wemedia",path = "/wmUser") public interface WmUserFeign { //创建自媒体账户信息 @PostMapping public Result save(@RequestBody WmUser wmUser); /** * 根据apUserId获取 * @param apUserId * @return */ @GetMapping("/one/{apUserId}") public WmUser getByApUserId(@PathVariable(name="apUserId") Integer apUserId); }
(3) 实现feign接口(业务实现)在controller中
@GetMapping("/one/{apUserId}") public WmUser getByApUserId(@PathVariable(name="apUserId") Integer apUserId){ QueryWrapper<WmUser> queryWrapper = new QueryWrapper<WmUser>(); queryWrapper.eq("ap_user_id",apUserId); WmUser wmUser = wmUserService.getOne(queryWrapper); return wmUser; }
还有一个接口不需要实现:因为抽象类中早已实现了:
(4)调用Feign接口实现创建自媒体账号
itheima-leadnews-service-user微服中添加使用到的依赖并Feign开启接口扫描:
<dependency> <groupId>com.itheima</groupId> <artifactId>itheima-leadnews-wemedia-api</artifactId> <version>1.0-SNAPSHOT</version> </dependency>
XML如下图:
开启扫描如下图:*** 号代表占位符,表示任意的包名**
通过审核业务实现类中实现远程调用:
private static final Logger logger = LoggerFactory.getLogger(ApUserRealnameServiceImpl.class); @Autowired private ApUserRealnameMapper apUserRealnameMapper; @Autowired private WmUserFeign wmUserFeign; @Autowired private ApUserMapper apUserMapper; @Override public void pass(Integer id) { ApUserRealname entity = new ApUserRealname(); entity.setId(id); //审核通过 entity.setStatus(BusinessConstants.ApUserRealnameConstants.SHENHE_SUCESS); apUserRealnameMapper.updateById(entity); //todo feign远程调用 自媒体服务 创建自媒体账号 ApUserRealname apUserRealname = apUserRealnameMapper.selectById(id); if (apUserRealname != null) { ApUser apUser = apUserMapper.selectById(apUserRealname.getUserId()); //判断是否已经存在 WmUser wmUser = wmUserFeign.getByApUserId(apUser.getId()); if (wmUser == null) { wmUser = new WmUser(); //copy数据到wmUser中 BeanUtils.copyProperties(apUser, wmUser); //设置状态 wmUser.setStatus(BusinessConstants.WmUserConstants.WM_USER_OK); //设置APP用户的ID wmUser.setApUserId(apUser.getId()); //设置创建时间 wmUser.setCreatedTime(LocalDateTime.now()); Result result = wmUserFeign.save(wmUser); if (result.isSuccess()) { logger.info("自媒体账号创建成功"); } } //todo feign远程调用 文章微服务 创建作者账号 } }
在业务接口常量接口中 创建静态内部常量类:
public static class WmUserConstants{ //有效 public static final Integer WM_USER_OK= 9; //冻结 public static final Integer WM_USER_LOCKED= 0; //永久失效 public static final Integer WM_USER_INVALID= 1; }
操作步骤如下:
(1) 添加依赖 (2) 创建feign接口 (3) 实现feign接口(业务实现) (4)调用Feign接口实现创建作者账号
(1) 添加依赖
忽略,上一个步骤已经做了。就是添加openfeign
(2) 创建feign接口
@FeignClient(name="leadnews-article",path = "/apAuthor") public interface ApAuthorFeign { //保存作者账号 @PostMapping public Result save(@RequestBody ApAuthor apAuthor); /** * 根据APP用户的ID 获取 作者信息 * @param apUserId * @return */ @GetMapping("/one/{apUserId}") public ApAuthor getByApUserId(@PathVariable(name="apUserId")Integer apUserId); }
(3) 实现feign接口(业务实现)
/** * 根据app用户的ID 获取作者信息 * @param apUserId * @return */ @GetMapping("/one/{apUserId}") public ApAuthor getByApUserId(@PathVariable(name="apUserId")Integer apUserId){ QueryWrapper<ApAuthor> queryWrapper = new QueryWrapper<ApAuthor>(); queryWrapper.eq("user_id",apUserId); ApAuthor apAuthor = apAuthorService.getOne(queryWrapper); return apAuthor; }
还有一个保存作者 不需要实现了,因为咱们抽象类中已经完成,你只需要进行声明即可。
(4)添加依赖并调用Feign接口实现创建作者账号
<dependency> <groupId>com.itheima</groupId> <artifactId>itheima-leadnews-wemedia-api</artifactId> <version>1.0-SNAPSHOT</version> </dependency>
在通过审核的业务代码的实现类中进行远程调用:
private static final Logger logger = LoggerFactory.getLogger(ApUserRealnameServiceImpl.class); @Autowired private ApUserRealnameMapper apUserRealnameMapper; @Autowired private WmUserFeign wmUserFeign; @Autowired private ApAuthorFeign apAuthorFeign; @Autowired private ApUserMapper apUserMapper; @Override public void pass(Integer id) { ApUserRealname entity = new ApUserRealname(); entity.setId(id); //审核通过 entity.setStatus(BusinessConstants.ApUserRealnameConstants.SHENHE_SUCESS); apUserRealnameMapper.updateById(entity); //todo feign远程调用 自媒体服务 创建自媒体账号 ApUserRealname apUserRealname = apUserRealnameMapper.selectById(id); if (apUserRealname != null) { ApUser apUser = apUserMapper.selectById(apUserRealname.getUserId()); //判断是否已经存在 WmUser wmUser = wmUserFeign.getByApUserId(apUser.getId()); if (wmUser == null) { //copy数据到wmUser中 wmUser = new WmUser(); BeanUtils.copyProperties(apUser, wmUser); //设置状态 wmUser.setStatus(BusinessConstants.WmUserConstants.WM_USER_OK); //设置APP用户的ID wmUser.setApUserId(apUser.getId()); //设置创建时间 wmUser.setCreatedTime(LocalDateTime.now()); Result<WmUser> result = wmUserFeign.save(wmUser); if (result.isSuccess()) { logger.info("自媒体账号创建成功"); wmUser = result.getData(); } } //todo feign远程调用 文章微服务 创建作者账号 ApAuthor apAuthor = apAuthorFeign.getByApUserId(apUser.getId()); if(apAuthor==null){ apAuthor = new ApAuthor(); //作者名称就是登录名 apAuthor.setName(apUser.getName()); apAuthor.setType(BusinessConstants.ApAuthorConstants.A_MEDIA_USER); apAuthor.setCreatedTime(LocalDateTime.now()); apAuthor.setUserId(apUser.getId()); apAuthor.setWmUserId(wmUser.getId()); apAuthorFeign.save(apAuthor); } } }
创建常量类:
public static class ApAuthorConstants{ /** * 平台自媒体人 */ public static final Integer A_MEDIA_USER= 2; //合作商 public static final Integer A_MEDIA_SELLER= 1; //普通作者 public static final Integer A_MEDIA_ZERO= 0; }
在admin网关的yml文件中进行配置如下:
- id: user uri: lb://leadnews-user predicates: - Path=/user/** filters: - StripPrefix= 1
通过网关登录,登录之后再进行测试校验。
我们发现,如果每一个feign都有相关的针对单表的操作,那么每一个都写一个样的代码是不合理的而且是麻烦的,那么我们可以参考抽取controller一样的方式去抽取feign ,我们不搞那么复杂,因为feign只是接口声明,
子类继承接口即可。
如图,就是 每一个业务接口 继承 定义了抽取了通用的方法的接口 ,然后每一个业务接口如果是基本的CRUD,都不用进行声明了。直接调用即可。
创建核心feign接口
代码如下:
package com.itheima.core.feign; import com.itheima.common.pojo.PageInfo; import com.itheima.common.pojo.PageRequestDto; import com.itheima.common.pojo.Result; import org.springframework.web.bind.annotation.*; import java.io.Serializable; import java.util.List; /** * @author ljh * @version 1.0 * @date 2021/2/26 09:06 * @description 标题 * @package com.itheima.core.feign */ public interface CoreFeign<T> { @DeleteMapping("/{id}") public Result deleteById(@PathVariable(name = "id") Serializable id) ; /** * 添加记录 * * @param record * @return */ @PostMapping public Result<T> save(@RequestBody T record) ; //更新数据 @PutMapping public Result updateByPrimaryKey(@RequestBody T record) ; @GetMapping("/{id}") public Result<T> findById(@PathVariable(name = "id") Serializable id) ; @GetMapping public Result<List<T>> findAll() ; /** * 通用条件分页查询 * * @param pageRequestDto * @return */ @PostMapping(value = "/search") public Result<PageInfo<T>> findByPage(@RequestBody PageRequestDto<T> pageRequestDto) ; }
添加依赖:
修改Feign接口:
再测试也是一样的效果,这样就不用重复编写了,
但是要注意的是:请求路径不能和父接口(coreFeign)中的一样。
在使用默认的feign的时候,feign底层实际上是使用restTemplete 而restTemplate底层又使用到了httpclient,默认使用java自带的httpUrlConnection。我们是可以使用okhttp ,默认的feign调用httpUrlConnection每次都会创建一个链接对象。效率较低。所以使用okhttp来替换,它可以使用连接池。调用效率较高。
在itheima-leadnews-core-feign微服务中的pom.xml中添加依赖
<dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-okhttp</artifactId> </dependency>
在需要用到feign的微服务中配置如下即可
feign: client: config: default: # default指定的是所有的 被调用方 都设置为该配置超时时间,可以设置为某一个微服务对应的服务名 connectTimeout: 5000 # 链接超时时间 readTimeout: 5000 # 读取的超时时间 okhttp: enabled: true httpclient: enabled: false
每次我们测试都需要通过POSTMAN 结合网关来测试,这样麻烦,我们可以集成到knife4j上去进行测试。如下:
(1)创建三个类在网关中
从此处copy
(2)admin微服务中创建配置类:如果有则不用创建了。
package com.itheima.admin.config; import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import springfox.bean.validators.configuration.BeanValidatorPluginsConfiguration; import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.PathSelectors; import springfox.documentation.builders.RequestHandlerSelectors; import springfox.documentation.service.ApiInfo; import springfox.documentation.service.Contact; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.swagger2.annotations.EnableSwagger2; import java.util.HashSet; @Configuration @EnableSwagger2//启用swagger @EnableKnife4j//启用Knife4j @Import(BeanValidatorPluginsConfiguration.class) public class SwaggerConfiguration { /*@Bean public Docket buildDocket() { HashSet<String> strings = new HashSet<>(); strings.add("application/json"); return new Docket(DocumentationType.SWAGGER_2) .apiInfo(buildApiInfo()) //设置返回值数据类型为json .produces(strings)//设置响应的结果的数据类型 变成JSON .select() // 要扫描的API(Controller)基础包 .apis(RequestHandlerSelectors.basePackage("com.itheima.admin.controller")) //针对那些路径生成API文档 /** .paths(PathSelectors.any()) .build(); } private ApiInfo buildApiInfo() { Contact contact = new Contact("黑马程序员","",""); return new ApiInfoBuilder() .title("黑马头条-平台管理API文档") .description("平台管理服务api") .contact(contact) .version("1.0.0").build(); }*/ @Bean public Docket buildDocket() { HashSet<String> strings = new HashSet<>(); strings.add("application/json"); Docket docket=new Docket(DocumentationType.SWAGGER_2) .apiInfo(buildApiInfo()) //设置返回数据类型 .produces(strings) //分组名称 //.groupName("1.0") .select() //这里指定Controller扫描包路径 .apis(RequestHandlerSelectors.basePackage("com.itheima.admin.controller")) //** .paths(PathSelectors.any()) .build(); return docket; } private ApiInfo buildApiInfo() { Contact contact = new Contact("黑马程序员","",""); return new ApiInfoBuilder() .title("黑马头条-平台管理API文档") .description("平台管理服务api") .contact(contact) .version("1.0.0").build(); } }
(3)过滤器中添加如下代码
(4)启动网关 和微服务测试:通过网关地址进行访问
登录:
根据登录之后获取到的token 在全局参数中添加之后,就可以直接测试了:
测试访问: