【做题系统】后端设计

2023-05-16

目录

一、设计思路

1.项目背景

2.技术栈选择

二、系统设计

1.系统结构图

2.项目结构 

3.数据建模

4.数据流图

5.主要流程图

三、问题及解决办法

1.实现安全登录、访问

2.数据库中的信息安全问题

3.Mybatis-plus如何实现多表联查问题

4.当查询到很多条数据的时候,如何高效实现分页

5.当删除了一条数据之后,怎么实现恢复

四、亮点

1.采用shiro框架实现安全访问数据库

2.封装了一个自定义的通用结果类来返回请求结果,并且使用了构造器的方法。

3.接口的设计采用了RESTful风格

五、收获与感悟

六、附录

1.接口文档


一、设计思路

1.项目背景

        一种在线做题系统。

2.技术栈选择

        前端: Vue2 + VueX + VueRouter+ Element + HTML + axios

        后端:Spring  + Mybatis-plus + Maven

        数据库:MySQL

        中间件:tomcat + shiro + JWT + log4j

        工具:idea + Vscode + git + navicat + Apifox

        插件:fastjson,druid,lombok,

二、系统设计

1.系统结构图

2.项目结构 

3.数据建模

(1)数据表的信息

sh_user(ID,username,password,salt);
sh_role(ID,rolename,description,locked);
sh_user_role(ID,userId,roleId,enabled);
sh_permission(ID,permissionName,requestPath,description,label,parentId,isDeleted);
sh_role_permission(ID,roleId,permissionId,enabled);
user_info(userId,nikeName,tel,email,quesNum,collectedNum,paperNum,createdTime);
admin_info(userId,nikeName,tel,email);
user_paper(ID,userId,paperId);
paper_info(ID,paperId,paperName,paperSource);
judge_question(quesId,quesType,quesLevel,quesSource,quesContent,quesAnswer,quesAnalysis);
select_question(quesId,quesType,quesLevel,quesSource,quesContent,option1,option2,option3,option4,quesAnswer,quesAnalysis);
discuss_question(quesId,quesType,quesLevel,quesSource,quesContent,quesAnswer,quesAnalysis);
paper_question(ID,paperId,quesId);
question_comment(ID,quesId,userId,content);
question_info(ID,quesId,class);
question_type(ID,quesId,quesType);
user_question_collected(ID,userId,quesId);
user_question_state(ID,userId,quesId,state,yourAnswer,visitTime);

 (2)E-R图

        由于各实体的属性值过多,下面的图中不展示实体的属性值。

 

4.数据流图

(1)管理员

(2)用户

5.主要流程图

(1)注册、登录、退出流程图

(2) 修改个人信息

(3)修改密码

(4)管理员对用户操作

(5)管理员对题目操作

(6)用户组卷

三、问题及解决办法

1.实现安全登录、访问

        这里采用了shiro框架,用Shiro来帮助我们完成:认证、授权、加密、会话管理、与 Web 集成、缓存等操作。具体介绍看我这篇博客。

2.数据库中的信息安全问题

        为了防止数据库信息泄露而导致的用户信息泄露问题,这里采用了MD5加密的方式,在数据库中存储加密后的字段。只要方式是通过shiro的配置来完成,在shiro写入的时候使用md5+salt+散列的方式进行数据加密之后存入数据库,读出的时候进行响应的解密。

简单操作步骤:

        ①maven中导入shiro坐标

<!-- shiro -->
<dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-spring-boot-starter</artifactId>
        <version>1.9.1</version>
</dependency>

        ② 自定义一个realm,用于认证授权等操作

public class CustomerRealm extends AuthorizingRealm {

    @Autowired
    private UserService userService;

