SpringCloud微服务项目实战 - 2.App登录及网关

如果你追求一个局部的更好甚至完美,你有可能花费巨大的资源和时间;
从总体上看,这往往意味着总体的浪费和失败,这是传说中的“打赢了战役打输了战争”。



系列文章目录

  1. 项目搭建
  2. App登录及网关

文章目录

  • 系列文章目录
  • 一、App登录
    • 1. 需求分析
    • 2. 表结构分析
      • ⑴. 导入数据库
      • ⑵. 库表
      • ⑶. 用户表(登录)
      • ⑷. 实体类
    • 3. 手动加密(md5+随机字符串)
    • 4. 运营端微服务搭建
      • ⑴. 创建模块
      • ⑵. 引导类
      • ⑶. 微服务配置文件
      • ⑷. 日志配置文件
      • ⑸. Nacos配置文件
      • ⑹. 微服务标准包结构
    • 5. 接口定义
      • ⑴. 接口返回类分析
        • ①. HttpCode枚举
        • ②. 通用的结果返回类
        • ③. 上面的返回类测试
        • ④. 分页返回类
      • ⑵. 实体类
      • ⑶. Controller
      • ⑷. Mapper
      • ⑸. service
      • ⑹. 实现类Impl
      • ⑺. Controller
  • 二、接口测试工具
    • 1. Postman
    • 2. Swagger
      • ⑴. 配置
        • ①. 引入依赖
        • ②. 配置类
        • ③. 自动配置
      • ⑵. 注解
        • ①. 常用注解
        • ②. 接口注解
        • ③. 参数注解
        • ④. 在线API文档
    • 3. Knife4j
      • ⑴. 配置
        • ①. 引入依赖
        • ②. 配置类
        • ③. 自动配置
      • ⑵. 文档(可下载离线文档)
  • 三、App网关
    • 1. gateway网关微服务
      • ⑴. 服务工程结构
      • ⑵. 配置
        • ①. 导入依赖
        • ②. 引导类
        • ③. 配置文件
        • ④. nacos配置
      • ⑶. 测试
    • 2. 认证过滤器
      • ⑴. 思路分析
      • ⑵. 全局过滤器
      • ⑶. 断点调试
  • 四、App前端项目集成
    • 1. nginx包
    • 2. 前端项目
    • 3. 配置nginx.conf文件1
    • 4. 配置nginx.conf文件2
    • 5. 重新加载nginx
    • 6. 运行测试


一、App登录

1. 需求分析

SpringCloud微服务项目实战 - 2.App登录及网关_第1张图片

  • 登录: 登录后的用户权限较大,可以查看,也可以操作(点赞,关注,评论)
  • 不登录,先看看: 游客只有查看的权限

2. 表结构分析

⑴. 导入数据库

sql文件链接:https://pan.baidu.com/s/11ITsQZQkR0Yl20lT10omwQ?pwd=abcd

SpringCloud微服务项目实战 - 2.App登录及网关_第2张图片

⑵. 库表

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

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

⑶. 用户表(登录)

登录需要用到的是 ap_user 表,表结构如下:
SpringCloud微服务项目实战 - 2.App登录及网关_第3张图片

DDL文件:

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用户信息表';

tinyint类型: 占1个字节,不指定unsigned(非负数),值范围(-128,127),指定了unsigned,值范围(0,255)
tinyint 通常表示小范围的数值,或者表示true或false,通常值为0表示false,值为1表示true


⑷. 实体类

新建 src/main/java/com/heima/model/user/pojos/ApUser.java 文件(app_user表对应的实体类):

