Technical Stack Overview
The backend leverages Spring Boot's auto-configuration capabilities, eliminating manual server setup through embedded Tomcat integration. This framework provides production-ready features including dependency injection, transaction management, and seamless integration with Spring Security and Spring Data ecosystems. The frontend utilizes Vue.js with its reactive data binding and virtual DOM implementation, enabling efficient UI updates without manual DOM manipulation. For data persistence, MyBatis-Plus extends traditional MyBatis functionality, offering automatic CRUD generation, pagination support, and dynamic query builders that reduce SQL boilerplate code.
Authentication Implementation
The security layer implements token-based authentication with stateless session management. The login endpoint validates credentials against hashed passwords stored in the member repository:
@RestController
@RequestMapping("/api/auth")
public class AuthenticationController {
@Autowired
private MemberRepository memberRepo;
@Autowired
private SessionTokenService sessionService;
@PublicAccess
@PostMapping("/signin")
public ResponseEntity<AuthResponse> authenticate(
@RequestParam String identifier,
@RequestParam String credential,
HttpServletRequest request) {
Member account = memberRepo.findByIdentifier(identifier);
if (account == null || !account.verifyCredential(credential)) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
.body(new AuthResponse("Authentication failed"));
}
String sessionToken = sessionService.createSession(
account.getMemberId(),
identifier,
account.getMemberType(),
account.getAccessLevel()
);
return ResponseEntity.ok(new AuthResponse(sessionToken));
}
}
The token generation service manages session lifecycle with configurable expiration:
@Service
public class SessionTokenService {
@Autowired
private TokenRepository tokenRepo;
private static final Duration SESSION_DURATION = Duration.ofHours(2);
public String createSession(Long memberId, String loginName, String entityType, String permissionLevel) {
Optional<TokenRecord> existing = tokenRepo.findByMemberIdAndRole(memberId, permissionLevel);
String jwtToken = generateSecureToken();
Instant expiration = Instant.now().plus(SESSION_DURATION);
TokenRecord record = existing.orElse(new TokenRecord());
record.setMemberId(memberId);
record.setLoginName(loginName);
record.setEntityTable(entityType);
record.setPermissionLevel(permissionLevel);
record.setTokenValue(jwtToken);
record.setExpiryTimestamp(Date.from(expiration));
tokenRepo.save(record);
return jwtToken;
}
private String generateSecureToken() {
byte[] bytes = new byte[32];
new SecureRandom().nextBytes(bytes);
return Base64.getUrlEncoder().withoutPadding().encodeToString(bytes);
}
}
Request authorization relies on an interceptor that validates tokens against the session store:
@Component
public class SecurityInterceptor implements HandlerInterceptor {
private static final String AUTH_HEADER = "Authorization";
@Autowired
private SessionTokenService tokenService;
@Override
public boolean preHandle(HttpServletRequest req, HttpServletResponse res, Object handler) throws Exception {
configureCorsHeaders(res, req);
if (isPreflightRequest(req)) {
res.setStatus(HttpServletResponse.SC_OK);
return false;
}
if (hasPublicAccess(handler)) {
return true;
}
String bearerToken = req.getHeader(AUTH_HEADER);
if (!StringUtils.hasText(bearerToken)) {
sendErrorResponse(res, HttpStatus.UNAUTHORIZED, "Missing authentication token");
return false;
}
String token = bearerToken.replace("Bearer ", "");
TokenRecord session = tokenService.validateToken(token);
if (session == null || session.isExpired()) {
sendErrorResponse(res, HttpStatus.UNAUTHORIZED, "Invalid or expired session");
return false;
}
req.setAttribute("currentUserId", session.getMemberId());
req.setAttribute("currentRole", session.getPermissionLevel());
req.setAttribute("userTable", session.getEntityTable());
req.setAttribute("currentUsername", session.getLoginName());
return true;
}
private void configureCorsHeaders(HttpServletResponse res, HttpServletRequest req) {
res.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Requested-With");
res.setHeader("Access-Control-Allow-Origin", req.getHeader("Origin"));
res.setHeader("Access-Control-Allow-Credentials", "true");
res.setHeader("Access-Control-Max-Age", "3600");
}
private boolean isPreflightRequest(HttpServletRequest req) {
return "OPTIONS".equalsIgnoreCase(req.getMethod());
}
private boolean hasPublicAccess(Object handler) {
if (handler instanceof HandlerMethod) {
HandlerMethod method = (HandlerMethod) handler;
return method.hasMethodAnnotation(PublicAccess.class);
}
return false;
}
private void sendErrorResponse(HttpServletResponse res, HttpStatus status, String message) throws IOException {
res.setStatus(status.value());
res.setContentType("application/json;charset=UTF-8");
res.getWriter().write(String.format("{\"error\":\"%s\"}", message));
}
}
Database Schema
The session management requires a persistent token store with the following structure:
CREATE TABLE `session_tokens` (
`record_id` bigint(20) NOT NULL AUTO_INCREMENT,
`member_id` bigint(20) NOT NULL COMMENT 'User reference ID',
`login_name` varchar(100) NOT NULL COMMENT 'Account identifier',
`entity_table` varchar(100) DEFAULT NULL COMMENT 'User type classification',
`permission_role` varchar(100) DEFAULT NULL COMMENT 'Access control level',
`token_value` varchar(255) NOT NULL COMMENT 'Encrypted session token',
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`expires_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
PRIMARY KEY (`record_id`),
KEY `idx_token_lookup` (`token_value`),
KEY `idx_member_session` (`member_id`, `permission_role`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Active authentication sessions';
Testing Methodology
System validation employs black-box testing techniques to verify functional requirements without examining internal code structure. The authentication module undergoes rigorous boundary value analysis, testing scenarios including valid credential pairs, mismatched passwords, null inputs, and expired session tokens. User management workflows validate CRUD operations through equivalence partitioning, ensuring proper handling of duplicate entries, mandatory field validation, and cascading deletion constraints. All test cases simulate real user interactions through the REST API endpoints, verifying response status codes, payload structures, and database state consistency. CORS configuration and preflight request handling undergo specific validation to insure cross-origin resource sharing functions correctly across different client domains.