The second project -- EMOS enterprise online office applet

Code quantity:

  • Mobile terminal: 30000+
  • Back end: 5000+

Technology stack:

  • Mobile terminal: uni app + Vue + Javascript + less + wechat applet
  • Backend: SpringBoot + SpringMVC + MyBatis + Shiro+ JWT + Quartz + ThreadPool + RabbitMQ + Docker

Source address:

Chapter I installing software

Database: MySQL + MongoDB + Redis

Backend: IDEA

Front end: wechat applet development tool + HBuilderX

Virtual machine: VirtualBox, Linux system adopts CentOS

Chapter II foundation of back-end environment

  1. Create Spring Boot project with Maven

  2. Configure MySQL, MongoDB and Redis data sources

  3. Integrated SSM framework

  4. Custom exception classes and encapsulated result sets

  5. Integration with Swagger for easy invocation of test Web methods

  6. Configure backend authentication

  7. Defend against XSS attacks by cross site scripting

    XSS attack usually refers to injecting malicious instruction code into the web page through clever methods by taking advantage of the loopholes left during web page development, so that the user can load and execute the web page program maliciously created by the attacker. These malicious web programs are usually JavaScript, but they can actually include Java, VBScript, ActiveX, Flash or even ordinary HTML. After a successful attack, the attacker may get various contents, including but not limited to higher permissions (such as performing some operations), private web page content, sessions, cookie s and so on.

    For example, when posting or registering, the user enters < script > alert (123) < / script > in the text box. If it is saved to the database without escape, the code will be executed during rendering in the future.

    Therefore, the most effective way is to escape the data entered by the user.

    If you rewrite the HttpServletRequest class, there are too many methods to override, which is very time-consuming.

    Just inherit the HttpServletRequestWrapper class, which is the request incoming wrapper class. The decorator mode is adopted, and the methods can be modified at will.

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

    It is not enough to define this class. You must write a filter so that the user's request passes through the wrapper class instead of the normal request method

    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 Yuan Mengda 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() {
    
        }
    }
    
    

Chapter III advanced construction of back-end environment

  1. Integrate shiro and jwt

    shiro is a framework for authentication and authorization, and jwt is used to generate and verify token s

  2. Refresh token

    The client and the server store the same token. The valid time of the server is twice that of the client. When the client's token expires, go to redis on the server to find out whether the token has expired. If it has not expired, a new token will be generated; If the token on the server also expires, the user must log in again

Chapter 4 create mobile terminal with UNI-APP

How to bind employee account and wechat account

  1. When you click Register, wechat will apply for temporary authorization, and wechat will send a temporary authorization string
  2. Pass the string to the back end, and the back end sends AppId, key and string to the wechat platform in exchange for OpenId
  3. Add OpenId, employee account, avatar, nickname, etc. to the database
  4. In the future, when employees log in, compare the OpenId sent by wechat with the OpenId in the database

Chapter V landing and registration

  1. Register super administrator
  2. RBAC permission model
  3. Encapsulates the applet's global path and Ajax requests
  4. Super administrator login

Chapter VI fundamentals of face check-in

  1. Open object cloud storage service

  2. Realize some functions of the home page

    • The rotation chart adopts labels and is nested inside
    • The column navigation adopts flex layout
  3. Design face check-in page

    • Business process: taking photos – > saving pictures – > hiding the camera area – > displaying pictures – > Click sign in

      At first, the photos are hidden. When you click to take photos, the photos will be displayed and the camera area will be hidden, and then the Photo button will change to the check-in button

    • The wechat applet provides a tag to call the system camera. You can take photos by calling the takePhoto() method of the camera object

  4. Cache system constant data

    sys_ Some constant configuration information is saved in the config data table, such as when attendance starts and ends. It needs to be loaded when springboot starts, cached as java objects, and can be used globally

    Process: read the table to get a list set, encapsulate the constant name and corresponding value, then traverse the set, obtain the key and value for each object in the set (with key value pairs), then load the variable (i.e. key) corresponding to the constant class through reflection, and then assign value

  5. Query whether you can sign in at the current time

    technological process:

    1. dao layer:
      • Query whether the workday table is a workday today
      • Query whether today is a holiday in the holiday table
      • Query whether the current user has signed in today
    2. service layer:
      • Call the dao layer method to determine whether today is a working day. If not, it will be returned directly without signing in
      • Call the start and end time of check-in in the constant class to judge whether you can check-in now
      • If you can check in, call the third method of dao layer to judge whether the user has checked in. If not, you can check in
    3. controller layer: get userId from token, then call service.
  6. Realize shiro authentication and authorization

    1. Authentication: obtain the userId from the token, and then query whether the user exists in the database. If so, add the user object, token, etc. to the info to return

    2. Authorization: obtain the user object from the authenticated object, then obtain the userId, find the permission set in the database according to the userId, add it to Info and return.

      If a web method requires users to have relevant permissions, add the @ RequiresPermissions annotation

Chapter VII advanced sign in

  1. Understand the whole process of face check-in model
    • The applet determines whether it can sign in. If not, the button is disabled; If you can, take a picture, click check-in, get the geographical coordinates through getLocation(), then Tencent cloud location service will convert it to the real address, and send the address and the url of the taken picture to the back-end Java program
    • Java gets the information from the front end, first judges whether the face model of the photo exists, if not, select Create or not, and send it to Python program for creation; If it exists, it will be sent to the python program to determine whether the face model belongs to the user. After successful identification, judge the epidemic risk level of the check-in area (send it to the local treasure h5 page and parse the returned response with JSOUP). If it is high risk, send a warning email. Finally, save the check-in record
  2. Open Tencent cloud location service and turn coordinates into addresses
  3. Deploy face recognition program in Docker
  4. Obtain the epidemic risk level of the check-in location and send an email
  5. Judge whether check-in can be performed at the current time
  6. Execute face check-in and record the check-in results
  7. New employee creates face model data

Chapter 8 face check-in and upgrade

  1. Write static content of successful check-in page
  2. Write code to query the data related to the successful check-in page
  3. Write a user page to encapsulate the mobile terminal authority verification function
  4. Write static content of my attendance page
  5. Query monthly attendance data

Chapter IX system notification module

  1. The design principle of message module is analyzed
  2. Introduction and installation of RabbitMQ
  3. Design MVC code of message module
  4. Write a thread task class to send and receive messages
  5. Calling the message module in other business processes

Chapter X conference management function

  1. Design static pages
  2. Realize the paging display function of meeting list
  3. Dynamically edit participants
  4. Successfully deployed workflow project
  5. Realize the addition, modification and deletion of meetings

Chapter XI online video conference

  1. Create online approval page and approval details page
  2. Complete online approval function
  3. Open Tencent cloud TRTC service
  4. Online multiplayer video conference
  5. Realize the calendar and meeting list on the home page

Added by ldmccla on Fri, 14 Jan 2022 03:59:40 +0200