Spring Boot入坑-12-项目实战

目标

  • 掌握后端项目整体架构搭建,掌握从0到1构建一个完整项目

  • 巩固已学习的后端技术,覆盖Java基础、Spring Boot的主要课程内容,包括但不限:序列化、反射、注解、泛型、Lambda、Stream、REST、Interceptor、数据访问、Swagger等等

  • 一些扩展内容的学习,比如登录、密码加密、项目部署等

需求

  • 一个测试人员记录开发Bug的Web前端网站

  • 核心功能:包含Bug列表、Bug信息维护,所有员工都能访问

  • 基础功能:人员信息维护、版本信息维护,供核心功能引用,仅管理员可以访问新增、修改、删除功能但所有员工能访问查询所有功能

  • 登录功能所有人都能访问

    项目需求图

【演示】

  1. 演示项目《BUG排行》项目,主要演示

    1. 未登录对接口访问返回401

    2. 不同角色用户登录,能访问不同接口

    3. 密码加密

开发步骤

一、数据库设计

  • 根据业务特点,设计数据表及表之间的关系

  • 实际的项目中,会有数据库设计的文档,使用Excel或PowerDesigner等工具进行

  • 设计好了数据表,将数据表创建到MySQL数据库中,提供数据服务

  • 项目只涉及3个表,分别是user、bug、version,具体见附件的bug_record_backend.sql脚本

  • 注意

    • 所有表的主键都为id,且为数据库自增

    • 所有表都有create_by_id、create_by_name、create_time、update_by_id、update_by_name、update_time

    数据库设计-物理模型图

【演示】

  1. 完成数据库的搭建与设计

二、搭建项目

  • 创建一个基于Web的Spring Boot项目

  • 按需要一次或逐步添加相应的包和类,主要的包,如下图

  • 这些包下的类的主要作用分别为

    • controller:控制器层,提供REST服务;很薄的一层,尽量不要有逻辑

    • service:服务层,承载了项目主要的业务功能,主要服务于控制器层

    • mapper:数据访问层,实现与数据库中的数据交互,使用MyBatis-Plus

    • entity:实体类包,里面的类与数据表一一对应,数据访问层中的Mapper都是针对entity进行操作,同时也在服务层被使用

    • dto:数据传输对象,用于控制器层、服务层对外提供请求/响应数据结构规范

    • config:通用配置包,所有的自定义配置类都定义在这个包,像Web配置、Swagger配置、数据库分页配置等

    • interceptor:拦截器包,包括对控制器层的REST服务权限拦截

    • annotation:注解包,包括是否需要进行权限拦截的注解

    • handler:操作包,包含实体类拦截类

    • util:工具包,封装了常用的工具,像密码加密、会话工具

【演示】

  1. 完成包层次定义

三、添加基本功能

  • 添加如下基本功能,包括pom.xml依赖config包下配置类application.properties中的配置节

    • Swagger

    • Lombok

    • 对象映射

    • 标准响应封装,见dto.common.ResponseData类

    • 通用异常处理,见advice.GlobalExceptionHandler类

    • 通用验证框架,见advice.GlobalExceptionHandler类

    • 会话工具类,见util.SessionUtil类

    • 密码加密工具类,见util.PasswordUtil类

  • 并添加一个控制器,测试能提供基本的REST服务

【演示】

  1. 完成基本功能添加、控制器输出、会话工具类定义、密码加密工具类定义

四、集成数据访问

  • 数据访问是一个软件项目的核心

  • 项目中使用MyBatis-Plus,按顺序通过下列步骤进行集成

    1. 添加依赖,数据库产品依赖和MyBatis-Plus依赖

    2. 添加配置,在application.properties中添加数据库配置

    3. 添加分页组件,见config.MyBatisPlusConfig类

    4. 添加实体类,用于与数据表一一对应,见entity包下的类

    5. 添加Mapper,见mapper包下的接口

    6. 添加Service,并继承MyBatis-Pluse中的服务接口和类,见service包下的接口、service\impl包下的类

    7. 添加用户信息CRUD,一切的数据都由用户产生,很多独立的系统都由构建用户信息开始,注意使用DTO和ResponseData

    8. 添加创建、修改信息拦截,见handler.CommonMetaObjectHandler类,里面使用了会话工具类

  • 注意,部分数据需要进行手动数据类型转换,如对于数据库中使用逗号分隔的内容,在程序中可以处理成List<String>;如用户表中的roles字段中的admin,staff,可转换成List<String>,包含两个元素,具体是通过TypeHandler接口实现,步骤如下

    1. 实现TypeHandler接口,主要实现其中的Java类型转DB类型、DB类型转Java类型,见entity.ListTypeHandler类

    2. 应用到实体类,对需要进行此种转换的实体类

      1. 在类上,@TableName注解中添加属性autoResultMap = true

      2. 在属性上,@TableFiled注解中进行应用,格式为 @TableField(jdbcType = JdbcType.VARCHAR,typeHandler = ListTypeHandler.class)

