2024/9/3黑马头条跟学笔记(一)

ops/2024/10/19 3:23:41/

D1

视频链接

Day1-05-nacos环境搭建_哔哩哔哩_bilibili

内容介绍

  1. 搭建微服务开发环境,登录接口包含注册中心和nacos配置中心

    服务端用户…微服务。网关负载均衡转发接口请求

    实现微服务间互相通信

  2. 接口测试

  3. 前后端联调

image-20240902072116857

前置知识

image-20240902073023383

背景介绍

image-20240902073303445

类似今日头条,qq看点

功能架构图


用户前台,内容浏览,登录注册。。。类似于看小说的人

用户后台,内容发布,私信管理。。。类似于小说家

超级管理员后台,对所有作者,其发布内容管理,类似于上帝

效果预览

image-20240902073958133

用户群众

image-20240902073903422

用户,吃瓜的——app用户端

自媒体人,卖瓜的——自媒体系统

管理员,城管——admin管理系统

技术选型

image-20240902074157948

image-20240902074217155

基础层负责前端nginx转发和cdn资源存储加快访问速度,前端框架组件及其第三方插件echarts快速开发

服务层也就是后端首先cloudGateway网关转发请求到各个服务接口,例如登录接口。。

同时还有数据层,持久化数据,mysql。mongodb。hbase。

中间件kafka,redis缓存

搜索索引elasticsearch,oss对象存储

收获

image-20240902074808270

技术运用

解决方案,分布式事务,任务调度,延迟队列,异步线程,热数据,评论系统,关注点赞

软硬编程思想,数据库设计,代码编写,测试用例,部署

课程大纲

image-20240902075030288

环境搭建

列表查看

热点计算(大数据)

cms前后台管理系统发布和审核(队列)

部署,迁移数据

实战(app端文章行为,评论系统,自媒体端评论管理,报表)

环境搭建

nacos环境搭建

  1. 安装一台虚拟机centos

  2. docker安装nacos

镜像拉取

docker pull nacos/nacos-server:1.2.0

创建容器

docker run --env MODE=standalone --name nacos --restart=always  -d -p 8848:8848 nacos/nacos-server:1.2.0//以下是国内无法拉取镜像的解决方案,具体请看往期文章
docker run --env MODE=standalone --name nacos --restart=always  -d -p 8848:8848 registry.cn-heyuan.aliyuncs.com/zwww/nacos-server:1.2.0

单机模式,名字,开机重启,守护式进程,端口号,容器镜像

访问地址 192.168.233.136:8848/nacos

image-20240902103007010

初始工程搭建

image-20240902103138943

maven仓库我是用的之前自己配置的

文件编码全设置为utf-8

image-20240902104002448

多模块开发——模块讲解

image-20240902104343937

common——所有服务都用到的,比如全局异常处理

feign-api——对外接口类似controller

gateway——网关转发

model——实体类

service——微服务

test——测试用例

utils——全局工具包

异常类

分为两种,

一种是已知的异常,如参数缺失,返回值自行DIY,

一种是未知异常系统异常,返回值固定

是的,你的理解是正确的。@Controller@ControllerAdvice 之间的工作流程可以总结如下:@ControllerAdvice会将异常丢给前端

需要(自定义异常实体类,异常捕获且进行返回或是log.info打印的类)

工作流程

  1. 请求接收
  • 控制器(使用 @Controller 注解的类)负责接收 HTTP 请求并处理业务逻辑。
  1. 异常抛出
  • 在处理请求的过程中,如果发生异常(如自定义异常或系统异常),控制器会抛出该异常。
  1. 异常捕获
  • 被抛出的异常会被 Spring 的异常处理机制捕获。如果该异常没有在控制器内部处理,它会被匹配到对应的 @ExceptionHandler 方法。
  • 这里,@ControllerAdvice 注解的类会起作用,检查是否有适用的异常处理方法。
  1. 返回响应
  • @ExceptionHandler 方法中,你可以定义如何处理异常,并返回一个响应实体(例如 ResponseEntity)。
  • 这个响应会作为控制器的返回值返回给前端,包含相应的状态码和错误信息。

初始化文件

image-20240902105431385

微服务引入了common依赖,当微服务初始容器时找到该文件并进行初始化,此时微服务可以使用该全局异常处理器

APP登录

需求分析

image-20240902105723646

登陆前只能看

