第二个项目---EMOS企业在线办公小程序

代码量:

  • 移动端:30000+
  • 后端:5000+

技术栈:

  • 移动端:uni-app + Vue + JavaScript + Less + 微信小程序
  • 后端:SpringBoot + SpringMVC + MyBatis + Shiro+ JWT + Quartz + ThreadPool + RabbitMQ + Docker

源码地址:

  • 移动端源码
  • 后端源码

第一章 安装软件

数据库:MySQL + MongoDB + Redis

后端:IDEA

前端:微信小程序开发工具 + HBuilderX

虚拟机:VirtualBox,Linux系统采用CentOS

第二章 后端环境搭建基础

  1. 利用Maven创建Spring Boot项目

  2. 配置MySQL、MongoDB、Redis数据源

  3. 整合SSM框架

  4. 自定义异常类和封装结果集

  5. 集成Swagger,便于调用测试Web方法

  6. 配置后端验证功能

  7. 抵御跨站脚本XSS攻击

    XSS攻击通常指的是通过利用网页开发时留下的漏洞,通过巧妙的方法注入恶意指令代码到网页,使用户加载并执行攻击者恶意制造的网页程序。这些恶意网页程序通常是JavaScript,但实际上也可以包括Java、 VBScript、ActiveX、 Flash 或者甚至是普通的HTML。攻击成功后,攻击者可能得到包括但不限于更高的权限(如执行一些操作)、私密网页内容、会话和cookie等各种内容。

    比如用户在发帖或注册时,文本框中输入,如果不经过转义,保存到数据库里后,将来渲染时会执行该代码。

    所以最有效的办法就是将用户输入的数据进行转义。

    如果重写HttpServletRequest类,需要覆盖的方法太多,非常耗时。

    只需继承HttpServletRequestWrapper类,该方法为请求传入包装类,采用装饰器模式,可以随意修改其中的方法。

    package com.example.emos.wx.config.xss;
    
    import cn.hutool.core.util.StrUtil;
    import cn.hutool.http.HtmlUtil;
    import cn.hutool.json.JSONUtil;
    
    import javax.servlet.ReadListener;
    import javax.servlet.ServletInputStream;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletRequestWrapper;
    import java.io.*;
    import java.nio.charset.Charset;
    import java.util.HashMap;
    import java.util.LinkedHashMap;
    import java.util.Map;
    
    /**
     * @author 袁梦达 2019012364
     */
    public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
        public XssHttpServletRequestWrapper(HttpServletRequest request) {
            super(request);
        }
    
        @Override
        public String getParameter(String name) {
            String value = super.getParameter(name);
            if(!StrUtil.hasEmpty(value)){
                value = HtmlUtil.filter(value);
            }
            return value;
        }
    
        @Override
        public String[] getParameterValues(String name) {
            String[] values = super.getParameterValues(name);
            if(values != null){
                for(int i = 0; i < values.length; i++){
                    String value = values[i];
                    if(!StrUtil.hasEmpty(value)){
                        value = HtmlUtil.filter(value);
                    }
                    values[i] = value;
                }
            }
            return values;
        }
    
        @Override
        public Map<String, String[]> getParameterMap() {
            Map<String, String[]> parameters = super.getParameterMap();
            Map<String, String[]> map = new LinkedHashMap<>();
            if(parameters != null){
                for (String key : parameters.keySet()) {
                    String[] values = getParameterValues(key);
                    for(int i = 0; i < values.length; i++){
                        String value = values[i];
                        if(!StrUtil.hasEmpty(value)){
                            value = HtmlUtil.filter(value);
                        }
                        values[i] = value;
                    }
                    map.put(key, values);
                }
            }
            return map;
        }
    
        @Override
        public String getHeader(String name) {
            String value = super.getHeader(name);
            if(!StrUtil.hasEmpty(value)){
                value = HtmlUtil.filter(value);
            }
            return value;
        }
    
        @Override
        public ServletInputStream getInputStream() throws IOException {
            InputStream in = super.getInputStream();
            StringBuffer body = new StringBuffer();
            InputStreamReader reader = new InputStreamReader(in, Charset.forName("UTF-8"));
            BufferedReader buffer = new BufferedReader(reader);
            String line = buffer.readLine();
            while (line != null){
                body.append(line);
                line = buffer.readLine();
            }
            buffer.close();
            reader.close();
            in.close();
    
            Map<String, Object> map = JSONUtil.parseObj(body.toString());
            Map<String, Object> resultMap = new LinkedHashMap<>();
            for (String key : map.keySet()) {
                Object val = map.get(key);
                if(map.get(key) instanceof String){
                    resultMap.put(key, HtmlUtil.filter(val.toString()));
                }else {
                    resultMap.put(key, val);
                }
            }
    
            String str = JSONUtil.toJsonStr(resultMap);
            ByteArrayInputStream bain = new ByteArrayInputStream(str.getBytes());
            return new ServletInputStream() {
                @Override
                public boolean isFinished() {
                    return false;
                }
    
                @Override
                public boolean isReady() {
                    return false;
                }
    
                @Override
                public void setReadListener(ReadListener readListener) {
    
                }
    
                @Override
                public int read() throws IOException {
                    return bain.read();
                }
            };
        }
    }
    
    

    只定义该类还不够,必须写一个过滤器,让用户的请求是经过这个wrapper类,而不是正常的request方法

    package com.example.emos.wx.config.xss;
    
    import javax.servlet.*;
    import javax.servlet.annotation.WebFilter;
    import javax.servlet.http.HttpServletRequest;
    import java.io.IOException;
    
    /**
     * @author 袁梦达 2019012364
     */
    
    @WebFilter(urlPatterns = "/*")
    public class XssFilter implements Filter {
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
    
        }
    
        @Override
        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
            XssHttpServletRequestWrapper wrapper = new XssHttpServletRequestWrapper((HttpServletRequest) servletRequest);
            filterChain.doFilter(wrapper, servletResponse);
        }
    
        @Override
        public void destroy() {
    
        }
    }
    
    

