Technology Stack Architecture
Backend Framework: Spring Boot
Spring Boot serves as the core backend framework, leveraging its embedded servlet containers (Tomcat, Jetty) to eliminate the need for external server deployment. Its convention-over-configuration paradigm significantly accelerates development velocity. Through auto-configuration, the framework manages dependency injection and bean lifecycle, allowing developers to focus on business logic rather than infrastructure boilerplate. Furthermore, it integrates seamlessly with Spring Data, Security, and Cloud ecosystems, providing a robust foundation for scalable application development.
Frontend Framework: Vue.js
Vue.js is utilized for building interactive user interfaces. The framework employs a reactive data binding system and a component-based architecture. By implementing a Virtual DOM, Vue ensures efficient rendering and state management. When the underlying data model changes, the view updates automatically, minimizing manual DOM manipulation. This approach enhances code maintainability and facilitates the development of Single Page Applications (SPAs) with a clear separation of concerns.
Persistence Layer: MyBatis-Plus
MyBatis-Plus acts as an enhancement to the standard MyBatis framework. It simplifies database interactions by providing a generic CRUD interface, significantly reducing the volume of SQL boilerplate code. Features such as code generation, pagination plugins, and dynamic SQL construction streamline data access layer development. Supporting multiple database dialects, it allows for efficient object-relational mapping and complex query execution without sacrificing performance.
System Testing Strategy
System testing is integral to ensuring software quality and reliability. The testing phase employs Black-Box testing techniques to validate functional requirements against actual system behavior. The primary objective is to identify logical defects, verify input constraints, and ensure the system meets user specifications under various operational scenarios.
Authentication Module Testing
The authentication module is tested to verify login security, including credential validation and role-based access control. Test cases cover valid inputs, incorrect passwords, and empty field handling.
| Test Input | Expected Result | Actual Result | Status |
|---|---|---|---|
| User: admin, Pass: valid_pwd, Captcha: correct | Login successful, redirect to dashboard | Redirected successfully | Pass |
| User: admin, Pass: wrong_pwd, Captcha: correct | System prompts 'Invalid credentials' | Error message displayed | Pass |
| User: admin, Pass: valid_pwd, Captcha: wrong | System prompts 'Captcha mismatch' | Error message displayed | Pass |
| User: [Empty], Pass: valid_pwd | System prompts 'Username required' | Validation error shown | Pass |
User Management Module Testing
User management functions, including creation, modification, and deletion, are verified to ensure data integrity and UI responsiveness.
| Operation | Test Scenario | Expected Result | Status |
|---|---|---|---|
| Add User | Submit valid user data | User appears in list | Pass |
| Edit User | Modify existing details | Updates reflected immediately | Pass |
| Delete User | Confirm deletion prompt | User removed from database | Pass |
| Duplicate User | Register existing username | System rejects submission | Pass |
Core Implementation Details
The following code snippets illustrate the implementation of the login logic, token generation, and request interception for authorization.
Login Controller
@RestController
@RequestMapping("/api/auth")
public class AuthController {
@Autowired
private SysUserService userService;
@Autowired
private TokenService tokenService;
@PostMapping("/login")
public Result<Map<String, Object>> login(@RequestBody LoginDTO dto) {
SysUser user = userService.getOne(
new LambdaQueryWrapper<SysUser>().eq(SysUser::getUsername, dto.getUsername())
);
if (user == null || !user.getPassword().equals(dto.getPassword())) {
return Result.error("Invalid username or password");
}
String accessToken = tokenService.generateToken(user.getId(), user.getUsername(), "sys_user", user.getRole());
Map<String, Object> data = new HashMap<>();
data.put("token", accessToken);
return Result.ok(data);
}
}Token Generation Service
@Service
public class TokenServiceImpl implements TokenService {
@Autowired
private UserTokenMapper tokenMapper;
@Override
public String generateToken(Long userId, String username, String table, String role) {
UserToken existingToken = tokenMapper.selectOne(
new LambdaQueryWrapper<UserToken>().eq(UserToken::getUserId, userId)
);
String newToken = UUID.randomUUID().toString().replace("-", "");
Date expiryTime = DateUtil.offsetHour(new Date(), 1);
if (existingToken != null) {
existingToken.setToken(newToken);
existingToken.setExpiryTime(expiryTime);
tokenMapper.updateById(existingToken);
} else {
UserToken tokenEntity = new UserToken();
tokenEntity.setUserId(userId);
tokenEntity.setUsername(username);
tokenEntity.setToken(newToken);
tokenEntity.setExpiryTime(expiryTime);
tokenMapper.insert(tokenEntity);
}
return newToken;
}
}Authorization Interceptor
@Component
public class AuthInterceptor implements HandlerInterceptor {
public static final String AUTH_HEADER = "Authorization";
@Autowired
private TokenService tokenService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// Handle CORS preflight requests
if (HttpMethod.OPTIONS.name().equals(request.getMethod())) {
response.setStatus(HttpStatus.OK.value());
return true;
}
// Check for @IgnoreAuth annotation
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
if (handlerMethod.getMethodAnnotation(IgnoreAuth.class) != null) {
return true;
}
}
String tokenStr = request.getHeader(AUTH_HEADER);
if (StrUtil.isBlank(tokenStr)) {
throw new AuthException("Missing authentication token");
}
UserToken tokenEntity = tokenService.getTokenEntity(tokenStr);
if (tokenEntity == null) {
throw new AuthException("Invalid or expired token");
}
// Store user info in session context
request.getSession().setAttribute("userId", tokenEntity.getUserId());
request.getSession().setAttribute("role", tokenEntity.getRole());
return true;
}
}Database Schema Design
The authentication system relies on a token table to manage user sessions. The schema definition is as follows:
CREATE TABLE `sys_user_token` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'Primary Key',
`user_id` bigint(20) NOT NULL COMMENT 'Associated User ID',
`username` varchar(100) NOT NULL COMMENT 'Username',
`table_name` varchar(100) DEFAULT NULL COMMENT 'Associated Table',
`role` varchar(100) DEFAULT NULL COMMENT 'User Role',
`token` varchar(200) NOT NULL COMMENT 'Access Token',
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'Creation Time',
`expiry_time` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT 'Expiration Time',
PRIMARY KEY (`id`),
UNIQUE KEY `idx_token` (`token`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Session Token Management';