    /**
     * 认证方式
     * @param authenticationToken   校验传入令牌
     * @return AuthenticationInfo
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        String principal = (String) authenticationToken.getPrincipal();
        User user = userService.getByUsername(principal);
        if (!ObjectUtils.isEmpty(user)) {
            return new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(), ByteSource.Util.bytes(user.getSalt()), this.getName());
        }
        return null;
    }

    /**
     * 授权方式
     * @param  principalCollection SimpleAuthenticationInfo对象第一个参数
     * @return AuthorizationInfo
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        return null;
    }

}

        ③编写shiro的配置文件

public class ShiroConfig {

    // 1. 创建ShiroFilter,负责拦截请求
    @Bean(name = "shiroFilterFactoryBean")
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager defaultWebSecurityManager){
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        // 设置安全管理器
        shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);

        // 配置系统受限资源和公告资源
        HashMap<String, String> map = new HashMap<>();
        // anon 表示公共资源, authc 表示需要认证授权
        map.put("/index.html","anon");
        map.put("/page/login.html","anon");     // 放行登录页面
        map.put("/page/register.html","anon");  // 放行注册页面
        map.put("/user/**","anon");          // 放行用户控制器
        map.put("/admin/**","anon");          // 放行管理员控制器

        map.put("/**","authc");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(map);

        // 默认认证界面,失败后跳转
        shiroFilterFactoryBean.setLoginUrl("/page/login.html");

        return shiroFilterFactoryBean;
    }

    //2. 创建安全管理器
    @Bean
    public DefaultWebSecurityManager getDefaultWebSecurityManager(Realm realm){
        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
        // 给安全管理器设置realm
        defaultWebSecurityManager.setRealm(realm);

        return defaultWebSecurityManager;
    }

    // 3.创建自定义realm,md5 + salt + 散列
    @Bean
    public Realm getRealm(){
        CustomerRealm customerRealm = new CustomerRealm();
        HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
        //设置使用MD5加密算法
        credentialsMatcher.setHashAlgorithmName("md5");
        //散列次数
        credentialsMatcher.setHashIterations(1024);
        customerRealm.setCredentialsMatcher(credentialsMatcher);
        return customerRealm;
    }
}

3.Mybatis-plus如何实现多表联查问题

        MP提供了大量单表查询的方法,但是没有多表的操作,所以涉及到多表的查询时,需要我们自己实现。

简单步骤如下:

        ①在一个的mapper中创建一个对多的对象,例如,一个用户有很多各角色,一个角色也可以对应很多各用户,那么就可以在用户mapper中定义一个角色列表,在角色mapper中定义一个用户列表。

/**
 * 用户mapper
 */
@Mapper
public interface UserMapper extends BaseMapper<User> {
    /**
     * 获取用户的所有角色
     * @param userId 用户id
     * @return List<Role>
     */
    List<Role> getRoles(String userId);
}

/**
 * 权限mapper
 */
@Mapper
public interface RoleMapper extends BaseMapper<Role> {
    /**
     * 获取一个角色有哪些用户
     * @param roleId  角色id
     * @return List<User>
     */
    List<User> getUsers(String roleId);
}

        ②创建对应的xml文件,在xml文件中实现数据库的查询操作。

<mapper namespace="com.ops.mapper.UserMapper">
    <select id="getRoles" resultType="List" parameterType="String" >
        select roleId from sh_user_role where userId = #{id};
    </select>
</mapper>

4.当查询到很多条数据的时候,如何高效实现分页

        其实Mybatis-plus中已经提供了对应的分类功能,简单实现步骤如下:

①编写拦截器配置类

@Configuration
public class MybatisConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));    //填写对应的数据库
        return interceptor;
    }
}

②在接口处设置分页参数

@GetMapping("/test")
public Response test(){
    Page<Produce> producePage = new Page<>(1,1);
    Page<Produce> page = produceService.page(producePage);
    System.out.println(producePage == page);
    List<Produce> records = page.getRecords();
    for (Produce record : records) {
        System.out.println(record);
    }
    return new Result<>(records, ResultEnum.SUCCESS);
}

5.当删除了一条数据之后,怎么实现恢复

        在表中增加一个字段名为“is_deleted"的字段,字段默认为0,表示没有删除,当执行了删除操作之后,会将该字段设置为1,表示已经删除了,以后的查询操作都会建立在”is_deleted"为0的基础上进行查询。

四、亮点

1.采用shiro框架实现安全访问数据库

        shiro的处理流程为:

                

        流程如下:

        1、Shiro把用户的数据封装成标识token,token一般封装着用户名,密码等信息

        2、使用Subject门面获取到封装着用户的数据的标识token

        3、Subject把标识token交给SecurityManager,在SecurityManager安全中心中,SecurityManager把标识token委托给认证器Authenticator进行身份验证。认证器的作用一般是用来指定如何验证,它规定本次认证用到哪些Realm

        4、认证器Authenticator将传入的标识token,与数据源Realm对比,验证token是否合法

2.封装了一个自定义的通用结果类来返回请求结果,并且使用了构造器的方法。

        在这里首先定义了一个表示状态的枚举类(status.java),里面封装了所有的状态和状态描述信息。然后再定义了一个结果类,并且使用了构造器的方法。

