• 黑马头条(day01)


    目录

    项目介绍

    业务说明

    技术栈

    环境搭建

    nacos安装

    初始工程搭建

    登录功能实现

    需求分析

    表结构分析

    思路分析

    登录流程

    密码加密

    接口实现

    微服务搭建

    功能实现

    接口测试

    网关

    配置网关

    全局过滤器实现网关jwt校验

    思路分析

    具体实现

    前端集成

    部署思路

    配置nginx


    项目介绍

    业务说明

    技术栈

    • 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.xxx.xxx:8848/nacos

    初始工程搭建

    ①:项目依赖环境

    • JDK1.8

    • Intellij Idea

    • maven-3.6.1

    • MySQL 8

    ②:在链接中下载heima-leadnews.zip文件,拷贝到一个没有中文和空格的目录,使用idea打开即可

    heima-leadnews.zip

    ③:IDEA开发工具配置

    ④:设置项目编码格式

    登录功能实现

    需求分析

    • 用户点击开始使用

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

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

           游客只有查看的权限

    表结构分析

    不同模块下与之相关的内容较多,数据库采用模块分库的模式,app端用户相关的内容较多,所以单独设置一个库leadnews_user

    表名称说明
    ap_userAPP用户信息表
    ap_user_fanAPP用户粉丝信息表
    ap_user_followAPP用户关注信息表
    ap_user_realnameAPP实名认证信息表

    leadnews_user.sql

    在链接中下载sql文件,执行该脚本即可创建对应的数据库,数据表及表中以及准备好的数据

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

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

    app_user表对应的实体类如下:

    1. package com.heima.model.user.pojos;
    2. import com.baomidou.mybatisplus.annotation.IdType;
    3. import com.baomidou.mybatisplus.annotation.TableField;
    4. import com.baomidou.mybatisplus.annotation.TableId;
    5. import com.baomidou.mybatisplus.annotation.TableName;
    6. import lombok.Data;
    7. import java.io.Serializable;
    8. import java.util.Date;
    9. /**
    10. * APP用户信息表
    11. * @author Dcoin
    12. */
    13. @Data
    14. @TableName("ap_user")
    15. public class ApUser implements Serializable {
    16. private static final long serialVersionUID = 1L;
    17. /**
    18. * 主键
    19. */
    20. @TableId(value = "id", type = IdType.AUTO)
    21. private Integer id;
    22. /**
    23. * 密码、通信等加密盐
    24. */
    25. @TableField("salt")
    26. private String salt;
    27. /**
    28. * 用户名
    29. */
    30. @TableField("name")
    31. private String name;
    32. /**
    33. * 密码,md5加密
    34. */
    35. @TableField("password")
    36. private String password;
    37. /**
    38. * 手机号
    39. */
    40. @TableField("phone")
    41. private String phone;
    42. /**
    43. * 头像
    44. */
    45. @TableField("image")
    46. private String image;
    47. /**
    48. * 0 男
    49. 1 女
    50. 2 未知
    51. */
    52. @TableField("sex")
    53. private Boolean sex;
    54. /**
    55. * 0 未
    56. 1 是
    57. */
    58. @TableField("is_certification")
    59. private Boolean certification;
    60. /**
    61. * 是否身份认证
    62. */
    63. @TableField("is_identity_authentication")
    64. private Boolean identityAuthentication;
    65. /**
    66. * 0正常
    67. 1锁定
    68. */
    69. @TableField("status")
    70. private Boolean status;
    71. /**
    72. * 0 普通用户
    73. 1 自媒体人
    74. 2 大V
    75. */
    76. @TableField("flag")
    77. private Short flag;
    78. /**
    79. * 注册时间
    80. */
    81. @TableField("created_time")
    82. private Date createdTime;
    83. }

     LoginDto

    1. package com.heima.model.user.dtos;
    2. import io.swagger.annotations.ApiModelProperty;
    3. import lombok.Data;
    4. @Data
    5. public class LoginDto {
    6. /**
    7. * 手机号
    8. */
    9. @ApiModelProperty(value = "手机号",required = true)
    10. private String phone;
    11. /**
    12. * 密码
    13. */
    14. @ApiModelProperty(value = "密码",required = true)
    15. private String password;
    16. }

     LoginVo

    1. package com.heima.model.user.vo;
    2. import lombok.Data;
    3. import java.util.Date;
    4. @Data
    5. public class LoginVo {
    6. /**
    7. * 主键
    8. */
    9. private Integer id;
    10. /**
    11. * 用户名
    12. */
    13. private String name;
    14. /**
    15. * 手机号
    16. */
    17. private String phone;
    18. /**
    19. * 头像
    20. */
    21. private String image;
    22. /**
    23. * 0 男
    24. * 1 女
    25. * 2 未知
    26. */
    27. private Boolean sex;
    28. /**
    29. * 0 未
    30. * 1 是
    31. */
    32. private Boolean certification;
    33. /**
    34. * 是否身份认证
    35. */
    36. private Boolean identityAuthentication;
    37. /**
    38. * 0正常
    39. * 1锁定
    40. */
    41. private Boolean status;
    42. /**
    43. * 0 普通用户
    44. * 1 自媒体人
    45. * 2 大V
    46. */
    47. private Short flag;
    48. /**
    49. * 注册时间
    50. */
    51. private Date createdTime;
    52. /**
    53. * token
    54. */
    55. private String token;
    56. }

    实体类对应项目的模块(model主要存放一些与数据库,前端交互的实体类和一些枚举类)

    • dtos包下的实体类用于接收前端请求的数据

    • pojos包下的实体类与数据库的表,字段相对应用于与数据库交互

    • vo包下的实体类用于封装返回的结果与前端进行交互

    思路分析

    登录流程

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

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

    密码加密

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

    注册->生成盐

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

    接口实现

    微服务搭建

    在heima-leadnews-service下创建工程heima-leadnews-user

    注意:heima-leadnews-user的pom父模块设置为heima-leadnews-service

    该项目使用了nacos注册中心,所以将配置文件交给nacos的配置中心统一管理

    bootstrap.yml

    1. server:
    2. port: 51801
    3. spring:
    4. application:
    5. name: leadnews-user
    6. cloud:
    7. nacos:
    8. discovery:
    9. server-addr: 192.168.xxx.xxx:8848
    10. config:
    11. server-addr: 192.168.xxx.xxx:8848
    12. file-extension: yml

    在nacos中创建配置文件

    1. spring:
    2. datasource:
    3. driver-class-name: com.mysql.cj.jdbc.Driver
    4. url: jdbc:mysql://192.168.xxx.xxx/数据库名?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=true
    5. username: 用户名
    6. password: 密码
    7. # 设置Mapper接口所对应的XML文件位置,如果你在Mapper接口中有自定义方法,需要进行该配置
    8. mybatis-plus:
    9. mapper-locations: classpath*:mapper/*.xml
    10. # 设置别名包扫描路径,通过该属性可以给包中的类注册别名
    11. type-aliases-package: com.heima.model.user.pojos
    12. configuration:
    13. log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

    logback.xml

    1. "1.0" encoding="UTF-8"?>
    2. "LOG_HOME" value="d:/logs"/>
    3. "CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
    4. %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
    5. utf8
    6. "FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
    7. "ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
    8. ${LOG_HOME}/leadnews.%d{yyyy-MM-dd}.log
    9. %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
    10. "ASYNC" class="ch.qos.logback.classic.AsyncAppender">
    11. 0
    12. 512
    13. "FILE"/>
    14. "org.apache.ibatis.cache.decorators.LoggingCache" level="DEBUG" additivity="false">
    15. "CONSOLE"/>
    16. "org.springframework.boot" level="debug"/>
    17. "info">
    18. "FILE"/>
    19. "CONSOLE"/>

    功能实现

    模块架构

    引导类

    1. package com.heima.user;
    2. import org.mybatis.spring.annotation.MapperScan;
    3. import org.springframework.boot.SpringApplication;
    4. import org.springframework.boot.autoconfigure.SpringBootApplication;
    5. import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
    6. @SpringBootApplication
    7. @EnableDiscoveryClient
    8. @MapperScan("com.heima.user.mapper")
    9. public class UserApplication {
    10. public static void main(String[] args) {
    11. SpringApplication.run(UserApplication.class);
    12. }
    13. }

    controller

    1. package com.heima.user.controller.v1;
    2. import com.heima.model.common.dtos.ResponseResult;
    3. import com.heima.model.user.dtos.LoginDto;
    4. import com.heima.user.service.ApUserService;
    5. import io.swagger.annotations.Api;
    6. import io.swagger.annotations.ApiOperation;
    7. import lombok.RequiredArgsConstructor;
    8. import lombok.extern.slf4j.Slf4j;
    9. import org.springframework.web.bind.annotation.*;
    10. @RestController
    11. @RequestMapping("/api/v1/login")
    12. @RequiredArgsConstructor
    13. @Api(value = "app端用户登录",tags = "app端用户登录")
    14. @Slf4j
    15. public class APUserLoginController {
    16. private final ApUserService apUserService;
    17. @PostMapping("/login_auth")
    18. @ApiOperation("用户登录")
    19. public ResponseResult login(@RequestBody LoginDto dto){
    20. log.info("用户登录参数:{}",dto);
    21. return apUserService.login(dto);
    22. }
    23. }

    service

    ApUserService

    1. package com.heima.user.service;
    2. import com.baomidou.mybatisplus.extension.service.IService;
    3. import com.heima.model.common.dtos.ResponseResult;
    4. import com.heima.model.user.dtos.LoginDto;
    5. import com.heima.model.user.pojos.ApUser;
    6. public interface ApUserService extends IService {
    7. /**
    8. * app端登录功能
    9. * @param dto
    10. * @return
    11. */
    12. public ResponseResult login(LoginDto dto);
    13. }

     ApUserServiceImpl

    1. package com.heima.user.service.impl;
    2. import com.baomidou.mybatisplus.core.toolkit.Wrappers;
    3. import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    4. import com.heima.model.common.dtos.ResponseResult;
    5. import com.heima.model.common.enums.AppHttpCodeEnum;
    6. import com.heima.model.user.dtos.LoginDto;
    7. import com.heima.model.user.pojos.ApUser;
    8. import com.heima.model.user.vo.LoginVo;
    9. import com.heima.user.mapper.ApUserMapper;
    10. import com.heima.user.service.ApUserService;
    11. import com.heima.utils.common.AppJwtUtil;
    12. import lombok.extern.slf4j.Slf4j;
    13. import org.apache.commons.lang.StringUtils;
    14. import org.springframework.beans.BeanUtils;
    15. import org.springframework.stereotype.Service;
    16. import org.springframework.util.DigestUtils;
    17. @Service
    18. //@Transactional
    19. @Slf4j
    20. public class ApUserServiceImpl extends ServiceImpl implements ApUserService {
    21. /**
    22. * app端登录功能
    23. *
    24. * @param dto
    25. * @return
    26. */
    27. @Override
    28. public ResponseResult login(LoginDto dto) {
    29. LoginVo loginVo = new LoginVo();
    30. if (StringUtils.isNotBlank(dto.getPhone()) && StringUtils.isNotBlank(dto.getPassword())){
    31. //1.正常登录
    32. //1.1根据手机号查询用户信息
    33. ApUser dbUser = getOne(Wrappers.lambdaQuery().eq(ApUser::getPhone, dto.getPhone()));
    34. if (dbUser == null){
    35. return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST,"用户信息不存在");
    36. }
    37. //1.2比对密码
    38. String salt = dbUser.getSalt();
    39. String password = dto.getPassword();
    40. String pwd = DigestUtils.md5DigestAsHex((password + salt).getBytes());
    41. if (!pwd.equals(dbUser.getPassword())){
    42. return ResponseResult.errorResult(AppHttpCodeEnum.LOGIN_PASSWORD_ERROR);
    43. }
    44. //1.3返回数据 jwt
    45. String token = AppJwtUtil.getToken(dbUser.getId().longValue());
    46. BeanUtils.copyProperties(dbUser,loginVo);
    47. loginVo.setToken(token);
    48. return ResponseResult.okResult(loginVo);
    49. }else {
    50. //2.游客登录
    51. loginVo.setToken(AppJwtUtil.getToken(0L));
    52. return ResponseResult.okResult(loginVo);
    53. }
    54. }
    55. }

    mapper

    1. package com.heima.user.mapper;
    2. import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    3. import com.heima.model.user.pojos.ApUser;
    4. import org.apache.ibatis.annotations.Mapper;
    5. @Mapper
    6. public interface ApUserMapper extends BaseMapper {
    7. }

    接口测试

    利用该条数据进行数据测试,初始密码为admin

    请求路径:http://localhost:51801/api/v1/login/login_auth

     该接口可以正常接收前端的请求并将处理好的数据正确封装返回至前端

    网关

    配置网关

    (1)在heima-leadnews-gateway导入以下依赖

    pom文件

    1. org.springframework.cloud
    2. spring-cloud-starter-gateway
    3. com.alibaba.cloud
    4. spring-cloud-starter-alibaba-nacos-discovery
    5. com.alibaba.cloud
    6. spring-cloud-starter-alibaba-nacos-config
    7. io.jsonwebtoken
    8. jjwt

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

    注意:heima-leadnews-app-gateway的pom父模块设置为heima-leadnews-gateway

    引导类:

    1. package com.heima.app.gateway;
    2. import org.springframework.boot.SpringApplication;
    3. import org.springframework.boot.autoconfigure.SpringBootApplication;
    4. import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
    5. @SpringBootApplication
    6. @EnableDiscoveryClient
    7. public class AppGatewayApplication {
    8. public static void main(String[] args) {
    9. SpringApplication.run(AppGatewayApplication.class);
    10. }
    11. }

    bootstrap.yml

    1. server:
    2. port: 51601
    3. spring:
    4. application:
    5. name: leadnews-app-gateway
    6. cloud:
    7. nacos:
    8. discovery:
    9. server-addr: 192.168.xxx.xxx:8848
    10. config:
    11. server-addr: 192.168.xxx.xxx:8848
    12. file-extension: yml

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

     具体配置:

    1. spring:
    2. cloud:
    3. gateway:
    4. globalcors:
    5. add-to-simple-url-handler-mapping: true
    6. corsConfigurations:
    7. '[/**]':
    8. allowedHeaders: "*"
    9. allowedOrigins: "*"
    10. allowedMethods:
    11. - GET
    12. - POST
    13. - DELETE
    14. - PUT
    15. - OPTION
    16. routes:
    17. # 平台管理
    18. - id: user
    19. uri: lb://leadnews-user
    20. predicates:
    21. - Path=/user/**
    22. filters:
    23. - StripPrefix= 1

    环境搭建完成以后,启动项目网关和用户两个服务,使用postman进行测试

    请求地址:http://localhost:51601/user/api/v1/login/login_auth

    全局过滤器实现网关jwt校验

    思路分析

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

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

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

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

    具体实现

    第一:

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

    第二:

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

    1. package com.heima.app.gateway.filter;
    2. import com.heima.app.gateway.util.AppJwtUtil;
    3. import io.jsonwebtoken.Claims;
    4. import lombok.extern.slf4j.Slf4j;
    5. import org.springframework.cloud.gateway.filter.GatewayFilterChain;
    6. import org.springframework.cloud.gateway.filter.GlobalFilter;
    7. import org.springframework.core.Ordered;
    8. import org.springframework.http.HttpStatus;
    9. import org.springframework.http.server.reactive.ServerHttpRequest;
    10. import org.springframework.http.server.reactive.ServerHttpResponse;
    11. import org.springframework.stereotype.Component;
    12. import org.springframework.util.StringUtils;
    13. import org.springframework.web.server.ServerWebExchange;
    14. import reactor.core.publisher.Mono;
    15. @Component
    16. @Slf4j
    17. public class AuthorizeFilter implements Ordered, GlobalFilter {
    18. @Override
    19. public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    20. //1.获取request和response
    21. ServerHttpRequest request = exchange.getRequest();
    22. ServerHttpResponse response = exchange.getResponse();
    23. //2.判断是否是登录
    24. if(request.getURI().getPath().contains("/login")){
    25. //放行
    26. return chain.filter(exchange);
    27. }
    28. //3.获取token
    29. String token = request.getHeaders().getFirst("token");
    30. //4.判断token是否存在
    31. if (StringUtils.isEmpty(token)){
    32. response.setStatusCode(HttpStatus.UNAUTHORIZED);
    33. return response.setComplete();
    34. }
    35. //5.判断token是否有效
    36. try {
    37. Claims claimsBody = AppJwtUtil.getClaimsBody(token);
    38. //是否过期
    39. int result = AppJwtUtil.verifyToken(claimsBody);
    40. if (result == 1 || result == 2){
    41. response.setStatusCode(HttpStatus.UNAUTHORIZED);
    42. return response.setComplete();
    43. }
    44. }catch (Exception e){
    45. e.printStackTrace();
    46. response.setStatusCode(HttpStatus.UNAUTHORIZED);
    47. return response.setComplete();
    48. }
    49. //6.放行
    50. return chain.filter(exchange);
    51. }
    52. /**
    53. * 优先过滤器 值越小越先执行
    54. * @return
    55. */
    56. @Override
    57. public int getOrder() {
    58. return 0;
    59. }
    60. }

    注意:

    启动user服务,因为目前只有登录功能,所以该过滤器会直接放行,当业务增加的时候,访问其他的接口则会提示需要认证才能访问,这个时候需要在heads中解析正确的token才能正常访问。

    前端集成

    部署思路

    通过nginx来进行配置,功能如下

    • 通过nginx的反向代理功能访问后台的网关资源

    • 通过nginx的静态服务器功能访问前端静态页面

    配置nginx

    app-web.zip

    nginx-1.18.0.zip

    ①:下载链接中的压缩包nginx-1.18.0.zip并解压

    ②:下载链接中的压缩包app-web.zip并解压

    ③:配置nginx.conf文件

    该项目需要配置三个前端项目,为了简化配置的过程使用了指定配置文件的方式

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

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

    1. upstream heima-app-gateway{
    2. server localhost:51601;
    3. }
    4. server {
    5. listen 8801;
    6. location / {
    7. root 前端项目路径;
    8. index index.html;
    9. }
    10. location ~/app/(.*) {
    11. proxy_pass http://heima-app-gateway/$1;
    12. proxy_set_header HOST $host; # 不改变源请求头的值
    13. proxy_pass_request_body on; #开启获取请求体
    14. proxy_pass_request_headers on; #开启获取请求头
    15. proxy_set_header X-Real-IP $remote_addr; # 记录真实发出请求的客户端IP
    16. proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; #记录代理信息
    17. }
    18. }

    nginx.conf 把里面注释的内容和静态资源配置相关删除,引入heima-leadnews-app.conf文件加载

    1. #user nobody;
    2. worker_processes 1;
    3. events {
    4. worker_connections 1024;
    5. }
    6. http {
    7. include mime.types;
    8. default_type application/octet-stream;
    9. sendfile on;
    10. keepalive_timeout 65;
    11. # 引入自定义配置文件
    12. include leadnews.conf/*.conf;
    13. }

    ④ :启动nginx

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

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

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

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

    用浏览器打开,调试移动端模式访问,可对登录接口进行测试

    出现该界面说明前端项目部署成功

    出现该界面说明前后端联调成功

  • 相关阅读:
    Review-SpringBoot
    华为机试 - 真正的密码
    云盒子联合深信服,为南京一中打造智慧双模教学资源分享平台
    浙江大学数据结构MOOC-课后习题-第十讲-排序4 统计工龄
    Ubuntu server 24 (Linux) 普通用户不能sudo 也不能使用root登录 忘记root密码 修复解决方案
    Java数组
    通过使用Portainer来管理Docke环境以及使用私人镜像仓库
    无需标注海量数据,目标检测新范式OVD
    Python014--python中的logging日志模块
    PDF转图片软件有什么?建议收藏这三款软件
  • 原文地址:https://blog.csdn.net/weixin_56702252/article/details/140320098