黑马头条微服务学习day01-环境搭建、SpringCloud微服务(注册发现、网关)

avatar
作者
筋斗云
阅读量:5

文章目录

项目介绍

环境搭建

在这里插入图片描述
在这里插入图片描述

项目背景

在这里插入图片描述

业务功能


在这里插入图片描述

技术栈说明

在这里插入图片描述
在这里插入图片描述

nacos

可以直接参照各种虚拟机安装centos7,不必拘泥于课程所给的镜像,只要保证能与外界ping通即可。后续环境可以自行安装。
同时使用mac的m系列芯片的同学可以直接不使用虚拟机直接在本地下载docker配置环境(注意要适配ARM架构)。

服务器环境准备

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

nacos安装

在这里插入图片描述
在这里插入图片描述

初始工程搭建

环境准备

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
配置Maven
在这里插入图片描述

主体结构

在这里插入图片描述
在这里插入图片描述

app登录

需求分析

在这里插入图片描述

表结构分析

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
通过逆向工程生成

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;  } 

手动加密

在这里插入图片描述
在这里插入图片描述

微服务搭建

在这里插入图片描述
在下面新建一个工程
在这里插入图片描述
bootstrap配置

server:   port: 51801 spring:   application:     name: leadnews-user   cloud:     nacos:       discovery:         server-addr: 192.168.200.130:8848       config:         server-addr: 192.168.200.130:8848         file-extension: yml 

使用nacos创建新集群
在这里插入图片描述

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

配置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> 

在这里插入图片描述

接口定义

在这里插入图片描述
在这里插入图片描述

功能实现

登录功能实现

下面分Controller,Service,ServiceImpl层,与Mapper层依次输入代码

@RestController @RequestMapping("/api/v1/login") public class ApUserLoginController {     @Autowired     private APUserService apUserService;     @PostMapping("/login_auth")     public ResponseResult login(@RequestBody LoginDto dto){         return apUserService.login(dto);     } } 
package com.heima.user.service;   import com.baomidou.mybatisplus.extension.service.IService; import com.heima.model.common.dtos.ResponseResult; import com.heima.model.common.user.dtos.LoginDto; import com.heima.model.common.user.pojos.ApUser;  public interface APUserService extends IService<ApUser> {      /**      * app端登录功能      * @param dto      * @return      */     public ResponseResult login(LoginDto dto); } 
@Service @Transactional @Slf4j /**  * ApUserMapper:这是一个 MyBatis-Plus 的 Mapper 接口,通常用于定义数据库操作的方法。  * ApUser:这是一个实体类,表示数据库中的一张表。  */ public class ApUserServiceImpl extends ServiceImpl<ApUserMapper, ApUser> implements APUserService {     /**      * app登录功能      * @param dto      * @return      */     @Override     public ResponseResult login(LoginDto dto) {         //1.正常登录用户用户名和密码         if (StringUtils.isNotEmpty(dto.getPhone())&&StringUtils.isNotEmpty(dto.getPassword())){             //1.1根据手机号从数据库中查询用户信息             ApUser dbUser = getOne(Wrappers.<ApUser>lambdaQuery().eq(ApUser::getPhone, dto.getPhone()));             if (dbUser == null){                 return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST,"用户信息不存在");             }             //1.2比对密码             String salt = dbUser.getSalt();             String password = dto.getPassword();             String pswd = DigestUtils.md5DigestAsHex((password + salt).getBytes());             if (!pswd.equals(dbUser.getPassword())){                 return ResponseResult.errorResult(AppHttpCodeEnum.LOGIN_PASSWORD_ERROR);             }             //1.3 返回数据jwt user             String token = AppJwtUtil.getToken(dbUser.getId().longValue());             //跟接口返回值描述保持一致             Map<String, Object> map = new HashMap<>();             map.put("token",token);             dbUser.setSalt("");             dbUser.setPassword("");             map.put("user",dbUser);             return ResponseResult.okResult(map);         }else {             //2.游客登录             Map<String,Object> map = new HashMap<>();             map.put("token",AppJwtUtil.getToken(0L));             return ResponseResult.okResult(map);         }      } } 
@Mapper public interface ApUserMapper extends BaseMapper<ApUser> {  } 

使用接口工具类进行测试
在这里插入图片描述

Swagger使用

在这里插入图片描述
在这里插入图片描述

  • 引入依赖,在heima-leadnews-model和heima-leadnews-common模块中引入该依赖

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

只需要在heima-leadnews-common中进行配置即可,因为其他微服务工程都直接或间接依赖即可。

  • 在heima-leadnews-common工程中添加一个配置类

新增:com.heima.common.swagger.SwaggerConfiguration

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 {     @Bean    public 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-leadnews-common模块中的resources目录中新增以下目录和文件

文件:resources/META-INF/Spring.factories

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

给Controller和DTO更新注解

@RestController @RequestMapping("/api/v1/login") @Api(value = "app端用户登录",tags = "app端用户登录") public class ApUserLoginController {     @Autowired     private ApUserService apUserService;     @PostMapping("/login_auth")     @ApiOperation("用户登录")     public ResponseResult login(@RequestBody LoginDto dto){         return apUserService.login(dto);     }   } 
@Data public class LoginDto {     /**      * 手机号      */     @ApiModelProperty(value = "手机号",required = true)     private String phone;     /**      * 密码      */     @ApiModelProperty(value = "密码",required = true)     private String password; } 

打开Swagger
在这里插入图片描述
在这里插入图片描述

app端网关

在这里插入图片描述
在这里插入图片描述
(1)在heima-leadnews-gateway导入以下依赖

pom文件

<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> 

(2)在heima-leadnews-gateway下创建heima-leadnews-app-gateway微服务

引导类:

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);     } } 