/**
 * 

* APP用户信息表 *

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

3. 手动加密(md5+随机字符串)

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

注册(生成盐)
SpringCloud微服务项目实战 - 2.App登录及网关_第4张图片

登录(使用盐来配合验证)
SpringCloud微服务项目实战 - 2.App登录及网关_第5张图片


4. 运营端微服务搭建

⑴. 创建模块

SpringCloud微服务项目实战 - 2.App登录及网关_第6张图片

⑵. 引导类

新建 src/main/java/com/heima/user/UserApplication.java 文件:

@SpringBootApplication
@EnableDiscoveryClient // 集成当前的注册中心
@MapperScan("com.heima.user.mapper") // 集成myBatisPlus 扫描mapper
public class UserApplication {
    public static void main(String[] args) {
        SpringApplication.run(UserApplication.class,args);
    }
}

⑶. 微服务配置文件

新建 heima-leadnews-service/heima-leadnews-user/src/main/resources/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

⑷. 日志配置文件

新建 heima-leadnews-service/heima-leadnews-user/src/main/resources/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>

⑸. Nacos配置文件

SpringCloud微服务项目实战 - 2.App登录及网关_第7张图片

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

⑹. 微服务标准包结构

SpringCloud微服务项目实战 - 2.App登录及网关_第8张图片


5. 接口定义

SpringCloud微服务项目实战 - 2.App登录及网关_第9张图片

⑴. 接口返回类分析

①. HttpCode枚举

查看 com/heima/model/common/enums/AppHttpCodeEnum.java 文件:

public enum AppHttpCodeEnum {

    // 成功段固定为200
    SUCCESS(200,"操作成功"),
    // 登录段1~50
    NEED_LOGIN(1,"需要登录后操作"),
    LOGIN_PASSWORD_ERROR(2,"密码错误"),
    // TOKEN50~100
    TOKEN_INVALID(50,"无效的TOKEN"),
    TOKEN_EXPIRE(51,"TOKEN已过期"),
    TOKEN_REQUIRE(52,"TOKEN是必须的"),
    // SIGN验签 100~120
    SIGN_INVALID(100,"无效的SIGN"),
    SIG_TIMEOUT(101,"SIGN已过期"),
    // 参数错误 500~1000
    PARAM_REQUIRE(500,"缺少参数"),
    PARAM_INVALID(501,"无效参数"),
    PARAM_IMAGE_FORMAT_ERROR(502,"图片格式有误"),
    SERVER_ERROR(503,"服务器内部错误"),
    // 数据错误 1000~2000
    DATA_EXIST(1000,"数据已经存在"),
    AP_USER_DATA_NOT_EXIST(1001,"ApUser数据不存在"),
    DATA_NOT_EXIST(1002,"数据不存在"),
    // 数据错误 3000~3500
    NO_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;
    }
}

②. 通用的结果返回类

查看 com/heima/model/common/dtos/PageResponseResult.java 文件:

/**
 * 通用的结果返回类
 * @param 
 */
public class ResponseResult<T> implements Serializable {

    private String host;

    private Integer code;

    private String errorMessage;

    private T data;

    public ResponseResult() {
        this.code = 200;
    }

    public ResponseResult(Integer code, T data) {
        this.code = code;
        this.data = data;
    }

    public ResponseResult(Integer code, String msg, T data) {
        this.code = code;
        this.errorMessage = msg;
        this.data = data;
    }

    public ResponseResult(Integer code, String msg) {
        this.code = code;
        this.errorMessage = msg;
    }

    public static ResponseResult errorResult(int code, String msg) {
        ResponseResult result = new ResponseResult();
        return result.error(code, msg);
    }

    public static ResponseResult okResult(int code, String msg) {
        ResponseResult result = new ResponseResult();
        return result.ok(code, null, msg);
    }

    public static ResponseResult okResult(Object data) {
        ResponseResult result = setAppHttpCodeEnum(AppHttpCodeEnum.SUCCESS, AppHttpCodeEnum.SUCCESS.getErrorMessage());
        if(data!=null) {
            result.setData(data);
        }
        return result;
    }

    public static ResponseResult errorResult(AppHttpCodeEnum enums){
        return setAppHttpCodeEnum(enums,enums.getErrorMessage());
    }

    public static ResponseResult errorResult(AppHttpCodeEnum enums, String errorMessage){
        return setAppHttpCodeEnum(enums,errorMessage);
    }

    public static ResponseResult setAppHttpCodeEnum(AppHttpCodeEnum enums){
        return okResult(enums.getCode(),enums.getErrorMessage());
    }

    private static ResponseResult setAppHttpCodeEnum(AppHttpCodeEnum enums, String errorMessage){
        return okResult(enums.getCode(),errorMessage);
    }

    public ResponseResult<?> error(Integer code, String msg) {
        this.code = code;
        this.errorMessage = msg;
        return this;
    }

    public ResponseResult<?> ok(Integer code, T data) {
        this.code = code;
        this.data = data;
        return this;
    }

    public ResponseResult<?> ok(Integer code, T data, String msg) {
        this.code = code;
        this.data = data;
        this.errorMessage = msg;
        return this;
    }

    public ResponseResult<?> ok(T data) {
        this.data = data;
        return this;
    }

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

    public String getErrorMessage() {
        return errorMessage;
    }

    public void setErrorMessage(String errorMessage) {
        this.errorMessage = errorMessage;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }

    public String getHost() {
        return host;
    }

    public void setHost(String host) {
        this.host = host;
    }