public class Result {
    private final Integer code;    //状态码
    private final String message;  //错误的的状态信息
    private final Object data;     //数据

    public static class Builder{
        private Integer code;    //状态码
        private String message;  //错误的的状态信息
        private Object data = null;   //数据

        public Builder(Integer code, String message) {
            this.code = code;
            this.message = message;
        }

        public Builder code(Integer code){
            this.code = code;
            return this;
        }
        public Builder message(String message){
            this.message = message;
            return this;
        }
        public Builder data(Object data){
            this.data = data;
            return this;
        }

        public Result build(){
            return new Result(this);
        }

    }

    private Result(Builder builder){
        this.code = builder.code;
        this.message = builder.message;
        this.data = builder.data;
    }

}

        这样就可以通过如下方式来构造:

// 当没有data时:
return new Result.Builder().code(Status.OK().code).message(Status.OK().message);
// 当有data时:
return new Result.Builder().code(Status.OK().code).message(Status.OK().message).data(data);

        关于这一部分的详细知识点可以看我的另一篇博客(实在是太优雅啦!!)

3.接口的设计采用了RESTful风格

         具体介绍可以看我这一篇博客。接口文档见附录。

五、收获与感悟

        通过这次后端项目的实战,加深了对框架的理解和认识,熟悉了利用框架开发的流程和方法;同时还学会了不同技术和spring-boot进行整合的方式;接触到了mybatis-plus,它是mybatis的升级版,是由国人开发的,符合了中国人的开发思维,能够极大地简化开发;同时新学习了shiro安全框架,保证数据和程序运行的安全性,相比于spring-security框架,shiro更加简洁轻便,学习成本更低。

        当然这个项目还有很多不完善的地方,在异常报错的处理方面还不够细致,当出现异常的时候,不能够准确地指出问题的出处,而是比较笼统地指出问题的原因,这一点需要改善,可以通过在status类中新增各种的请求状态来完成;同时,项目中没有很注重失败原子性的操作,失败的方法调用应该使对象保持在被调用之前的状态,当然实现的方法也有很多,可以看我的这篇博客;当然,由于项目的规模是比较小的,所以也没有注重高并发相关的处理。

六、附录

