# 第三章 app端用戶認證
目標
-
能夠完成網關統一鑒權的功能
-
能夠完成認證用戶列表查詢
-
能夠熟悉app端用戶認證審核流程
-
能夠完成app用戶審核代碼開發
1 網關校驗jwt
1.1 微服務網關概述
不同的微服務一般會有不同的網絡地址,而外部客戶端可能需要調用多個服務的接口才能完成一個業務需求,如果讓客戶端直接與各個微服務通信,會有以下的問題:
- 客戶端會多次請求不同的微服務,增加了客戶端的復雜性
- 存在跨域請求,在一定場景下處理相對復雜
- 認證復雜,每個服務都需要獨立認證
- 難以重構,隨著項目的迭代,可能需要重新劃分微服務。例如,可能將多個服務合并成一個或者將一個服務拆分成多個。如果客戶端直接與微服務通信,那么重構將會很難實施
- 某些微服務可能使用了防火墻 / 瀏覽器不友好的協議,直接訪問會有一定的困難
以上這些問題可以借助網關解決。
網關是介于客戶端和服務器端之間的中間層,所有的外部請求都會先經過 網關這一層。也就是說,API 的實現方面更多的考慮業務邏輯,而安全、性能、監控可以交由 網關來做,這樣既提高業務靈活性又不缺安全性,典型的架構圖如圖所示:

優點如下:
- 安全 ,只有網關系統對外進行暴露,微服務可以隱藏在內網,通過防火墻保護。
- 易于監控。可以在網關收集監控數據并將其推送到外部系統進行分析。
- 易于認證。可以在網關上進行認證,然后再將請求轉發到后端的微服務,而無須在每個微服務中進行認證。
- 減少了客戶端與各個微服務之間的交互次數
- 易于統一授權。
總結:微服務網關就是一個系統,通過暴露該微服務網關系統,方便我們進行相關的鑒權,安全控制,日志統一處理,易于監控的相關功能。
實現微服務網關的技術有很多,
- nginx Nginx (engine x) 是一個高性能的HTTP和反向代理web服務器,同時也提供了IMAP/POP3/SMTP服務
- zuul ,Zuul 是 Netflix 出品的一個基于 JVM 路由和服務端的負載均衡器。
- spring-cloud-gateway, 是spring 出品的 基于spring 的網關項目,集成斷路器,路徑重寫,性能比Zuul好。
我們使用gateway這個網關技術,無縫銜接到基于spring cloud的微服務開發中來。
gateway官網:
https://spring.io/projects/spring-cloud-gateway
1.2 搭建gatway網關微服務
(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.3 全局過濾器實現jwt校驗
有了網關之后,我們應當從網關開始訪問,并通過網關實現權限校驗等功能,結構圖和流程圖如下


思路分析:
- 用戶進入網關開始登陸,網關過濾器進行判斷,如果是登錄,則路由到后臺管理微服務進行登錄
- 用戶登錄成功,后臺管理微服務簽發JWT TOKEN信息返回給用戶
- 用戶再次進入網關開始訪問,網關過濾器接收用戶攜帶的TOKEN
- 網關過濾器解析TOKEN ,判斷是否有權限,如果有,則放行,如果沒有則返回未認證錯誤
在網關微服務中新建全局過濾器:
(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再進行訪問:

2 APP端用戶實名認流程說明
2.1 需求分析
APP端用戶在app端進行實名認證

平臺后臺管理系統可以查看實名認證的信息,便于進行審核。

當用戶在app前端進行了實名認證請求之后會自動往ap_user_realname表中加入數據

我們的需求是:
需要實現平臺的用戶實名認證的列表信息查詢,如下圖:

2.2 搭建user微服務
(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之后查詢的功能就已經實現了。
此時用戶實名認證列表查詢功能已經完成。啟動服務并測試如下

3 app端用戶認證后審核
3.1 需求分析
當用戶提交了實名認證之后,認證信息便已經存儲到了數據庫表中。我們平臺需要進行審核。
審核流程說明如下:

- 在app端的個人中心用戶可以實名認證,需要材料為:姓名、身份證號、身份證正面照、身份證反面照、手持照片、活體照片(通過微笑、眨眼、張嘴、搖頭、點頭等組合動作,確保操作的為真實活體人臉。),當用戶提交審核后就到了后端讓運營管理人員進行審核
- 平臺運營端查看用戶認證信息,進行審核,其中審核包括了用戶身份審核,需要對接公安系統校驗身份證信息
- 用戶通過審核后需要開通自媒體賬號(該賬號的用戶名和密碼與app一致)
- 用戶通過審核后需要在article中在作者表中新建一個作者信息
如圖剛才的流程對應的微服務的數據庫為如下圖所示:

3.2 實現思路分析

上圖解釋:
1.當平臺管理審核人員進行點擊審核通過的按鈕之后
2.用戶微服務接收到請求進行數據操作 實名認證狀態
3.并同時通過Feign調用實現自媒體微服務的業務操作 創建自媒體賬號
4.并同時通過Feign調用實現文章微服務創建作者信息。
操作步驟說明:
1.搭建 自媒體微服務 和 文章微服務
2.實現審核通過 用戶微服務 修改狀態
3.創建feign 分別遠程調用 自媒體和文章
4 搭建微服務
4.1 搭建自媒體微服務
(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步驟略,最終效果如下

4.2 搭建文章微服務
(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步驟省略 ,最終效果如下

5 實現業務功能
5.1 實現審核用戶通過和駁回
5.1.1 需求分析
通過:

點擊通過之后,直接發送請求到后臺 后臺接收到請求之后更新狀態即可,我們可以定義一個controller 和service dao 接收該請求的處理即可。
請求:/apUserRealname/pass/{id} PUT
參數:id 用戶實名認證的記錄的ID
返回值:Result 返回成功與否即可
駁回:

點擊提交之后,直接發送請求 攜帶拒絕的原因信息到后臺 后臺接收到請求之后更新狀態即可。
我們可以定義一個controller 和service dao 接收該請求的處理即可。
表結構如下:

5.1.2 功能實現
(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遠程調用 文章微服務 創建作者賬號
}

5.2 實現Feign遠程調用

相關表如下:
自媒體表:

作者表:

5.2.1 實現自媒體遠程調用
操作步驟如下:
(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;
}

5.2.2 實現文章遠程調用
操作步驟如下:
(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;
}
5.2.3 網關對接user微服務
在admin網關的yml文件中進行配置如下:

- id: user
uri: lb://leadnews-user
predicates:
- Path=/user/**
filters:
- StripPrefix= 1
5.3 測試
通過網關登錄,登錄之后再進行測試校驗。
6 feign抽取
6.1 分析
我們發現,如果每一個feign都有相關的針對單表的操作,那么每一個都寫一個樣的代碼是不合理的而且是麻煩的,那么我們可以參考抽取controller一樣的方式去抽取feign ,我們不搞那么復雜,因為feign只是接口聲明,
子類繼承接口即可。

如圖,就是 每一個業務接口 繼承 定義了抽取了通用的方法的接口 ,然后每一個業務接口如果是基本的CRUD,都不用進行聲明了。直接調用即可。
6.2 代碼實現抽取
創建核心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)中的一樣。
7 使用okhttp(可選項)
? 在使用默認的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
8 網關整合knife4j
每次我們測試都需要通過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 在全局參數中添加之后,就可以直接測試了:

測試訪問:

浙公網安備 33010602011771號