    // 测试类
    public static void main(String[] args) {
        //前置
       /* AppHttpCodeEnum success = AppHttpCodeEnum.SUCCESS;
        System.out.println(success.getCode());
        System.out.println(success.getErrorMessage());*/

        //查询一个对象
        /*Map map = new HashMap();
        map.put("name","zhangsan");
        map.put("age",18);
        ResponseResult result = ResponseResult.okResult(map);
        System.out.println(JSON.toJSONString(result));*/


        //新增,修改,删除  在项目中统一返回成功即可
        /*ResponseResult result = ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
        System.out.println(JSON.toJSONString(result));*/


        //根据不用的业务返回不同的提示信息  比如:当前操作需要登录、参数错误
        /*ResponseResult result = ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN,"自定义提示信息");
        System.out.println(JSON.toJSONString(result));*/

        //查询分页信息
        PageResponseResult responseResult = new PageResponseResult(1,5,50);
        List list = new ArrayList();
        list.add("itcast");
        list.add("itheima");
        responseResult.setData(list);
        System.out.println(JSON.toJSONString(responseResult));

    }

}

③. 上面的返回类测试

在这里插入图片描述

④. 分页返回类

查看 com/heima/model/common/dtos/PageResponseResult.java 文件:

public class PageResponseResult extends ResponseResult implements Serializable {
    private Integer currentPage;
    private Integer size;
    private Integer total;

    public PageResponseResult(Integer currentPage, Integer size, Integer total) {
        this.currentPage = currentPage;
        this.size = size;
        this.total = total;
    }

    public PageResponseResult() {

    }


    public int getCurrentPage() {
        return currentPage;
    }

    public void setCurrentPage(int currentPage) {
        this.currentPage = currentPage;
    }

    public int getSize() {
        return size;
    }

    public void setSize(int size) {
        this.size = size;
    }

    public int getTotal() {
        return total;
    }

    public void setTotal(int total) {
        this.total = total;
    }
}

⑵. 实体类

新建 heima-leadnews-model/src/main/java/com/heima/model/user/dtos/LoginDto.java 文件:

@Data
public class LoginDto {

    /**
     * 手机号
     */
    @ApiModelProperty(value = "手机号",required = true)
    private String phone;

    /**
     * 密码
     */
    @ApiModelProperty(value = "密码",required = true)
    private String password;
}

⑶. Controller

新建 heima-leadnews-service/heima-leadnews-user/src/main/java/com/heima/user/controller/v1/ApUserLoginController.java 文件:

@RestController
@RequestMapping("/api/v1/login")
public class ApUserLoginController {

    @PostMapping("/login_auth")
    public ResponseResult login(@RequestBody LoginDto dto) {
        return null;
    }
}

⑷. Mapper

新建 heima-leadnews-service/heima-leadnews-user/src/main/java/com/heima/user/mapper/ApUserMapper.java 文件:

@Mapper
public interface ApUserMapper extends BaseMapper<ApUser> {
}

⑸. service

新建 heima-leadnews-service/heima-leadnews-user/src/main/java/com/heima/user/service/ApUserService.java 文件:

public interface ApUserService extends IService<ApUser>{

    /**
     * app端登录
     * @param dto
     * @return
     */
    public ResponseResult login(LoginDto dto);
    
}

⑹. 实现类Impl

  • 用户输入了用户名和密码进行登录,校验成功后返回jwt(基于当前用户的id生成)
  • 用户游客登录,生成jwt返回(基于默认值0生成)

新建 heima-leadnews-service/heima-leadnews-user/src/main/java/com/heima/user/service/impl/ApUserServiceImpl.java 文件:

@Service
public class ApUserServiceImpl extends ServiceImpl<ApUserMapper, ApUser> implements ApUserService {

    @Override
    public ResponseResult login(LoginDto dto) {

        //1.正常登录(手机号+密码登录)
        if (!StringUtils.isBlank(dto.getPhone()) && !StringUtils.isBlank(dto.getPassword())) {
            //1.1查询用户
            ApUser apUser = getOne(Wrappers.<ApUser>lambdaQuery().eq(ApUser::getPhone, dto.getPhone()));
            if (apUser == null) {
                return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST,"用户不存在");
            }

            //1.2 比对密码
            String salt = apUser.getSalt();
            String pswd = dto.getPassword();
            pswd = DigestUtils.md5DigestAsHex((pswd + salt).getBytes());
            if (!pswd.equals(apUser.getPassword())) {
                return ResponseResult.errorResult(AppHttpCodeEnum.LOGIN_PASSWORD_ERROR);
            }
            //1.3 返回数据  jwt
            Map<String, Object> map = new HashMap<>();
            map.put("token", AppJwtUtil.getToken(apUser.getId().longValue()));
            apUser.setSalt("");
            apUser.setPassword("");
            map.put("user", apUser);
            return ResponseResult.okResult(map);
        } else {
            //2.游客  同样返回token  id = 0
            Map<String, Object> map = new HashMap<>();
            map.put("token", AppJwtUtil.getToken(0l));
            return ResponseResult.okResult(map);
        }
    }
}