bootstrap.yml

server:   port: 51601 spring:   application:     name: leadnews-app-gateway   cloud:     nacos:       discovery:         server-addr: 192.168.200.130:8848       config:         server-addr: 192.168.200.130:8848         file-extension: yml 

在nacos的配置中心创建dataid为leadnews-app-gateway的yml配置

spring:   cloud:     gateway:       globalcors:         add-to-simple-url-handler-mapping: true         corsConfigurations:           '[/**]':             allowedHeaders: "*"             allowedOrigins: "*"             allowedMethods:               - GET               - POST               - DELETE               - PUT               - OPTION       routes:         # 平台管理         - id: user           uri: lb://leadnews-user           predicates:             - Path=/user/**           filters:             - StripPrefix= 1 

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

bootstrap

server:   port: 51601 spring:   application:     name: leadnews-app-gateway   cloud:     nacos:       discovery:         server-addr: localhost:8848       config:         server-addr: localhost:8848         file-extension: yml 

设置权限过滤器

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;  @Component @Slf4j public class AuthorizeFilter implements Ordered, GlobalFilter {      @Override     public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {         //1.获取Request和Response对象         ServerHttpRequest request = exchange.getRequest();         ServerHttpResponse response = exchange.getResponse();          //2.判断是否登录         if (request.getURI().getPath().contains("/login")){             //放行             return chain.filter(exchange);         }          //3.获取token         String token = request.getHeaders().getFirst("token");         //4.判断token是否存在         if (StringUtils.isBlank(token)){             response.setStatusCode(HttpStatus.UNAUTHORIZED);             return response.setComplete();         }         //5.判断token是否有效         try {             Claims claimsBody = AppJwtUtil.getClaimsBody(token);             //是否是过期             int result = AppJwtUtil.verifyToken(claimsBody);             if (result==1 || result==2) {                 response.setStatusCode(HttpStatus.UNAUTHORIZED);                 return response.setComplete();             }         }catch (Exception e){             e.printStackTrace();             response.setStatusCode(HttpStatus.UNAUTHORIZED);             return response.setComplete();         }         //6.放行         return chain.filter(exchange);     }      /**      * 优先级设置: 值越小 优先级越高      * @return      */     @Override     public int getOrder() {         return 0;     } }  
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;     // 加密KEY     private static final String TOKEN_ENCRY_KEY = "MDk4ZjZiY2Q0NjIxZDM3M2NhZGU0ZTgzMjYyN2I0ZjY";     // 最小刷新间隔(S)     private static final int REFRESH_TIME = 300;      // 生产ID     public 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());             // 需要自动刷新TOKEN             if((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"));      }  }  

nginx配置

在这里插入图片描述
在这里插入图片描述
①:解压资料文件夹中的压缩包nginx-1.18.0.zip

②:解压资料文件夹中的前端项目app-web.zip

③:配置nginx.conf文件

在nginx安装的conf目录下新建一个文件夹leadnews.conf,在当前文件夹中新建heima-leadnews-app.conf文件

heima-leadnews-app.conf配置如下:

upstream  heima-app-gateway{     server localhost:51601; }  server { 	listen 8801; 	location / { 		root D:/workspace/app-web/; 		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;   # 记录真实发出请求的客户端IP 		proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;  #记录代理信息 	} } 

nginx.conf 把里面注释的内容和静态资源配置相关删除,引入heima-leadnews-app.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

​ 在nginx安装包中使用命令提示符打开,输入命令nginx启动项目

​ 可查看进程,检查nginx是否启动

​ 重新加载配置文件:nginx -s reload

⑤:打开前端项目进行测试 – > http://localhost:8801

​ 用谷歌浏览器打开,调试移动端模式进行访问
在这里插入图片描述

广告一刻

为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!