第三章 后端环境搭建进阶

  1. 整合shiro和jwt

    shiro是进行认证与授权的框架,jwt是用来生成、验证token的

  2. 刷新token

    客户端和服务端存一样的token,服务端的有效时间是客户端的2倍,当客户端的token过期后,去服务端的redis中查找token是否过期,如果还未过期,则生成新的token;如果服务端的token也过期,那么就要让用户重新登录

第四章 利用UNI-APP创建移动端

如何将员工账户和微信账号绑定

  1. 点击注册时,微信申请临时授权,微信会发送一个临时授权字符串
  2. 将字符串传给后端,后端向微信平台发送AppId、密钥、字符串换取OpenId
  3. 将OpenId、员工账户、头像、昵称等添加到数据库中
  4. 以后员工登录时,将登录时微信发出的OpenId与数据库中的OpenId去比对

第五章 登陆与注册

  1. 实现注册超级管理员
  2. RBAC权限模型
  3. 封装小程序的全局路径和Ajax请求
  4. 实现超级管理员登陆

第六章 人脸签到基础

  1. 开通对象云存储服务

  2. 实现首页部分功能

    • 轮播图采用标签,里面嵌套
    • 栏目导航采用flex布局
  3. 设计人脸签到页面

    • 业务流程:拍照–>保存图片–>隐藏摄像区–>显示图片–>点击签到

      一开始照片是隐藏的,当点击拍照后,照片显示出来,摄像区隐藏,然后拍照按钮变为签到按钮

    • 微信小程序提供标签来调用系统相机,调用相机对象的takePhoto()方法即可拍照

  4. 缓存系统常量数据

    sys_config数据表中保存了一些常量配置信息,如:考勤几点开始,几点结束等。需要在springboot启动时加载,缓存成java对象,全局都可以使用

    流程:读取表得到一个list集合,封装了常量的名字和对应的值,然后遍历这个集合,对集合中每一个对象(有键值对),获取键和值,然后通过反射将常量类对应的变量(即键)加载,然后赋值

  5. 查询当前时刻是否可以签到

    流程:

    1. dao层:
      • 查询工作日表今天是否为工作日
      • 查询节假日表今天是否为节假日
      • 查询当前用户在今天是否签到过
    2. service层:
      • 调用dao层的方法判断今天是否为工作日,不是的话直接返回无需签到
      • 调用常量类中的签到开始和结束时间,判断现在是否可以签到
      • 如果可以签到的话,调用dao层的第三个方法判断该用户是否签到过,如果没签到可以进行签到
    3. controller层:从token中获取userId,然后调用service
  6. 实现shiro认证与授权

    1. 认证:从token中获取userId,然后查询数据库中是否有该用户,如果有的话将user对象,token等添加到info里返回

    2. 授权:从认证后的对象中获取user对象,然后获取userId,根据userId在数据库中查找拥有的权限集合,添加到Info中返回。

      如果某个web方法需要用户具有相关权限,则加上@RequiresPermissions注解即可

第七章 人脸签到进阶

  1. 了解人脸签到模型的全部流程
    • 小程序端判定是否可以签到,如果不可以的话按钮为禁用状态;可以的话,拍照,点击签到,通过getLocation( )获取地理坐标,然后腾讯云位置服务转为真实地址,将地址和拍照图片的url传给后端Java程序
    • Java拿到前端传来的信息,先判断是否存在该照片的人脸模型,如果不存在则选择创建或不创建,并发送给python程序进行创建;如果存在则发送给python程序该人脸模型是否属于该用户。识别成功后,判断签到地区疫情风险等级(发送给本地宝h5页面,并用JSOUP解析返回的响应),如果为高风险则发送警告邮件。最后保存签到记录
  2. 开通腾讯云位置服务,把坐标转为地址
  3. 在Docker中部署人脸识别程序
  4. 获取签到地点的疫情风险等级,发送邮件
  5. 判断当前时刻能否执行签到
  6. 执行人脸签到,记录签到结果
  7. 新员工创建人脸模型数据

第八章 人脸签到升级

  1. 编写签到成功页面静态内容
  2. 编写代码查询签到成功页面相关数据
  3. 编写用户页面,封装移动端权限验证函数
  4. 编写我的考勤页面静态内容
  5. 查询月考勤数据

第九章 系统通知模块

  1. 分析消息模块的设计原理
  2. RabbitMQ的入门与安装
  3. 设计消息模块的MVC代码
  4. 编写线程任务类实现消息的收发
  5. 在其他业务流程中调用消息模块

第十章 会议管理功能

  1. 设计静态页面
  2. 实现会议列表分页显示功能
  3. 动态编辑参会成员
  4. 成功部署工作流项目
  5. 实现会议的添加、修改和删除

第十一章 在线视频会议

  1. 创建在线审批页面和审批详情页面
  2. 完成在线审批功能
  3. 开通腾讯云TRTC服务
  4. 可以在线多人视频会议
  5. 实现首页的日历和会议列表

你可能感兴趣的:(项目)