Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Design and Implementation of a Personal Financial Management System Using SpringBoot, Vue, and uniapp

Tech 1

Technical Overview

The system is built using the SpringBoot framework for the backend, which includes embedded servers like Tomcat, Jetty, and Undertow. This allows for quick deployment without additional configuration. SpringBoot's auto-configuration feature simplifies setup by automatically configuring dependencies based on project needs. It also provides features such as Spring Data, Spring Security, and Spring Cloud, enhancing development efficiency.

For the frontend, Vue.js is used, leveraging its virtual DOM technology to enable efficient updates when data changes. Vue offers responsive data binding, componant-based architecture, and an intuitive API, making it easy to build scalable and maintainable applications.

The persistence layer uses MyBatis-Plus, an enhanced version of MyBatis that streamlines database operations. It supports various databases and includes tools like code generation, pagination, dynamic queries, and optimistic locking, reducing manual SQL writing and improving productivity.

Testing

System testing focuses on identifying issues through functional tests, ensuring all features meet requirements. Testing involves creating test cases that cover input validation, boundary checks, and role-based access control. The goal is to validate functionality, ensure usability, and confirm that the system meets user expectations.

Login Functionality Test Cases:

User Management Test Cases:

Code Example:

@IgnoreAuth
@PostMapping("/login")
public R login(String username, String password, String captcha, HttpServletRequest request) {
   UsersEntity user = userService.selectOne(new EntityWrapper<UsersEntity>().eq("username", username));
   if(user == null || !user.getPassword().equals(password)) {
      return R.error("Invalid username or password");
   }
   String token = tokenService.generateToken(user.getId(), username, "users", user.getRole());
   return R.ok().put("token", token);
}

@Override
public String generateToken(Long userId, String username, String tableName, String role) {
   TokenEntity tokenEntity = this.selectOne(new EntityWrapper<TokenEntity>().eq("userId", userId).eq("role", role));
   String token = CommonUtil.getRandomString(32);
   Calendar cal = Calendar.getInstance();
   cal.setTime(new Date());
   cal.add(Calendar.HOUR_OF_DAY, 1);
   if(tokenEntity != null) {
      tokenEntity.setToken(token);
      tokenEntity.setExpiryTime(cal.getTime());
      this.updateById(tokenEntity);
   } else {
      this.insert(new TokenEntity(userId, username, tableName, role, token, cal.getTime()));
   }
   return token;
}

@Component
public class AuthorizationInterceptor implements HandlerInterceptor {

    public static final String AUTH_TOKEN_HEADER = "Authorization";

    @Autowired
    private TokenService tokenService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
        response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Credentials", "true");
        response.setHeader("Access-Control-Allow-Headers", "x-requested-with,request-source,Authorization, Origin,imgType, Content-Type, cache-control,postman-token,Cookie, Accept,authorization");
        response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));

        if (request.getMethod().equals("OPTIONS")) {
            response.setStatus(HttpStatus.OK.value());
            return false;
        }

        IgnoreAuth annotation;
        if (handler instanceof HandlerMethod) {
            annotation = ((HandlerMethod) handler).getMethodAnnotation(IgnoreAuth.class);
        } else {
            return true;
        }

        String token = request.getHeader(AUTH_TOKEN_HEADER);
        if (annotation != null) {
            return true;
        }

        TokenEntity tokenEntity = null;
        if (StringUtils.isNotBlank(token)) {
            tokenEntity = tokenService.getTokenEntity(token);
        }

        if (tokenEntity != null) {
            request.getSession().setAttribute("userId", tokenEntity.getUserId());
            request.getSession().setAttribute("role", tokenEntity.getRole());
            request.getSession().setAttribute("tableName", tokenEntity.getTableName());
            request.getSession().setAttribute("username", tokenEntity.getUsername());
            return true;
        }

        PrintWriter writer = null;
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json; charset=utf-8");
        try {
            writer = response.getWriter();
            writer.print(JSONObject.toJSONString(R.error(401, "Please log in first")));
        } finally {
            if (writer != null) {
                writer.close();
            }
        }
        return false;
    }
}

Database Structure:

-- ----------------------------
-- Table structure for token
-- ----------------------------
DROP TABLE IF EXISTS `token`;
CREATE TABLE `token` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'Primary key',
  `userId` bigint(20) NOT NULL COMMENT 'User ID',
  `username` varchar(100) NOT NULL COMMENT 'Username',
  `tableName` varchar(100) DEFAULT NULL COMMENT 'Table name',
  `role` varchar(100) DEFAULT NULL COMMENT 'Role',
  `token` varchar(200) NOT NULL COMMENT 'Token value',
  `addTime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'Add time',
  `expiryTime` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT 'Expiry time',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=27 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='Token table';

-- ----------------------------
-- Records of token
-- ----------------------------
INSERT INTO `token` VALUES ('9', '23', 'cd01', 'student', 'student', 'al6svx5qkei1wljry5o1npswhdpqcpcg', '2023-02-23 21:46:45', '2023-03-15 14:01:36');
INSERT INTO `token` VALUES ('10', '11', 'xh01', 'student', 'student', 'fahmrd9bkhqy04sq0fzrl4h9m86cu6kx', '2023-02-27 18:33:52', '2023-03-17 18:27:42');
INSERT INTO `token` VALUES ('11', '17', 'ch01', 'student', 'student', 'u5km44scxvzuv5yumdah2lhva0gp4393', '2023-02-27 18:46:19', '2023-02-27 19:48:58');
INSERT INTO `token` VALUES ('12', '1', 'admin', 'user', 'admin', 'h1pqzsb9bldh93m92j9m2sljy9bt1wdh', '2023-02-27 19:37:01', '2023-03-17 18:23:02');
INSERT INTO `token` VALUES ('13', '21', 'xiaohao', 'club_leader', 'club_leader', 'zdm7j8h1wnfe27pkxyiuzvxxy27ykl2a', '2023-02-27 19:38:07', '2023-03-17 18:25:20');
INSERT INTO `token` VALUES ('14', '27', 'djy01', 'student', 'student', 'g3teq4335pe21nwuwj2sqkrpqoabqomm', '2023-03-15 12:56:17', '2023-03-15 14:00:16');
INSERT INTO `token` VALUES ('15', '29', 'dajiyue', 'club_leader', 'club_leader', '0vb1x9xn7riewlp5ddma5ro7lp4u8m9j', '2023-03-15 12:58:08', '2023-03-15 14:03:48');

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.