⑺. Controller

编辑 heima-leadnews-service/heima-leadnews-user/src/main/java/com/heima/user/controller/v1/ApUserLoginController.java 文件:

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




二、接口测试工具

1. Postman

测试 账号错误密码错误游客登录用户登录 返回信息是否正确
SpringCloud微服务项目实战 - 2.App登录及网关_第10张图片

2. Swagger

Swagger 是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务(https://swagger.io/)。

⑴. 配置

①. 引入依赖

heima-leadnews-modelheima-leadnews-common 模块中引入该依赖

  <dependency>
      <groupId>io.springfoxgroupId>
      <artifactId>springfox-swagger2artifactId>
  dependency>
  <dependency>
      <groupId>io.springfoxgroupId>
      <artifactId>springfox-swagger-uiartifactId>
  dependency>

②. 配置类

新建 heima-leadnews-common/src/main/java/com/heima/common/swagger/SwaggerConfiguration.java 文件:

@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/src/main/resources/META-INF/spring.factories 文件:

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

⑵. 注解

①. 常用注解

在Java类中添加Swagger的注解即可生成Swagger接口文档,常用Swagger注解如下:

  • @Api:修饰整个类,描述Controller的作用
  • @ApiOperation:描述一个类的一个方法,或者说一个接口
  • @ApiParam:单个参数的描述信息
  • @ApiModel:用对象来接收参数
  • @ApiModelProperty:用对象接收参数时,描述对象的一个字段
  • @ApiResponse:HTTP响应其中1个描述
  • @ApiResponses:HTTP响应整体描述
  • @ApiIgnore:使用该注解忽略这个API
  • @ApiError :发生错误返回的信息
  • @ApiImplicitParam:一个请求参数
  • @ApiImplicitParams:多个请求参数的描述信息

@ApiImplicitParam属性:

属性 取值 作用
paramType 查询参数类型
path 以地址的形式提交数据
query 直接跟参数完成自动映射赋值
body 以流的形式提交 仅支持POST
header 参数在request headers 里边提交
form 以form表单的形式提交 仅支持POST
dataType 参数的数据类型 只作为标志说明,并没有实际验证
Long
String
name 接收参数名
value 接收参数的意义描述
required 参数是否必填
true 必填
false 非必填
defaultValue 默认值

②. 接口注解

编辑 heima-leadnews-service/heima-leadnews-user/src/main/java/com/heima/user/controller/v1/ApUserLoginController.java 文件:

@RestController
@RequestMapping("/api/v1/login")
@Api(value = "app端用户登录", tags = "ap_user", description = "app端用户登录API")
public class ApUserLoginController {

    @Autowired
    private ApUserService apUserService;

    @PostMapping("/login_auth")
    @ApiOperation("用户登录")
    public ResponseResult login(@RequestBody LoginDto dto){
        return apUserService.login(dto);
    }
}

③. 参数注解

编辑 heima-leadnews-model/src/main/java/com/heima/model/user/dtos/LoginDto.java 文件:

@Data
public class LoginDto {

    /**
     * 手机号
     */
    @ApiModelProperty(value="手机号",required = true)
    private String phone;

    /**
     * 密码
     */
    @ApiModelProperty(value="密码",required = true)
    private String password;
}

④. 在线API文档

启动user微服务,访问地址:http://localhost:51801/swagger-ui.html
SpringCloud微服务项目实战 - 2.App登录及网关_第11张图片

3. Knife4j

knife4j是为Java MVC框架集成Swagger生成Api文档的增强解决方案,前身是swagger-bootstrap-ui,取名kni4j是希望它能像一把匕首一样小巧,轻量,并且功能强悍!(https://doc.xiaominfo.com/)

⑴. 配置

①. 引入依赖

编辑 heima-leadnews-common/pom.xml 文件:

        
        <dependency>
            <groupId>com.github.xiaoymingroupId>
            <artifactId>knife4j-spring-boot-starterartifactId>
        dependency>

②. 配置类

新建 heima-leadnews-common/src/main/java/com/heima/common/swagger/Swagger2Configuration.java 文件:

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

以上有两个注解需要特别说明:

注解 说明
@EnableSwagger2 该注解是Springfox-swagger框架提供的使用Swagger注解,该注解必须加
@EnableKnife4j 该注解是knife4j提供的增强注解,Ui提供了例如动态参数、参数过滤、接口排序等增强功能,如果你想使用这些增强功能就必须加该注解,否则可以不用加

③. 自动配置

编辑 heima-leadnews-common/src/main/resources/META-INF/spring.factories 文件:

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

⑵. 文档(可下载离线文档)

接口文档: http://localhost:51801/doc.htm
SpringCloud微服务项目实战 - 2.App登录及网关_第12张图片




三、App网关

1. gateway网关微服务

⑴. 服务工程结构

  • heima-leadnews-gateway 网关
    • heima-leadnews-admin-gateway 管理平台
    • heima-leadnews-wemedia-gateway 用户自媒体平台
    • heima-leadnews-app-gateway 用户移动APP

⑵. 配置

①. 导入依赖

新建 heima-leadnews-gateway/pom.xml 文件:

    <dependencies>
        
        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-gatewayartifactId>
        dependency>
        
        <dependency>
            <groupId>com.alibaba.cloudgroupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
        dependency>
        
        <dependency>
            <groupId>com.alibaba.cloudgroupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-configartifactId>
        dependency>
        
        <dependency>
            <groupId>io.jsonwebtokengroupId>
            <artifactId>jjwtartifactId>
        dependency>
    dependencies>

②. 引导类

新建 heima-leadnews-gateway/heima-leadnews-app-gateway/src/main/java/com/heima/app/gateway/AppGatewayApplication.java 文件:

@SpringBootApplication
@EnableDiscoveryClient  //开启注册中心
public class AppGatewayApplication {

    public static void main(String[] args) {
        SpringApplication.run(AppGatewayApplication.class,args);
    }
}

③. 配置文件

编辑 heima-leadnews-gateway/heima-leadnews-app-gateway/src/main/resources/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配置

nocos地址: http://192.168.200.130:8848/nacos
SpringCloud微服务项目实战 - 2.App登录及网关_第13张图片

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

⑶. 测试

启动项目网关和用户两个服务,使用postman进行测试

网关后的请求地址: localhost:51601/user/api/v1/login/login_auth
SpringCloud微服务项目实战 - 2.App登录及网关_第14张图片



2. 认证过滤器

⑴. 思路分析

SpringCloud微服务项目实战 - 2.App登录及网关_第15张图片

  • 用户进入网关开始登陆,网关过滤器进行判断,如果是登录,则路由到后台管理微服务进行登录
  • 用户登录成功,后台管理微服务签发JWT TOKEN信息返回给用户
  • 用户再次进入网关开始访问,网关过滤器接收用户携带的TOKEN
  • 网关过滤器解析TOKEN ,判断是否有权限,如果有,则放行,如果没有则返回未认证错误

⑵. 全局过滤器

新建 heima-leadnews-gateway/heima-leadnews-app-gateway/src/main/java/com/heima/app/gateway/filter/AuthorizeFilter.java 文件:

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

⑶. 断点调试

启动user服务,访问其他微服务,会提示需要认证才能访问,这个时候需要在heads中设置设置token才能正常访问。

SpringCloud微服务项目实战 - 2.App登录及网关_第16张图片




四、App前端项目集成

  • 通过nginx的反向代理功能访问后台的网关资源
  • 通过nginx的静态服务器功能访问前端静态页面

1. nginx包

资源链接: https://pan.baidu.com/s/1gPfoG0FnLC3dIUjJhqlKSQ?pwd=abcd

在这里插入图片描述
解压包,运行nginx

2. 前端项目

资源链接: https://pan.baidu.com/s/1wqfxtQCj_aGqSZVxgADVTQ?pwd=abcd
在这里插入图片描述
解压包,记住前端项目存放路径


3. 配置nginx.conf文件1

(nginx包中)新建 D:\code\hm\leadnews\config\nginx-1.18.0\conf\leadnews.conf\heima-leadnews-app.conf 文件:

upstream  heima-app-gateway{
    server localhost:51601; # 根据网关去做的请求
}

server {
	listen 8801;
	location / {
		root D:/code/hm/leadnews/config/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;  #记录代理信息
	}
}

4. 配置nginx.conf文件2

(nginx包中)编辑 D:\code\hm\leadnews\config\nginx-1.18.0\conf\leadnews.conf\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;
}

5. 重新加载nginx

# 重新加载nginx
nginx -s reload

SpringCloud微服务项目实战 - 2.App登录及网关_第17张图片


6. 运行测试

前端地址: http://localhost:8801/
SpringCloud微服务项目实战 - 2.App登录及网关_第18张图片



你可能感兴趣的:(Java,spring,cloud,微服务,java)