1.接口文档

        由于篇幅限制,接口文档点击这里查看

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【做题系统】后端设计 的相关文章

  • 最详细UWB技术及特点介绍

    关注与分享 xff0c 是对原创最大的鼓励 这篇文章偏技术 xff0c 信息偏深 xff0c 建议大家可以先跳到感兴趣的章节阅读 xff1b 01 UWB简介 UWB是Ultra Wide Band缩写 xff0c 来源于很久之前的脉冲通信
  • 浅聊Matter协议 (原CHIP协议)

    聚焦 xff1a 芯产品 xff0c 芯市场 xff0c 芯资讯 因为Matter协议目前还没有发布 xff0c 标准只针对部分协会成员开放 xff1b 很多朋友可能听过这个名字 xff0c 然后知道是一个 上层 协议 xff0c 更多内容
  • 2021 MCU WiFi竞争新格局,国产MCU WiFi芯片盘点,附录2020/2021 MCU WiFi排行

    关注智联网事 iotthings xff1a 芯产品 xff0c 芯市场 xff0c 芯资讯 缺货 xff0c 是半导体产业2021年最主要的基调 xff0c 有公司拿不到产能 xff0c 有公司新芯片流片周期大幅拉长 xff1b 新冠病情
  • 芯科(Silabs) Matter 全栈解决方案,附录高质量Matter培训资

    关注智联网事 xff1a 芯产品 xff0c 芯市场 xff0c 芯资讯 对芯科的最初印象 xff0c 最早应该是2014 5年左右 xff0c 当时SI44xx系列渗透了很多市场的客户 xff0c 记得一个是低功耗的特性 xff0c 一个
  • WAF技术选型介绍

    WAF目前是企业必不可少的安全设备 xff0c 目前常见的开源技术选型包括 xff1a jxwafopenstarngx lua wafApache APISIXmodsecurity 介绍参考 xff1a https zhuanlan z
  • int 占多少字节

    char 1 int 4 long 8 float 4 double 8 xff08 1 xff09 使用VC xff0c int类型占4个字节 xff08 2 xff09 使用Turbo C xff0c int类型占2个字节 16位编译器
  • 海外LPWAN的王者是我,一文看懂Wi-Sun协议

    聚焦 xff1a 芯产品 xff0c 芯市场 xff0c 芯技术 注 xff1a 欢迎加入文章底部的 lt 物联坊间 gt 微信 刚刚毕业的我 xff0c 有参与城市照明系统的建设 xff0c 包括城市公交系统 xff0c 那个时候困扰我的
  • 22家国产汽车MCU公司及型号盘点

    专注芯片 xff0c 应用系统 xff0c 行销技能的公众号 如果有一家芯片MCU或模拟公司和你说 xff0c 他不做汽车方向芯片 xff0c 你可以内心欣喜的 xff0c 严肃的问一句 xff0c 为什么 xff1b 做汽车芯片 xff0
  • 2022 MCU公司交卷,总营收84.8亿人民币,排名第一和最后的分别是

    2022财报季结束 xff0c 我们看下上市MCU公司的最新排名 xff0c 毛利 xff0c 库存及库存周转率情况 xff1b 根据 Omdia 的数据 xff0c 2022 年中国 MCU 市场规模约为 82 亿美元 xff0c 小二统
  • 深度:旋转变压器原理,芯片,算法,选型

    之前介绍的新能源汽车电机控制器 MCU 和电动助力转向 EPS 文章中 xff0c 有提到电机的角度反馈可选择转旋转变压器方案 xff0c 今天做个分享 xff0c 欢迎留言交流 本文目录 xff1a 旋转变压器应用及参数概览 旋转变压器原
  • 实时微控制器的关键技术及国产玩家,国产DSP盘点

    小二用芯在写 xff0c 如果您觉得有帮助 xff0c 帮忙朋友圈推荐下 34 xff0c 感谢 xff01 在介绍OBC xff0c DCDC时候 xff0c 觉得有必要对主控芯片做个介绍 xff0c 比如为什么说数字电源的控制一般集成H
  • 天猫精灵的开发者生态

    文章转自 智联网事 欢迎关注 xff0c 每周一篇原创 xff0c 直至 No End https mp weixin qq com s biz 61 MzI3NDE2NDMwNQ 61 61 amp mid 61 2649905740 a
  • 蓝牙Mesh网络性能及网络特点总结(一)

    原文链接 xff1a 欢迎关注公众号 智联网事 xff0c 一周一篇原创文章 xff0c 一起探讨智联网 https mp weixin qq com s biz 61 MzI3NDE2NDMwNQ 61 61 amp mid 61 264
  • 华为物联网(IOT)开发者平台

    智联网事 关注与分享 xff0c 是对原创最大的鼓励 原文链接 https mp weixin qq com s biz 61 MzI3NDE2NDMwNQ 61 61 amp mid 61 2649905835 amp idx 61 1
  • Kubernetes v1.21.14二进制搭建单节点集群

    1 集群环境准备 1 1 主机规划 IP主机名主机角色操作系统安装组件192 168 11 71k8s master1master workerCentos7 9api server controller manager scheduler
  • shell脚本第一行:#!/bin/bash的含义

    相信有接触过shell脚本的同学们都应该知道 xff0c shell脚本的第一行一般会写有以下字样 xff1a bin bash或者 bin sh或者 bin awk 比较常见的说法是 xff1a 第一行的内容指定了shell脚本解释器的路
  • LPMS-IMU姿态解算

    参考文章 xff1a AHRS姿态解算说明 加速度 43 陀螺仪 43 磁力计原理及原始数据分析 AHRS俗称航姿参考系统 xff0c AHRS由加速度计 xff0c 磁场计 xff0c 陀螺仪构成 xff0c AHRS的真正参考来自于地球
  • 如何在我的VsCode中集成Git

    在VsCode中配置Git后 xff0c 我们就可以简单快速管理我们的代码仓库 一 下载安装Git 如果没有Git xff0c 这里可以参考我之前的文章安装 配置Git 精讲Git xff08 从安装到熟练使用一文全解 xff0c 看完轻松
  • 虚拟机:xshell连接虚拟机Ubuntu失败解决方法

    当我们买不起服务器但却想模拟服务器环境进行学习时 xff0c 再好不过的就是直接装Linux虚拟机了 xff0c 非常简单快捷 xff01 首先我们可以通过ifconfig a来查看Ubuntu Server的IP地址 xff08 前提是你
  • SVM原理:超平面方程

    xff08 1 xff09 超平面方程 3维空间中平面方程的一般形式 xff1a 1 我们都知道为平面到原点的距离 这里简单证明超平面的法向量为 d维空间平面方程的一般形式 xff1a 2 平面的法向量为 xff0c xff08 分号表示列

随机推荐