登陆后能点赞评论关注

表结构

导入sql脚本

CREATE TABLE `ap_user` (`id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键',`salt` VARCHAR(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '密码、通信等加密盐',`name` VARCHAR(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '用户名',`password` VARCHAR(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '密码,md5加密',`phone` VARCHAR(11) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '手机号',`image` VARCHAR(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '头像',`sex` TINYINT(1) UNSIGNED DEFAULT NULL COMMENT '0 男\r\n            1 女\r\n            2 未知',`is_certification` TINYINT(1) UNSIGNED DEFAULT NULL COMMENT '0 未\r\n            1 是',`is_identity_authentication` TINYINT(1) DEFAULT NULL COMMENT '是否身份认证',`status` TINYINT(1) UNSIGNED DEFAULT NULL COMMENT '0正常\r\n            1锁定',`flag` TINYINT(1) UNSIGNED DEFAULT NULL COMMENT '0 普通用户\r\n            1 自媒体人\r\n            2 大V',`created_time` DATETIME DEFAULT NULL COMMENT '注册时间',PRIMARY KEY (`id`) USING BTREE
) ENGINE=INNODB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC COMMENT='APP用户信息表';

image-20240902113942940

实体类
java">package com.heima.model.user.pojos;import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;import java.io.Serializable;
import java.util.Date;/*** <p>* APP用户信息表* </p>** @author itheima*/
@Data
@TableName("ap_user")
public class ApUser implements Serializable {private static final long serialVersionUID = 1L;/*** 主键*/@TableId(value = "id", type = IdType.AUTO)private Integer id;/*** 密码、通信等加密盐*/@TableField("salt")private String salt;/*** 用户名*/@TableField("name")private String name;/*** 密码,md5加密*/@TableField("password")private String password;/*** 手机号*/@TableField("phone")private String phone;/*** 头像*/@TableField("image")private String image;/*** 0 男1 女2 未知*/@TableField("sex")private Boolean sex;/*** 0 未1 是*/@TableField("is_certification")private Boolean certification;/*** 是否身份认证*/@TableField("is_identity_authentication")private Boolean identityAuthentication;/*** 0正常1锁定*/@TableField("status")private Boolean status;/*** 0 普通用户1 自媒体人2 大V*/@TableField("flag")private Short flag;/*** 注册时间*/@TableField("created_time")private Date createdTime;}
手动加密(md5+加盐随机字符串)

image-20240902113020737

md5每次加密后都是一样的 会被别人试出来正确密码,因此我们每次MD5加生成一个随机字符串一起加密,这样即使是相同密码的md5加密出来的值也是不一样的

流程为,登陆时将输入的密码和盐字符串进行加密 比对数据库一开始加密后的密码

用户端微服务搭建

service模块

image-20240902114556089

 <dependencies><!-- 引入依赖模块 --><dependency><groupId>com.heima</groupId><artifactId>heima-leadnews-model</artifactId></dependency><dependency><groupId>com.heima</groupId><artifactId>heima-leadnews-common</artifactId></dependency><dependency><groupId>com.heima</groupId><artifactId>heima-leadnews-feign-api</artifactId></dependency><!-- Spring boot starter --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId></dependency></dependencies>

新建用户子模块

image-20240902114757319

新建引导类

先搞个user包出来,在user包下粘贴以下代码

java">package com.heima.user;import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;@SpringBootApplication
@EnableDiscoveryClient
@MapperScan("com.heima.user.mapper")
public class UserApplication {public static void main(String[] args) {SpringApplication.run(UserApplication.class,args);}
}

文件+包初始化

image-20240902115556148

bootstrap.yml

引导配置文件,相当于application.yml

ip改成你虚拟机的地址

server:port: 51801
spring:application:name: leadnews-usercloud:nacos:discovery:server-addr: 192.168.233.136:8848config:server-addr: 192.168.233.136:8848file-extension: yml
在nacos中创建配置文件

image-20240902115857428

image-20240902120150875

spring:datasource:driver-class-name: com.mysql.jdbc.Driverurl: jdbc:mysql://localhost:3306/leadnews_user?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTCusername: rootpassword: 123456
# 设置Mapper接口所对应的XML文件位置,如果你在Mapper接口中有自定义方法,需要进行该配置
mybatis-plus:mapper-locations: classpath*:mapper/*.xml# 设置别名包扫描路径,通过该属性可以给包中的类注册别名type-aliases-package: com.heima.model.user.pojos

这里的mybatis依赖在model的pom里引入了

logback.xml
<?xml version="1.0" encoding="UTF-8"?><configuration><!--定义日志文件的存储地址,使用绝对路径--><property name="LOG_HOME" value="e:/logs"/><!-- Console 输出设置 --><appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"><encoder><!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符--><pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern><charset>utf8</charset></encoder></appender><!-- 按照每天生成日志文件 --><appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"><rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><!--日志文件输出的文件名--><fileNamePattern>${LOG_HOME}/leadnews.%d{yyyy-MM-dd}.log</fileNamePattern></rollingPolicy><encoder><pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern></encoder></appender><!-- 异步输出 --><appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender"><!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 --><discardingThreshold>0</discardingThreshold><!-- 更改默认的队列的深度,该值会影响性能.默认值为256 --><queueSize>512</queueSize><!-- 添加附加的appender,最多只能添加一个 --><appender-ref ref="FILE"/></appender><logger name="org.apache.ibatis.cache.decorators.LoggingCache" level="DEBUG" additivity="false"><appender-ref ref="CONSOLE"/></logger><logger name="org.springframework.boot" level="debug"/><root level="info"><!--<appender-ref ref="ASYNC"/>--><appender-ref ref="FILE"/><appender-ref ref="CONSOLE"/></root>
</configuration>

image-20240902120042688

日志输出位置

image-20240902120237802

更加详细的日志信息

总体结构

image-20240902120402052

接口定义

image-20240902132017905

httpenum枚举

以后直接复制粘贴,不用设置常量了

java">package com.heima.model.common.enums;public enum AppHttpCodeEnum {// 成功段0SUCCESS(200,"操作成功"),// 登录段1~50NEED_LOGIN(1,"需要登录后操作"),LOGIN_PASSWORD_ERROR(2,"密码错误"),// TOKEN50~100TOKEN_INVALID(50,"无效的TOKEN"),TOKEN_EXPIRE(51,"TOKEN已过期"),TOKEN_REQUIRE(52,"TOKEN是必须的"),// SIGN验签 100~120SIGN_INVALID(100,"无效的SIGN"),SIG_TIMEOUT(101,"SIGN已过期"),// 参数错误 500~1000PARAM_REQUIRE(500,"缺少参数"),PARAM_INVALID(501,"无效参数"),PARAM_IMAGE_FORMAT_ERROR(502,"图片格式有误"),SERVER_ERROR(503,"服务器内部错误"),// 数据错误 1000~2000DATA_EXIST(1000,"数据已经存在"),AP_USER_DATA_NOT_EXIST(1001,"ApUser数据不存在"),DATA_NOT_EXIST(1002,"数据不存在"),// 数据错误 3000~3500NO_OPERATOR_AUTH(3000,"无权限操作"),NEED_ADMIND(3001,"需要管理员权限");int code;String errorMessage;AppHttpCodeEnum(int code, String errorMessage){this.code = code;this.errorMessage = errorMessage;}public int getCode() {return code;}public String getErrorMessage() {return errorMessage;}
}

静态方法获取枚举对象,包含code和message

image-20240902130023141

响应对象静态方法调用成功标识,获取枚举并填入

image-20240902130253118

image-20240902131233974

也可以自定义提示信息,传enum和自定义message

image-20240902131336496

分页信息结果类

继承了基础的结果类,构造函数填入 pageNum,pageSize,total

该类在构造时如果没有super()构造父类则默认无参构造父类

image-20240902131923986

image-20240902131928609

功能实现

思路分析

思路

image-20240902114335963

相关类

ApUserLoginController接口类
java">package com.heima.user.configcontroller.v1;import com.heima.model.common.dtos.ResponseResult;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("/api/v1/login")
public class ApUserLoginController {@PostMapping("/login_auth")public ResponseResult login(@RequestBody LoginDto dto) {return null;}
}
logindto

dto前端传来的数据,vo,后端给前端的数据

java">package com.heima.model.user.dtos;import io.swagger.annotations.ApiModelProperty;
import lombok.Data;@Data
public class LoginDto {/*** 手机号*/@ApiModelProperty(value = "手机号",required = true)private String phone;/*** 密码*/@ApiModelProperty(value = "密码",required = true)private String password;
}
mapper
java">package com.heima.user.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.heima.model.user.pojos.ApUser;
import org.apache.ibatis.annotations.Mapper;@Mapper
public interface ApUserMapper extends BaseMapper<ApUser> {
}
业务层service
java">package com.heima.user.service;import com.baomidou.mybatisplus.extension.service.IService;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.user.dtos.LoginDto;
import com.heima.model.user.pojos.ApUser;public interface ApUserService extends IService<ApUser>{/*** app端登录* @param dto* @return*/public ResponseResult login(LoginDto dto);}
业务实现类ApUserServiceImpl
java">package com.heima.user.service.impl;import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.user.dtos.LoginDto;
import com.heima.model.user.pojos.ApUser;
import com.heima.user.mapper.ApUserMapper;
import com.heima.user.service.ApUserService;
import com.heima.utils.common.AppJwtUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.cli.Digest;
import org.apache.commons.lang3.StringUtils;
import org.checkerframework.checker.units.qual.A;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;import java.util.HashMap;
import java.util.Objects;@Service
@Transactional
@Slf4j
public class ApUserServiceImpl extends ServiceImpl<ApUserMapper, ApUser> implements ApUserService {@Autowiredprivate ApUserMapper apUserMapper;/*** app端登录功能** @param dto* @return*/@Overridepublic ResponseResult login(LoginDto dto) {// 用户登录,有传递用户名和密码就不是游客if (StringUtils.isNotBlank(dto.getPhone()) && StringUtils.isNotBlank(dto.getPassword())) {// 拿用户名查数据库,如果是空返回用户不存在响应类ApUser dbUser = apUserMapper.selectById(dto.getPhone());if (Objects.isNull(dbUser)) {return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST, "用户不存在");}// 如果用户存在且输入了密码,获取盐值,和用户输入的密码一同进行加密,如果匹配成功则以id返回tokenString salt = dbUser.getSalt();String password = dto.getPassword();String pswd = DigestUtils.md5DigestAsHex((password + salt).getBytes());if (!pswd.equals(password)) {return ResponseResult.errorResult(AppHttpCodeEnum.LOGIN_PASSWORD_ERROR);}String token = AppJwtUtil.getToken(dbUser.getId().longValue());HashMap<String, Object> stringObjectHashMap = new HashMap<>();stringObjectHashMap.put("token", token);dbUser.setPassword("");dbUser.setSalt("");stringObjectHashMap.put("user", dbUser);return ResponseResult.okResult(stringObjectHashMap);} else {// 如果没输入密码,以0生成token,返回前端,视为游客HashMap<String, Object> map = new HashMap<>();map.put("token", AppJwtUtil.getToken(0L));return ResponseResult.okResult(map);}}
}

启动测试

接口测试工具

postman

http://localhost:51801/api/v1/login/login_auth

这里sql脚本的密码不与视频中admin一致,前往测试类生成一个复制到数据库里再次登录即可

image-20240902224501507

swagger接口文档

  • 在线的,发生变化实时更新

  • 可进行功能测试

依赖引入到model和common

java"><dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger2</artifactId>
</dependency>
<dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger-ui</artifactId>
</dependency>

增加自动配置类SwaggerConfiguration

java">package com.heima.common.swagger;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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;@Configuration
@EnableSwagger2
public class SwaggerConfiguration {@Beanpublic Docket buildDocket() {return new Docket(DocumentationType.SWAGGER_2).apiInfo(buildApiInfo()).select()// 要扫描的API(Controller)基础包.apis(RequestHandlerSelectors.basePackage("com.heima")).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();}
}

扫描heima包下的所有类,进行接口文档配置

配置到factory清单文件里,当common加载时加载清单里的类

spring.factories

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.heima.common.exception.ExceptionCatch
com.heima.common.swagger.SwaggerConfiguration
com.heima.common.swagger.Swagger2Configuration

注解

常见对应关系如下

@Api = controller整个的作用

@ApiOperation = controller下的方法或者接口

@ApiParam = 接口方法参数信息

@ApiModel = 对象

@ApiModel = 实体类

@@ApiModelProperty = 实体类属性

其他注解

@ApiResponse:HTTP响应其中1个描述

@ApiResponses:HTTP响应整体描述

@ApiIgnore:使用该注解忽略这个API

@ApiError :发生错误返回的信息

@ApiImplicitParam:一个请求参数

@ApiImplicitParams:多个请求参数的描述信息

启动user微服务,访问地址:http://localhost:51801/swagger-ui.html

测试成功

image-20240903084819347

knife4J

image-20240903084954355

提供离线文档

依赖引入

common模块下

<dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-spring-boot-starter</artifactId>
</dependency>

配置类

java">package com.heima.common.swagger;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.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;@Configuration
@EnableSwagger2
@EnableKnife4j
@Import(BeanValidatorPluginsConfiguration.class)
public class Swagger2Configuration {@Bean(value = "defaultApi2")public Docket defaultApi2() {Docket docket=new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo())//分组名称.groupName("1.0").select()//这里指定Controller扫描包路径.apis(RequestHandlerSelectors.basePackage("com.heima")).paths(PathSelectors.any()).build();return docket;}private ApiInfo apiInfo() {return new ApiInfoBuilder().title("黑马头条API文档").description("黑马头条API文档").version("1.0").build();}
}

bean扫描清单

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\com.heima.common.exception.ExceptionCatch,\com.heima.common.swagger.SwaggerConfiguration,\com.heima.common.swagger.Swagger2Configuration

访问

http://localhost:51801/doc.html

image-20240903094629369 image-20240903094812475 image-20240903094840436

APP网关

image-20240903094935502 image-20240903095002773

管理员/自媒体/app网关

依赖引入

<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.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId></dependency><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId></dependency>
</dependencies>

包含了管理所有模块的网关依赖,nacos服务发现与注册,jwt依赖

新建app-gateway模块指定父模块

image-20240903095744809

启动类

image-20240903095457195

java">package com.heima.app.gateway;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;@SpringBootApplication
@EnableDiscoveryClient
public class AppGatewayApplication {public static void main(String[] args) {SpringApplication.run(AppGatewayApplication.class,args);}
}

@EnableDiscoveryClient //开启注册中心

配置文件bootstrap.yml

server:port: 51601
spring:application:name: leadnews-app-gatewaycloud:nacos:discovery:server-addr: 192.168.233.136:8848config:server-addr: 192.168.233.136:8848file-extension: yml

注册到nacos

image-20240903095945759
spring:cloud:gateway:globalcors:add-to-simple-url-handler-mapping: truecorsConfigurations:'[/**]':allowedHeaders: "*"allowedOrigins: "*"allowedMethods:- GET- POST- DELETE- PUT- OPTIONroutes:# 平台管理- id: useruri: lb://leadnews-userpredicates:- Path=/user/**filters:- StripPrefix= 1

启动并请求测试

http://localhost:51601/user/api/v1/login/login_auth

image-20240903100831805

认证过滤器jwt

思路分析

image-20240903101034191

登录请求不鉴权,直接去微服务校验密码用户名

其他先判断token,在判断有效期

有效就放行到想去的微服务

步骤

实现过滤器类,后续请求会被该过滤器拦截

JWT工具类

java">package com.heima.app.gateway.util;import io.jsonwebtoken.*;import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.*;public class AppJwtUtil {// TOKEN的有效期一天(S)private static final int TOKEN_TIME_OUT = 3_600;// 加密KEYprivate static final String TOKEN_ENCRY_KEY = "MDk4ZjZiY2Q0NjIxZDM3M2NhZGU0ZTgzMjYyN2I0ZjY";// 最小刷新间隔(S)private static final int REFRESH_TIME = 300;// 生产IDpublic static String getToken(Long id){Map<String, Object> claimMaps = new HashMap<>();claimMaps.put("id",id);long currentTime = System.currentTimeMillis();return Jwts.builder().setId(UUID.randomUUID().toString()).setIssuedAt(new Date(currentTime))  //签发时间.setSubject("system")  //说明.setIssuer("heima") //签发者信息.setAudience("app")  //接收用户.compressWith(CompressionCodecs.GZIP)  //数据压缩方式.signWith(SignatureAlgorithm.HS512, generalKey()) //加密方式.setExpiration(new Date(currentTime + TOKEN_TIME_OUT * 1000))  //过期时间戳.addClaims(claimMaps) //cla信息.compact();}/*** 获取token中的claims信息** @param token* @return*/private static Jws<Claims> getJws(String token) {return Jwts.parser().setSigningKey(generalKey()).parseClaimsJws(token);}/*** 获取payload body信息** @param token* @return*/public static Claims getClaimsBody(String token) {try {return getJws(token).getBody();}catch (ExpiredJwtException e){return null;}}/*** 获取hearder body信息** @param token* @return*/public static JwsHeader getHeaderBody(String token) {return getJws(token).getHeader();}/*** 是否过期** @param claims* @return -1:有效,0:有效,1:过期,2:过期*/public static int verifyToken(Claims claims) {if(claims==null){return 1;}try {claims.getExpiration().before(new Date());// 需要自动刷新TOKENif((claims.getExpiration().getTime()-System.currentTimeMillis())>REFRESH_TIME*1000){return -1;}else {return 0;}} catch (ExpiredJwtException ex) {return 1;}catch (Exception e){return 2;}}/*** 由字符串生成加密key** @return*/public static SecretKey generalKey() {byte[] encodedKey = Base64.getEncoder().encode(TOKEN_ENCRY_KEY.getBytes());SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");return key;}public static void main(String[] args) {/* Map map = new HashMap();map.put("id","11");*/System.out.println(AppJwtUtil.getToken(1102L));Jws<Claims> jws = AppJwtUtil.getJws("eyJhbGciOiJIUzUxMiIsInppcCI6IkdaSVAifQ.H4sIAAAAAAAAADWLQQqEMAwA_5KzhURNt_qb1KZYQSi0wi6Lf9942NsMw3zh6AVW2DYmDGl2WabkZgreCaM6VXzhFBfJMcMARTqsxIG9Z888QLui3e3Tup5Pb81013KKmVzJTGo11nf9n8v4nMUaEY73DzTabjmDAAAA.4SuqQ42IGqCgBai6qd4RaVpVxTlZIWC826QA9kLvt9d-yVUw82gU47HDaSfOzgAcloZedYNNpUcd18Ne8vvjQA");Claims claims = jws.getBody();System.out.println(claims.get("id"));}}

过滤器类

java">package com.heima.app.gateway.filter;import com.heima.app.gateway.util.AppJwtUtil;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
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.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;import java.net.URI;@Component
@Slf4j
public class AuthorizeFilter implements Ordered, GlobalFilter {@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {//1.获取request和response对象ServerHttpRequest request = exchange.getRequest();ServerHttpResponse response = exchange.getResponse();// 1,获取请求路径,如果是登录请求则放行URI uri = request.getURI();if (uri.getPath().contains("/login")) {return chain.filter(exchange);}// 2,如果不是登录请求,获取token,判断是否存在String token = exchange.getRequest().getHeaders().getFirst("token");if (StringUtils.isEmpty(token)) {response.setStatusCode(HttpStatus.UNAUTHORIZED);return response.setComplete();}// 解析token可能会失败因此我们用try catch将其包住在catch处也返回401// 3,如果存在token,那么再判断其有效期try {Claims claimsBody = AppJwtUtil.getClaimsBody(token);//验证是否过期int verifyToken = AppJwtUtil.verifyToken(claimsBody);if(verifyToken==1||verifyToken==2){response.setStatusCode(HttpStatus.UNAUTHORIZED);return response.setComplete();}} catch (Exception e) {e.printStackTrace();response.setStatusCode(HttpStatus.UNAUTHORIZED);return response.setComplete();}// 4,如果token在有效期内则放行return chain.filter(exchange);}/*** 优先级设置  值越小  优先级越高** @return*/@Overridepublic int getOrder() {return 0;}
}

App端前端项目集成

image-20240903102609298

解压nginx压缩包(路径必须全英文),web项目,配置nginx.conf

先配置nginx.conf其端口为8222,因为80一般早被占用了

image-20240903103549543

不搞到linux上了,直接在window用cmd打开根目录后输入nginx进行启动

image-20240903103727774

http://localhost:8222/

image-20240903103801825

配置nginx服务器地址跳转到前端项目

由于一共有三个前端服务,我们给他们分别创建一个配置文件,后续用包含的方式将他们归结在一起,类似于html=》vue的过程

在conf文件夹中新建一个leadnews.conf文件夹

新建第一个配置文件heima-leadnews-app.conf内容如下(注意,有些解压可能会给你多嵌套一层app-web。)

upstream  heima-app-gateway{server localhost:51601;
}server {listen 8801;location / {root  你前端项目的地址;index index.html;}location ~/app/(.*) {proxy_pass http://heima-app-gateway/$1;proxy_set_header HOST $host;  # 不改变源请求头的值proxy_pass_request_body on;  #开启获取请求体proxy_pass_request_headers on;  #开启获取请求头proxy_set_header X-Real-IP $remote_addr;   # 记录真实发出请求的客户端IPproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;  #记录代理信息}
}

在nginx。conf引入所有子配置文件,内容如下,直接覆盖原先所有内容

#user  nobody;
worker_processes  1;events {worker_connections  1024;
}
http {include       mime.types;default_type  application/octet-stream;sendfile        on;keepalive_timeout  65;# 引入自定义配置文件include leadnews.conf/*.conf;
}

重载nginx,生效配置文件,在刚才打开cmd启动nginx那个目录上输入

nginx.exe -s reload
image-20240903104918152

登录成功

image-20240903110029748

游客登录则默认token

image-20240903110113444

http://www.ppmy.cn/ops/105081.html

相关文章

对比 PDAF、CDAF 和 LAAF 自动对焦技术

深入解析相位检测自动对焦&#xff08;PDAF&#xff09; 相位检测自动对焦&#xff08;PDAF&#xff0c;Phase Detection Auto Focus&#xff09;是一种高效的自动对焦技术&#xff0c;广泛应用于现代数码相机、无反相机和智能手机摄像头中。为了更好地理解 PDAF&#xff0c;我…

设计模式学习-命令模式

概念 命令&#xff0c;接收者&#xff0c;执行者&#xff0c;一个命令模式由这些基本的组件组成。 接收者 会有一个函数 命令有一个持有接收者并且 有一个 执行函数 执行者 持有一个命令 并且 会执行这个命令 using UnityEngine; using System.Collections; namespace CommondS…

PHP一键创建全球参与探索现代在线投票系统

一键创建全球参与探索现代在线投票系统 &#x1f310;✨ &#x1f680; 开篇&#xff1a;解锁全球互动新纪元 在这个数字化飞速发展的时代&#xff0c;每一个声音都值得被听见&#xff0c;每一份意见都能跨越山海相连。想象一下&#xff0c;只需轻轻一点&#xff0c;就能发起…

sqlite3的db.interrupt方法深入解析

在Node.js环境中&#xff0c;sqlite3库是一个广受欢迎的轻量级数据库库&#xff0c;它为开发者提供了一个简洁的API来与SQLite数据库进行交互。在处理长时间运行或复杂的数据库查询时&#xff0c;有时可能需要中断这些查询。sqlite3库提供了db.interrupt方法来实现这一功能。本…

集合及映射

1、集合类图 1&#xff09;ArrayList与LinkedList 区别 LinkedList 实现了双向队列的接口&#xff0c;对于数据的插入速度较快&#xff0c;只需要修改前后的指向即可&#xff1b;ArrayList对于特定位置插入数据&#xff0c;需要移动特定位置后面的数据&#xff0c;有额外开销 …

HTML静态网页成品作业(HTML+CSS)——动漫樱桃小丸子网页(4个页面)

&#x1f389;不定期分享源码&#xff0c;关注不丢失哦 文章目录 一、作品介绍二、作品演示三、代码目录四、网站代码HTML部分代码 五、源码获取 一、作品介绍 &#x1f3f7;️本套采用HTMLCSS&#xff0c;未使用Javacsript代码&#xff0c;共有4个页面。 二、作品演示 三、代…

日常实习【面试记录】

快手一面【日常实习】 base&#xff1a; 北京 岗位&#xff1a;客户端开发-ios 时间&#xff1a; 2024/9/2 上午 11点 文章目录 快手一面【日常实习】操作系统 &#xff1a;进程和线程的区别 计网&#xff1a;1. 网络协议有哪些&#xff1f;2. TCP和UDP在哪一层&#xff1f…

为什么使用雪花算法,有什么优缺点,如何解决?为什么不使用UUID的方法,如何解决系统回拨的问题?

为什么使用雪花算法&#xff0c;有什么优缺点&#xff0c;如何解决&#xff1f;为什么不使用UUID的方法&#xff0c;如何解决系统回拨的问题&#xff1f; 生成的id应该满足下面的条件&#xff1a; 首先是全局唯一&#xff0c;不能出现重复的ID之后是总体应该是递增的&#xf…