Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Engineering a Laboratory Equipment Monitoring System with Spring Boot and Vue.js

Tech May 17 4

Technology Stack Overview

The system architecture leverages specific frameworks to ensure scalability and maintainability. Spring Boot serves as the backend foundation, embedding containers like Tomcat or Jetty directly. This eliminates external configuration overhead. Key features include automatic bean assembly based on classpath dependencies and extensive starter packages for security (Spring Security) and data access (Spring Data). This reduces boilerplate code significantly.

On the frontend, Vue.js utilizes a virtual DOM to optimize rendering performence. The framework employs reactive data binding, ensuring UI components update instantly when state changes occur. Component-based architecture promotes reusability and modularity in the user interface layer.

Data persistence is handled by MyBatis-Plus, an enhancement of the classic MyBatis framework. It provides a powerful Object-Relational Mapping (ORM) capability, reducing manual SQL writing. Features include built-in pagination, dynamic query builders via lambdas, and automated CRUD operations. A code generation utility is also available to create entity classes and mapper interfaces from database schemas.

Testing Strategy

Comprehensive testing ensures the reliability of the laboratory equipment management platform. The primary objective is black-box testing, focusing on functional correctness without inspecting internal code structures. Test cases are designed to validate requirement adherence and identify logic flaws before deployment.

Authentication Validation

The login module verifies credentials against stored records and enforces session management. Inputs such as username, password, and CAPTCHA must match exact criteria. Incorrect inputs trigger specific error messages with out exposing sensitive system details.

Input Scenario Expected Outcome Actual Outcome Analysis
Valid Credentials (Admin) Successful Access System Granted Pass
Invalid Password Access Denied Error Message Shown Pass
Invalid Captcha Access Denied Verification Failed Pass
Empty Username Field Required Validation Error Pass
Admin Role Mismatch Unauthorized Permission Rejected Pass

User Management Verification

Standard operations such as creation, modification, deletion, and retrieval require strict validation. Null checks prevent incomplete records, while uniqueness constraints avoid duplicate entries. Deletion actions confirm intent via pop-up prompts.

Input Scenario Expected Outcome Actual Outcome Analysis
Complete New Record Created Successfully Listed in Database Pass
Update Existing Record Information Updated Profile Changed Correctly Pass
Delete Selection Removal Confirmation Record Purged After Confirm Pass
Duplicate Entry Attempt Creation Blocked "Exists" Warning Triggered Pass
Missing Name Field Null Check "Required" Prompt Displayed Pass

Core Implementation Details

Authentication Controller

The folloiwng snippet demonstrates the endpoint for handling secure sign-ins. It generates session tokens upon successful verification.

@PostMapping("/auth/sign-in")
public ApiResponse authenticate(AuthRequest req, HttpServletRequest request) {
    // Query account using Lambda Query Wrapper
    AccountEntity account = accountMapper.selectOne(
        new LambdaQueryWrapper<AccountEntity>()
            .eq(AccountEntity::getUsername, req.getUsername())
    );

    if (account == null || !passwordEncoder.matches(req.getPassword(), account.getPassword())) {
        return ApiResponse.error(401, "Invalid credentials");
    }

    // Generate unique JWT-like token
    String generatedToken = tokenService.createSession(account.getId(), account.getUsername(), "users", account.getRole());
    return ApiResponse.ok(Map.of("token", generatedToken));
}

Token Generation Service

This service manages session lifecycle, including expiration and storage updates.

@Service
public class SessionServiceImpl implements SessionService {

    @Override
    public String createSession(Long userId, String username, String tableName, String role) {
        String key = generateUniqueKey();
        Calendar expireTime = Calendar.getInstance();
        expireTime.add(Calendar.HOUR_OF_DAY, 2); 

        // Check existing session first
        SessionRecord record = sessionMapper.selectOne(
            new LambdaQueryWrapper<SessionRecord>()
                .eq(SessionRecord::getUserid, userId)
        );

        if (record != null) {
            record.setToken(key);
            record.setExpiration(expireTime.getTime());
            sessionMapper.updateById(record);
        } else {
            SessionRecord newRecord = new SessionRecord(userId, username, tableName, role, key, expireTime.getTime());
            sessionMapper.insert(newRecord);
        }
        return key;
    }

    private String generateUniqueKey() {
        return UUID.randomUUID().toString().replace("-", "").substring(0, 32);
    }
}

Authorization Interceptor

Middleware handles request validation and CORS configuration to support cross-origin frontend requests.

@Component
public class AuthInterceptor implements HandlerInterceptor {
    public static final String TOKEN_HEADER = "Authorization";

    @Autowired
    private TokenService tokenValidator;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        
        // Configure CORS Headers
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE");
        response.setHeader("Access-Control-Allow-Headers", "*" );
        
        // Handle Preflight Requests
        if (RequestMethod.OPTIONS.name().equals(request.getMethod())) {
            return true;
        }

        // Skip auth for public endpoints
        RequestMappingInfo mapping = ((RequestMappingHandlerAdapter) handler).getHandlerMappings();
        // Logic to check IgnoreAuth annotation would go here

        String inputToken = request.getHeader(TOKEN_HEADER);
        if (StringUtils.isBlank(inputToken)) {
            response.setContentType("application/json;charset=UTF-8");
            PrintWriter out = response.getWriter();
            out.print(JSONObject.toJSONString(ApiResponse.error(401, "Login required")));
            return false;
        }

        SessionRecord record = tokenValidator.validateToken(inputToken);
        if (record == null) {
             response.setContentType("application/json;charset=UTF-8");
            PrintWriter out = response.getWriter();
            out.print(JSONObject.toJSONString(ApiResponse.error(401, "Invalid Session")));
            return false;
        }
        
        request.getSession().setAttribute("userId", record.getUserid());
        request.getSession().setAttribute("role", record.getRole());
        
        return true;
    }
}

Database Schema Definition

Persistence relies on a relational structure optimized for transactional integrity. The example below defines the session storage table used for authentication tracking.

CREATE TABLE `session_tokens` (
  `tid` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'Primary Key',
  `user_identifier` bigint(20) NOT NULL COMMENT 'User ID Reference',
  `account_name` varchar(100) NOT NULL COMMENT 'Account Username',
  `target_table` varchar(100) DEFAULT NULL COMMENT 'Associated Table Name',
  `permission_level` varchar(100) DEFAULT NULL COMMENT 'User Role',
  `access_credential` varchar(200) NOT NULL COMMENT 'Session Token Hash',
  `creation_timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'Generated Time',
  `expiry_timestamp` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT 'Session Expiry',
  PRIMARY KEY (`tid`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC COMMENT='Active Sessions';

-- Sample Records
INSERT INTO `session_tokens` VALUES ('1', '101', 'lab_admin', 'users', 'administrator', 'a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6', '2024-01-15 10:00:00', '2024-01-15 12:00:00');
INSERT INTO `session_tokens` VALUES ('2', '102', 'student_01', 'equipment_logs', 'student', 'q7w8e9r0t1y2u3i4o5p6a7s8d9f0g1h2', '2024-01-16 09:30:00', '2024-01-16 11:30:00');

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.