Technical Architecture
Backend Framework: SpringBoot
SpringBoot simplifies server setup with embedded Tomcat, Jetty, and Undertow servers. Its auto-configuration feature reduces manual setup by analyzing project dependencies. The framework offers out-of-the-box modules like Spring Data and Spring Security for rapid appplication development.
Frontend Framework: Vue.js
Vue.js utilizes a virtual DOM for efficient UI updates. Its reactive data binding automatically synchronizes UI with state changes, enabling developers to focus on business logic rather than DOM manipulation.
Persistence Layer: MyBatisPlus
This MyBatis extension simplifies database operations through code generation and annotation-based configuration. It supports pagination, dynamic queries, and optimistic locking across multiple database systems.
System Validation
Testing Objectives
Comprehensive testing ensures functional correctness and reliability. Black-box testing validates user workflows while verifying requirement compliance.
Functional Validation
Authentication Testing
| Input | Expected | Actual |
|---|---|---|
| Valid credentials | Access granted | Access granted |
| Invalid password | Access denied | Access denied |
| Incorrect CAPTCHA | Access denied | Access denied |
User Management Testing
| Scenario | Expected | Actual |
|---|---|---|
| Create valid user | Creation success | Creation success |
| Duplicate username | Creation failure | Creation failure |
| Missing username | Validation error | Validation error |
Testing Conclusion
Black-box testing confirmed functional requirements with satisfactory defect resolution. The system meets design specifications for user experience and workflow efficiency.
Implementation Samples
Authentication Endpoint
@PublicEndpoint
@PostMapping("/authenticate")
public Response login(@RequestParam String username,
@RequestParam String password) {
User user = userRepo.findByUsername(username);
if (user == null || !passwordEncoder.matches(password, user.getPassword())) {
return Response.error("Invalid credentials");
}
String authToken = tokenService.createToken(user.getId(), user.getRole());
return Response.ok().withData("token", authToken);
}
Token Generation Service
public String createToken(Long userId, String role) {
Token token = tokenRepo.findByUserId(userId);
String newToken = generateRandomString(32);
Date expiry = Date.from(Instant.now().plus(1, ChronoUnit.HOURS));
if (token != null) {
token.setValue(newToken);
token.setExpiry(expiry);
tokenRepo.save(token);
} else {
tokenRepo.save(new Token(userId, role, newToken, expiry));
}
return newToken;
}
Security Interceptor
@Component
public class AuthInterceptor implements HandlerInterceptor {
@Autowired
private TokenService tokenService;
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) {
// CORS configuration
response.setHeader("Access-Control-Allow-Origin", "*");
if (((HandlerMethod) handler).getMethodAnnotation(PublicEndpoint.class) != null) {
return true;
}
String tokenHeader = request.getHeader("Authorization");
if (StringUtils.isBlank(tokenHeader)) {
sendError(response, 401, "Authentication required");
return false;
}
Token token = tokenService.validateToken(tokenHeader);
if (token != null) {
request.getSession().setAttribute("userContext",
new UserContext(token.getUserId(), token.getRole()));
return true;
}
sendError(response, 403, "Invalid session");
return false;
}
private void sendError(HttpServletResponse response, int code, String message) {
// Error response implementation
}
}
Database Schema
Token Table Structure
CREATE TABLE auth_tokens (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
user_id BIGINT NOT NULL,
user_role VARCHAR(50) NOT NULL,
token_value VARCHAR(200) NOT NULL UNIQUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
expires_at TIMESTAMP NOT NULL
);
-- Sample entry
INSERT INTO auth_tokens (user_id, user_role, token_value, expires_at)
VALUES (101, 'ADMIN', '7s82kj3hda98q3', '2023-12-31 23:59:59');