厂里资讯之总体架构介绍以及环境搭建

本项目是本人根据黑马程序员的微服务项目黑马头条进行包装改造,作为实习简历上面的项目,为了进一步熟悉深挖这个项目,写了这一系列的博客来加深自己对项目的理解。

概述

项目背景

本项目主要着手于使用户获取学校最新最热的资讯,包括学校官方的相关告示,老师学术发布的相关内容,通过大数据分析用户喜好精准推送相关资讯。基于SpringCloud系列微服务组件,搭建微服务基础架构。项目开发层采用SpringBoot、SpringMVC、Mybatis开源框架实现。持久层采用:MySQL、Elasticsearch、 MongoDB进行存储。涉及到的中间件:Redis、RabbitMQ、并且搭建基于ELK日志分析平台。前端采用 uniapp,后台管理系统采用vue+ElementUI.

业务说明

系统架构

  • Spring-Cloud-Gateway : 微服务之前架设的网关服务,实现服务注册中的API请求路由,以及控制流速控制和熔断处理都是常用的架构手段,而这些功能Gateway天然支持

  • 运用Spring Boot快速开发框架,构建项目工程;并结合Spring Cloud全家桶技术,实现后端个人中心、自媒体、管理中心等微服务。

  • 运用Spring Cloud Alibaba Nacos作为项目中的注册中心和配置中心

  • 运用mybatis-plus作为持久层提升开发效率

  • 运用Kafka完成内部系统消息通知;与客户端系统消息通知;以及实时数据计算

  • 运用Redis缓存技术,实现热数据的计算,提升系统性能指标

  • 使用Mysql存储用户数据,以保证上层数据查询的高性能

  • 使用Mongo存储用户热数据,以保证用户热数据高扩展和高性能指标

  • 使用FastDFS作为静态资源存储器,在其上实现热静态资源缓存、淘汰等功能

  • 运用Hbase技术,存储系统中的冷数据,保证系统数据的可靠性

  • 运用ES搜索技术,对冷数据、文章数据建立索引,以保证冷数据、文章查询性能

  • 运用AI技术,来完成系统自动化功能,以提升效率及节省成本。比如实名认证自动化

  • PMD&P3C : 静态代码扫描工具,在项目中扫描项目代码,检查异常点、优化点、代码规范等,为开发团队提供规范统一,提升项目代码质量

环境搭建

nacos安装

①:docker拉取镜像

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
  • MODE=standalone 单机版

  • --restart=always 开机启动

  • -p 8848:8848 映射端口

  • -d 创建一个守护式容器在后台运行

③:访问地址:http://192.168.200.130:8848/nacos

初始工程搭建

环境准备

①:项目依赖环境(需提前安装好)

  • JDK1.8

  • Intellij Idea

  • maven-3.6.1

  • Git

②:创建如下图所示的初始工程

③:IDEA开发工具配置

④:设置项目编码格式

主体结构

这里我就不专门画图了,结构于黑马头条是一样的。

登录

需求分析

  • 用户点击开始使用

    登录后的用户权限较大,可以查看,也可以操作(点赞,关注,评论)

  • 用户点击不登录,先看看

      ​ 游客只有查看的权限

表结构分析

关于app端用户相关的内容较多,可以单独设置一个库Information_user

表名称 说明
ap_user APP用户信息表
ap_user_fan APP用户粉丝信息表
ap_user_follow APP用户关注信息表
ap_user_realname APP实名认证信息表

登录需要用到的是ap_user表,表结构如下:

项目中的持久层使用的mybatis-plus,一般都使用mybais-plus逆向生成对应的实体类

app_user表对应的实体类如下:

package com.changli.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+随机字符串)

md5是不可逆加密,md5相同的密码每次加密都一样,不太安全。在md5的基础上手动加盐(salt)处理

注册->生成盐

登录->使用盐来配合验证

思路分析

1,用户输入了用户名和密码进行登录,校验成功后返回jwt(基于当前用户的id生成)

2,用户游客登录,生成jwt返回(基于默认值0生成)

用户端微服务搭建

在changli-lnformation-service下创建工程changli-Information-user

引导类

package com.changli.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);
    }
}

bootstrap.yml

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/lnformation_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.changli.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>

登录功能实现

登录功能具体代码就不展示了,非常简单,太占篇幅了。

网关搭建

(1)在changli-lnformation-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)在changli-Information-gateway下创建changli-lnformation-app-gateway微服务

引导类:

package com.changli.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为lnformation-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

全局过滤器实现jwt校验

思路分析:

  1. 用户进入网关开始登陆,网关过滤器进行判断,如果是登录,则路由到后台管理微服务进行登录

  2. 用户登录成功,后台管理微服务签发JWT TOKEN信息返回给用户

  3. 用户再次进入网关开始访问,网关过滤器接收用户携带的TOKEN

  4. 网关过滤器解析TOKEN ,判断是否有权限,如果有,则放行,如果没有则返回未认证错误

具体实现:

第一:

​ 在认证过滤器中需要用到jwt的解析,所以需要把工具类拷贝一份到网关微服务

第二:

在网关微服务中新建全局过滤器:

package com.changli.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;
    }
}

相关推荐

  1. python环境以及pip

    2024-06-18 14:32:01       6 阅读
  2. node.js环境详细介绍

    2024-06-18 14:32:01       29 阅读
  3. Rust介绍与开发环境

    2024-06-18 14:32:01       28 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-06-18 14:32:01       14 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-06-18 14:32:01       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-06-18 14:32:01       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-06-18 14:32:01       18 阅读

热门阅读

  1. 数据库分库分表

    2024-06-18 14:32:01       6 阅读
  2. MySQL触发器基本结构

    2024-06-18 14:32:01       6 阅读
  3. Cesium4Unreal - # 011A Http通信

    2024-06-18 14:32:01       5 阅读
  4. windows11 ssh 无法连接问题解决方法

    2024-06-18 14:32:01       6 阅读
  5. C语言、C++和C#的区别在什么地方?

    2024-06-18 14:32:01       7 阅读
  6. HTML 事件

    2024-06-18 14:32:01       6 阅读
  7. 云端数据保护的挑战与对策

    2024-06-18 14:32:01       7 阅读
  8. 【C/C++】工业级别的日志文件轮转策略原理

    2024-06-18 14:32:01       6 阅读
  9. VO 和 DO

    2024-06-18 14:32:01       7 阅读
  10. 8D错漏件分析改进

    2024-06-18 14:32:01       5 阅读
  11. 编程连接主板:深入探索与实践的技术之旅

    2024-06-18 14:32:01       7 阅读