Technical Stack Overview
Backend Framework: SpringBoot
SpringBoot provides embedded servers such as Tomcat, Jetty, and Undertow, eliminating the need for separate installations. Its auto-configuration feature automatically configures the application based on dependencies present in the project. This significantly simplifies the setup process by removing the need for manual configuration of each component. The framework offers numerous built-in features and extensions including Spring Data, Spring Security, and Spring Cloud, enabling developers to build applications more efficiently. These tools facilitate easier integration with other technologies and provide a solid foundation for scalable enterprise solutions.
Frontend Framework: Vue.js
Vue.js utilizes a virtual DOM as its core technology, which is an in-memory data structure enabling efficient DOM manipulation. The framework implements reactive data binding, virtual DOM, and component-based architecture, providing developers with a flexible and maintainable development approach. When data changes, the UI automatically updates, allowing developers to focus on data processing rather than manual UI updates. This approach results in a development experience that is both streamlined and efficient.
Persistence Layer Framework: MyBatis-Plus
MyBatis-Plus is an enhancement tool built upon the MyBatis framework, designed to simplify database operations. As an open-source Java framework, it supports multiple databases icnluding MySQL, Oracle, SQL Server, and PostgreSQL. The framework provides extensive APIs and annotations that enable ORM operations through minimal configuration, significantly reducing manual SQL coding. Additionally, MyBatis-Plus includes a code generator that automatically creates entity classes, Mapper interfaces, and XML mapping files, streamlining the development process. It also supports practical features such as pagination queries, dynamic queries, optimistic locking, and performance analysis, allowing developers to perform efficient data operations and quickly develop high-quality data access layers.
System Testing Methodology
Testing Objectives
System testing serves as a critical quality assurance phase in the development lifecycle. Its primary purpose is to identify and resolve system issues from multiple perspectives. Through comprehensive testing, defects are discovered and rectified to ensure the system meets quality standards. The testing process verifies that the system fulfills customer requirements while identifying areas for improvement. Effective testing evaluates system functionality, completeness, and logical flow. A thorough testing process significantly enhances system quality and user experience. The goal is to validate that the system adheres to the requirements specification and identifies any discrepancies or conflicts.
Functional Testing Approach
The system underwent black-box testing focused on functional modules, using methods such as interface interactions, boundary value testing, and validation of required versus optional fields. Test cases were developed and executed to validate system behavior.
Login Functionality Test Cases
| Input Data | Expected Result | Actual Result | Analysis |
|---|---|---|---|
| Username: admin, Password: 123456, Captcha: Correct | System login successful | User successfully logged in | Matches expected result |
| Username: admin, Password: wrongpass, Captcha: Correct | Password error message | Displays "Incorrect password" message | Matches expected result |
| Username: admin, Password: 123456, Captcha: Incorrect | Captcha error message | Displays "Invalid captcha" message | Matches expected result |
| Username: empty, Password: 123456, Captcha: Correct | Username required message | Displays "Username is required" message | Matches expected result |
| Username: admin, Password: empty, Captcha: Correct | Password required message | Displays "Password is required" message | Matches expected result |
User Management Functionality Test Cases
| Input Data | Expected Result | Actual Result | Analysis |
|---|---|---|---|
| Complete user information | User added successfully, appears in list | User appears in the user list | Matches expected result |
| Modified user information | Update successful, changes reflected | User informasion updated successfully | Matches expected result |
| Selected user for deletion | Confirmation dialog, user removed after confirmation | System prompts for confirmation, user removed | Matches expected result |
| User creation without username | Username required validation message | Displays "Username cannot be blank" message | Matches expected result |
| Creation with existing username | Duplicate username error message | Displays "Username already exists" message | Matches expected result |
Testing Conclusion
The system was primarily tested using black-box testing methodology, with test cases developed to simulate user interactions and validate functional flows. This comprehensive testing approach ensures system correctness. System testing is essential for improving system usability and functionality. The testing process aimed to verify that all functional modules met the initial design requirements and that their underlying logic was sound. Given the system's focus on user-friendly operation, complex logic processing was intentionally minimized. All testing scenarios were aligned with user requirements, with issues addressed from a user perspective. The final test results confirm that the implemented system meets both functional and performance design specifications.
Implementation Code Examples
Authentication Controller
@SkipAuth
@PostMapping("/authenticate")
public Response authenticate(String username, String password, String captcha, HttpServletRequest request) {
UserEntity user = userService.queryOne(new QueryWrapper<userentity>().eq("username", username));
if(user == null || !user.getPassword().equals(password)) {
return Response.error("Invalid username or password");
}
String authToken = tokenService.generateToken(user.getId(), username, "users", user.getRole());
return Response.success().put("authToken", authToken);
}</userentity>
Token Service Implementation
@Override
public String generateToken(Long userId, String username, String tableName, String role) {
TokenEntity existingToken = this.selectOne(new QueryWrapper<tokenentity>()
.eq("userId", userId)
.eq("role", role));
String newToken = SecurityUtils.generateRandomString(32);
Calendar calendar = Calendar.getInstance();
calendar.setTime(new Date());
calendar.add(Calendar.HOUR_OF_DAY, 1);
if(existingToken != null) {
existingToken.setToken(newToken);
existingToken.setExpirationTime(calendar.getTime());
this.updateById(existingToken);
} else {
this.insert(new TokenEntity(userId, username, tableName, role, newToken, calendar.getTime()));
}
return newToken;
}</tokenentity>
Security Interceptor
@Component
public class SecurityInterceptor 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 {
// Enable cross-origin requests
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");
response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
// Handle preflight OPTIONS requests
if(request.getMethod().equals(HttpMethod.OPTIONS.name())) {
response.setStatus(HttpStatus.OK.value());
return false;
}
SkipAuth annotation;
if(handler instanceof HandlerMethod) {
annotation = ((HandlerMethod) handler).getMethodAnnotation(SkipAuth.class);
} else {
return true;
}
// Retrieve token from header
String token = request.getHeader(AUTH_TOKEN_HEADER);
// Allow access for methods without authentication requirement
if(annotation != null) {
return true;
}
TokenEntity tokenEntity = null;
if(StringUtils.isNotBlank(token)) {
tokenEntity = tokenService.validateToken(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(JsonUtils.toJsonString(Response.error(401, "Authentication required")));
} finally {
if(writer != null) {
writer.close();
}
}
return false;
}
}
Database Schema
<code-- authentication_tokens="" auto_increment="" bigint="" btree="" charset="utf8" comment="" create="" current_timestamp="" default="" drop="" engine="InnoDB" exists="" for="" identifier="" if="" insert="" into="" key="" name="" not="" null="" primary="" records="" role="" row_format="COMPACT" sample="" structure="" table="" timestamp="" token="" using="" values="" varchar=""></code-->