【演示】

  1. 完成用户各层次CRUD,注意其中的通用性验证、业务性验证、请求信息拦截、类型转换

五、集成登录

  • 大部分的软件,都需要登录后,才能访问,后端的REST服务也是如此

  • 具体的过程

    1. 添加登录控制器和功能,见controller.AccountController类、service.impl.AccountService类

    2. 将登录信息保存到会话(HttpSession),参考util.SessionUtil工具

    3. 应用到业务,创建、修改信息拦截

  • 另外,再考虑注册登出接口

【演示】

  1. 完成集成登录,使用明文密码

六、密码加密

  • 项目中一定不能将明文密码保存到数据库,必须加密后再存储

  • 一般会定义一个密码加密工具,对密码进行加密,且密码不能解密,并加盐防止彩虹表破解

  • 密码加密主要在代码中下面部分使用

    • 新增用户,密码需要加密保存

    • 注册

    • 修改密码

    • 登录,都使用加密后的密码进行比较

【演示】

  1. 完成集成密码加密

七、认证与授权--基于Session

认证与授权时序图
  • 如上图一般,除了登录、注册服务,其他服务都需要登录才能使用

  • 不同用户登录后,访问的REST服务也不一样,一般根据其角色决定,不同的用户使用不同的服务

  • 如何完成按角色过滤REST服务访问权限?具体实现步骤如下

    1. 设计权限过滤注解,自定义注解@UserRight,里面包含roles属性,用于标识使用该注解的类或方法需要根据角色进行权限过滤;就此项目业务,见annotaion.UserRight注解

      1. staff角色,所有人员都可访问,Bug信息所有接口、人员信息的查询所有接口、版本信息的查询所有接口

      2. admin角色,仅管理员访问,人员信息除了查询所有之外接口、版本信息除了查询所有之外接口

    2. 设计登录接口,请求参数为用户名和密码,登录成功,将用户信息存入HttpSession,这个前面已经完成,见controller.AccountController类

    3. 设计拦截器,对自定义注解@UserRight进行拦截,根据注解中需要的角色与登录时保存在HtppSession中用户拥有的角色进行比对,如果当前用户拥有访问接口需要的角色权限,可以访问;否则,拒绝访问,具体如下;见interceptor.AuthenticationInterceptor类

      • 无需权限访问,像账户控制器中的访问,主要用于登录

      • 登录才能访问,其他的所有控制器的访问

      • 登录且具备权限才能访问,基础数据中的CRUD方法(除了queryAll)之外

    4. 绑定拦截器,将拦截器绑定到/**的路由中,拦截所有的Action,见config.WebConfig类

    5. 标注REST服务,对需要进行权限过滤的REST服务,添加注解@UserRight注解,并配置角色,见controller.UserController类、controller.VersionController类、controller.BugController类

    6. 查看拦截效果,通过未登录、staff登录用户、admin登录用户查看不同效果

【演示】

  1. 完成认证与授权

八、业务功能开发

  • 上述功能完成后,一个基本的软件框架就搭建好了

  • 后续根据业务,往各个层次添加代码即可,主要有

    • 版本业务

    • BUG业务

    • ...

【演示】

  1. 完成业务功能开发

九、与前端集成

  • 学完前端后可实现

十、打包与发布

  • 项目开发完成后,打包并部署到Linux服务器,具体参考打包和发布

【演示】

  1. 完成项目打包并部署到CentOS 7服务器,具体参考打包和发布

【练习】

  1. 完成演示代码编写

实战和作业

  1. 完成项目实战,具体代码见bug-record-backend

补充-1:认证与授权--基于Token

传统Token

  • 传统的Token,是在服务器生成,并保存相应的信息在服务器(如Redis),然后返回给用户保存

JWT(JSON Web Token)

  • 数据不再保留在服务器,而是由客户端保存自己登录后获取的Token

  • 后续对服务器的请求,都需要携带Token

  • 服务器对Token中的信息进行解密后,识别当前用户信息,是否过期等数据,进行认证与授权

  • JWT中的内容分为Header、Payload、Signature三部分

    • Header:存放类型、加密算法

    • Payload:存储超时时间、用户自定义信息等

    • Signature:经过密钥签名的加密信息,安全的保证

  • 使用Base64进行编码

特点

  • 一般无需服务器端通过会话或缓存保存信息

  • 无需cookies支持

与Session对比

相同点
  • 作用相同,都是在HTTP协议无状态特性下,用于记住多次请求时的用户票据相关信息,实现认证与授权

不同点
  • 保存方式

    • Session,使用内存或缓存保存用户票据信息,并通过Cookies形式下发Session ID到客户端浏览器;客户端浏览器每次请求都通过Cookies携带这个Session ID,来告诉服务器“我是谁”

    • Token,通过自包含形式保存用户票据信息,服务器端无需存储相关信息;客户端每次请求一般都通过请求头Header携带这个Token

  • 过期时间

    • Session,服务器端Session和下发到Cookies的Session ID都可以设置过期时间

    • Token,过期时间也是通过自包含形式保存,一般服务器不可以过期掉Token

  • 加密方式

    • Session,由于用户票据信息保存在服务器端,无需加密,只下发唯一的Session ID给客户端即可

    • Token,使用密钥配合加密算法加密,只要密钥不泄露,相对安全

  • 多负载

    • Session,要处理分布式会话共享

    • Token,只要密钥、加密算法一致,单体与多负载处理方法也一致,适合微服务

使用方式

  • 使用方式与会话模式一致,查看附件项目中有标识【JWT】的内容部分

    1. 引入依赖

      <!--【JWT】1、引入依赖-->
      <dependency>
          <groupId>com.auth0</groupId>
          <artifactId>java-jwt</artifactId>
          <version>3.18.3</version>
      </dependency>
    2. 设计加密工具类,确定密钥、过期时间等;具体见附件项目中的util.JWTUtil类

    3. 登录时返回Token,使用工具类,生成token并返回给请求方;见附件项目中的dto.output.account.LoignOutputDto类、service.impl.AccountServiceImpl类;并可在https://jwt.io查看jwt内容

    4. 拦截器中使用Token进行认证与授权,验证token的有效性,并根据里面的Payload信息进行授权验证;见附件项目中的interceptor.AuthenticationInterceptor类

    5. 自动填充中表和Token中的用户信息,使用token信息,如果像创建人、更新人自动填充中使用第4步的方式获取token中的Payload信息进行填充

【演示】

  1. 基于Token的认证与授权,见附件中项目bug-record-backend-token

补充-2:认证与授权--基于Spring Security【了解】

概述

  • Spring Security 基于 Spring 框架,提供了一套 Web 应用安全性的完整解决方案

  • 一般来说,Web 应用的安全性包括用户认证(Authentication)和用户授权(Authorization)

    • 用户认证

      • 是验证某个用户是否为系统中的合法主体,即用户能否访问该系统

      • 一般要求用户提供用户名和密码

      • 系统通过校验用户名和密码来完成认证过程

    • 用户授权

      • 是验证某个用户是否有权限执行某个操作

      • 在软件中,不同用户所具有的权限是不同的;比如对一个文件来说,有的用户只能进行读取,而有的用户可以进行修改

      • 系统会为不同的用户分配不同的角色,而每个角色则对应一系列的权限

  • 一般会对接基于角色的访问控制(RBAC,Role-Based Access Control )

使用方式

  • 使用方式与会话模式一致,查看附件项目中有标识【JWT】的内容部分

    1. 引入依赖

      <!--【SpringSecurity】1、引入依赖-->
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-security</artifactId>
      </dependency>
    2. 定义UserDetails子类

      1. 将一些验证项返回true

      2. 包含用户信息(UserEntity类型)的属性

      3. 使用用户信息中的角色转换成Spring Security中的角色

      4. 见附件项目中的security.UserDetailsImpl类

    3. 定义UserDetailsService子类

      1. 覆盖loadUserByUsername方法,在此方法中,会接收到用户名,根据用户名从数据库中获取用户信息(UserEntity对象)

      2. 并返回上一步的UserDetails子类对象,里面包含从数据库中查询到的用户信息对象

      3. 见附件项目中的security.UserDetailsServiceImpl类

    4. 注入加密工具BCryptPasswordEncoder

      1. 定义BCryptPasswordEncoder工具类对象,并注入到Spring容器,采用SHA-256 +随机盐+密钥对密码进行加密

      2. 见附件项目中的security.WebSecurityConfig类中的passwordEncoder方法

    5. 注入按角色进行授权类SecurityFilterChain

      1. 使用HttpSecurity类对象,进行URL与角色匹配,完成Action认证与授权逻辑

      2. 见附件项目中的security.WebSecurityConfig类中的securityFilterChain方法,且类上需要加上@EnableWebSecurity注解

    6. 使用加密工具BCryptPasswordEncoder,分别在下列逻辑中使用

      1. 注册

      2. 新增用户

      3. 重置密码

    7. 获取登录用户信息,使用下列代码获取,一般用于创建信息、更新信息拦截中

      //从SpringSecurity获取用户信息
      Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
      //登录票据
      UserDetailsImpl userDetails = null;
      if(authentication.getPrincipal() instanceof UserDetailsImpl) {
          userDetails = (UserDetailsImpl) authentication.getPrincipal();
      }
    8. 认证与授权就生效了

【演示】

  1. 基于Spring Security的认证与授权,见附件中项目bug-record-backend-spring-security

代码

网盘地址:链接:https://pan.baidu.com/s/1bCIsfG390nh-mTLzkAi7Bw?pwd=8888 

posted @ 2024-06-09 21:06  拐子  阅读(9)  评论(0编辑  收藏  举报