A production-grade, full-stack secure file sharing platform built with Java Spring Boot and React. Designed with Fintech-level security standards including JWT authentication, token blacklisting, virus scanning, audit logging, and expiring share links.
Swagger UI: https://sftg-backend.onrender.com/swagger-ui/index.html
- Features
- Tech Stack
- Architecture
- Security Design
- API Endpoints
- Database Schema
- Getting Started
- Environment Variables
- Docker Deployment
- Cloud Deployment
- Project Structure
- Key Engineering Decisions
- Author
- ๐ JWT Authentication with DB-backed token blacklisting (survives server restarts)
- ๐ง Email OTP Verification via Brevo API before account activation
- ๐ BCrypt Password Hashing with secure salting
- ๐ก๏ธ Rate Limiting on public endpoints using Bucket4j token bucket algorithm
- ๐ฆ Virus Scanning โ ClamAV (local Docker) / VirusTotal API (cloud)
- ๐ MIME Type Detection via Apache Tika (content-based, not extension-based)
- ๐ซ Executable File Blocking โ rejects .exe, .sh, .bat uploads
- ๐ก๏ธ Path Traversal Protection on all file operations
- ๐ Secure File Upload with size validation (max 100MB)
- โณ Auto File Expiry โ files automatically deleted after 1 hour
- ๐๏ธ Scheduled Cleanup โ removes expired files and all associated data every 5 minutes
- โ๏ธ Cloud Storage via Cloudinary โ stateless, no local disk dependency
- ๐ Expiring Share Links โ UUID tokens, expire in 1 hour
- ๐ Download Limits โ maximum 5 downloads per share link
- ๐ Download Audit โ tracks who downloaded, when, from which IP
- ๐ฑ QR Code Generation for share links
- ๐ Fintech-Style Audit Log โ every action logged with IP address and User-Agent
- ๐ Global Statistics โ total users, uploads, downloads
- ๐ค User Profile Dashboard โ complete activity history
- ๐ Keep-Alive Scheduler โ prevents cloud cold starts
| Technology | Version | Purpose |
|---|---|---|
| Java | 21 | Core language |
| Spring Boot | 3.4.11 | Application framework |
| Spring Security | 6.4.x | Authentication & authorization |
| JJWT | 0.11.5 | JWT token generation & validation |
| Hibernate / JPA | 6.6.x | Database ORM |
| MySQL | 8.0 | Primary database |
| Bucket4j | 8.15.0 | Rate limiting |
| Apache Tika | 2.8.0 | MIME type detection |
| Cloudinary SDK | 1.37.0 | Cloud file storage |
| OkHttp | 4.9.3 | Brevo email API client |
| SpringDoc OpenAPI | 2.8.5 | Swagger documentation |
| Maven | 3.x | Build & dependency management |
| Technology | Version | Purpose |
|---|---|---|
| React | 19 | UI framework |
| Vite | 8.x | Build tool |
| Material UI (MUI) | 7.x | Component library |
| React Router DOM | 7.x | Client-side routing |
| Axios | 1.x | HTTP client with interceptors |
| Tailwind CSS | 4.x | Utility CSS |
| qrcode.react | 4.x | QR code generation |
| Service | Purpose |
|---|---|
| Render | Backend hosting (Docker) |
| Railway | Frontend hosting (Docker) + MySQL database + ClamAV |
| Cloudinary | Cloud file storage |
| Docker Hub | Container image registry |
| UptimeRobot | Keep-alive monitoring |
| Brevo | Transactional email (OTP) |
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ CLIENT (Browser) โ
โ React + MUI + Vite (Railway) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ HTTPS
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ SPRING BOOT API โ
โ (Render - Docker) โ
โ โ
โ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโ โ
โ โ Security โ โ Controllers โ โ Schedulers โ โ
โ โ JWT Filter โ โ Auth/File/ โ โ Cleanup โ โ
โ โ Rate Limit โ โ Share/Stats โ โ KeepAlive โ โ
โ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโ โ
โ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Services โ โ
โ โ FileService โ AuthService โ FileShareService โ โ
โ โ VirusTotal โ Cloudinary โ TokenBlacklist โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโ
โผ โผ โผ
โโโโโโโโโโโโโโโ โโโโโโโโโโโโโ โโโโโโโโโโโโโโโโ
โ MySQL DB โ โCloudinary โ โ VirusTotal โ
โ (Railway) โ โ Storage โ โ API โ
โโโโโโโโโโโโโโโ โโโโโโโโโโโโโ โโโโโโโโโโโโโโโโ
Controller Layer โ Handles HTTP, validates input, returns responses
Service Layer โ Business logic, orchestration
Repository Layer โ Database operations via JPA
Security Layer โ JWT filter, rate limiting, CORS
Register โ OTP Email โ Verify โ Login โ JWT Token โ Authenticated Requests
Most tutorials use in-memory maps for token blacklisting. This project uses database storage because:
In-memory approach problem:
Server restarts โ blacklisted tokens become valid again
Attacker with stolen token just waits for deployment
DB-backed solution:
Token stored in blacklisted_tokens table on logout
Every request: SELECT exists WHERE token = ?
Index on token column โ O(log n) lookup
Hourly scheduler: DELETE WHERE expires_at < NOW()
Tokens remain blacklisted across restarts
1. BCrypt (cost factor 10) โ password hashing
2. Email OTP verification โ prevents fake registrations
3. JWT (HS256, 2hr expiry) โ stateless authentication
4. Token blacklist (DB-backed) โ logout invalidation
5. Rate limiting (Bucket4j) โ 5 req/min on share endpoints
6. MIME type detection (Tika) โ content-based file validation
7. Executable blocking โ rejects dangerous file types
8. Path traversal protection โ filename sanitization
9. Virus scanning (VirusTotal) โ malware detection
10. Ownership verification โ users only access their own files
11. CORS configuration โ locked to specific frontend origin
POST /api/auth/register Register with email + password
POST /api/auth/verify-otp Verify 6-digit OTP
POST /api/auth/login Login, returns JWT token
POST /api/auth/logout Blacklist current token (auth required)
GET /api/account/me Profile + full audit log
POST /api/files/upload Upload file (multipart/form-data)
GET /api/files/download/{filename} Download own file
GET /api/files/my-files List all uploaded files with details
POST /api/share/generate Generate share link (JWT required)
GET /api/share/download/{token} Download via share link (public)
GET /api/stats/global Total users, uploads, downloads
GET /api/stats/health Health check endpoint
GET /swagger-ui/index.html Interactive API documentation
GET /v3/api-docs OpenAPI JSON specification
users โ credentials, OTP, timestamps
user_roles โ role assignments (ROLE_USER, ROLE_ADMIN)
files โ metadata, Cloudinary URL, expiry
shared_links โ tokens, download limits, expiry
download_logs โ who downloaded, when, IP address
user_activity_logs โ full audit trail (LOGIN, LOGOUT, UPLOAD, DOWNLOAD)
blacklisted_tokens โ DB-backed JWT blacklist with cleanupJava 21+
Maven 3.x
Node.js 20+
Docker + Docker Compose
1. Clone the repository
git clone https://github.com/anupkumar9705/SFTG.git
cd SFTG2. Start dependencies (MySQL + ClamAV)
docker-compose up mysql_db clamav_service3. Configure backend
Create src/main/resources/application-dev.properties:
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/SFTG?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC
spring.datasource.username=your_mysql_user
spring.datasource.password=your_mysql_password
jwt.secret=your_64_byte_base64_secret
jwt.expirationMs=7200000
clamav.host=127.0.0.1
clamav.port=3310
spring.mail.host=smtp-relay.brevo.com
spring.mail.port=587
spring.mail.username=your_brevo_smtp_user
spring.mail.password=your_brevo_smtp_password
brevo.api.key=your_brevo_api_key
cloudinary.cloud_name=your_cloud_name
cloudinary.api_key=your_cloudinary_api_key
cloudinary.api_secret=your_cloudinary_api_secret
virustotal.api.key=your_virustotal_api_key
cors.allowed-origin=http://localhost:51734. Start backend
cd app/backendJava/SFTG-backend
mvn spring-boot:run5. Start frontend
cd app/backendJava/SFTG-frontend
npm install
npm run devAccess:
- Frontend: http://localhost:5173
- Backend API: http://localhost:8000
- Swagger UI: http://localhost:8000/swagger-ui/index.html
docker-compose up --build- Frontend: http://localhost
- Backend: http://localhost:8000
| Variable | Description | Example |
|---|---|---|
SPRING_PROFILES_ACTIVE |
Active profile | dev |
DB_URL |
JDBC MySQL URL | jdbc:mysql://host:port/SFTG?... |
DB_USERNAME |
Database username | root |
DB_PASSWORD |
Database password | strongpassword |
JWT_SECRET |
64-byte base64 secret | openssl rand -base64 64 |
JWT_EXPIRATION_MS |
Token expiry in ms | 7200000 |
CLAMAV_HOST |
ClamAV hostname | 127.0.0.1 |
CLAMAV_PORT |
ClamAV port | 3310 |
MAIL_HOST |
SMTP host | smtp-relay.brevo.com |
MAIL_PORT |
SMTP port | 587 |
MAIL_USERNAME |
SMTP username | brevo_user |
MAIL_PASSWORD |
SMTP password | brevo_smtp_key |
BREVO_API_KEY |
Brevo REST API key | xkeysib-... |
CLOUDINARY_CLOUD_NAME |
Cloudinary cloud name | myapp |
CLOUDINARY_API_KEY |
Cloudinary API key | 123456789 |
CLOUDINARY_API_SECRET |
Cloudinary secret | abcdef... |
VIRUSTOTAL_API_KEY |
VirusTotal API key | abc123... |
CORS_ALLOWED_ORIGIN |
Frontend URL | https://myapp.railway.app |
APP_BASE_URL |
Backend URL (keep-alive) | https://myapi.onrender.com |
| Variable | Description | Example |
|---|---|---|
VITE_API_URL |
Backend API base URL | https://sftg-backend.onrender.com/api |
# Backend
docker build -t yourusername/sftg-backend:latest \
./app/backendJava/SFTG-backend
# Frontend
docker build \
--build-arg VITE_API_URL=https://your-backend.onrender.com/api \
-t yourusername/sftg-frontend:latest \
./app/backendJava/SFTG-frontenddocker push yourusername/sftg-backend:latest
docker push yourusername/sftg-frontend:latestdocker-compose up --build| Service | Platform | Notes |
|---|---|---|
| Backend | Render (free) | Cold start ~90s, keep-alive via UptimeRobot |
| Frontend | Railway (free) | Always warm, instant load |
| Database | Railway MySQL | 1GB free monthly |
| File Storage | Cloudinary | 25GB free tier |
| Brevo | 300 emails/day free | |
| Virus Scan | VirusTotal | 500 requests/day free |
| Monitoring | UptimeRobot | 5-minute pings |
- Create Web Service โ Deploy existing Docker image
- Image:
docker.io/yourusername/sftg-backend:latest - Port:
8000 - Add all environment variables
- Deploy
- Add Service โ Docker Image
- Image:
docker.io/yourusername/sftg-frontend:latest - Networking โ Generate Domain
- Port:
80
SFTG/
โโโ app/
โ โโโ backendJava/
โ โ โโโ SFTG-backend/
โ โ โ โโโ src/main/java/com/securetransfer/SFTG/
โ โ โ โ โโโ config/ # CORS, Cloudinary, AppConfig
โ โ โ โ โโโ controller/ # Auth, File, Share, Account, Stats
โ โ โ โ โโโ dto/ # Request/Response objects
โ โ โ โ โโโ exception/ # Custom exceptions + global handler
โ โ โ โ โโโ model/ # JPA entities
โ โ โ โ โโโ repository/ # JPA repositories
โ โ โ โ โโโ security/ # JWT filter, SecurityConfig, rate limiting
โ โ โ โ โโโ service/ # Business logic layer
โ โ โ โโโ Dockerfile
โ โ โ โโโ pom.xml
โ โ โ
โ โ โโโ SFTG-frontend/
โ โ โโโ src/
โ โ โ โโโ api/ # Axios instance with interceptors
โ โ โ โโโ components/ # Navbar, Layout, ProtectedRoute
โ โ โ โโโ pages/ # Login, Register, Dashboard, Upload, etc.
โ โ โโโ public/
โ โ โ โโโ _redirects # Netlify/Railway React Router support
โ โ โโโ nginx.conf
โ โ โโโ Dockerfile
โ โ
โโโ docker-compose.yml
โโโ README.md
In-memory blacklists reset on server restart, making logged-out tokens valid again. Using MySQL with an index on the token column gives fast lookups with restart-proof persistence. A scheduled job cleans expired tokens hourly to keep the table small.
ClamAV and VirusTotal are treated as non-blocking services. ConnectException and timeouts allow the upload to proceed with a warning log rather than failing the user request. Only a confirmed MALICIOUS result blocks the upload. This matches how production systems handle optional security services that may be temporarily unavailable.
Deleting a file requires deleting in FK-constrained order: download_logs โ shared_links โ files. The scheduler handles this explicitly in a @Transactional method rather than relying on JPA cascade, giving explicit control over the deletion sequence.
No JPA entity is ever returned directly from a controller. DTOs decouple the persistence layer from the API layer, prevent accidental data leaks, and allow the database schema to evolve without breaking API consumers.
File extensions are trivially spoofed (malware.exe renamed to document.pdf). Apache Tika reads the actual file bytes to determine the real content type, making extension-based attacks ineffective.
- Refresh token strategy (15-min access + 7-day refresh)
- Unit and integration tests (JUnit 5 + Mockito)
- API versioning (
/api/v1/) - File encryption at rest
- Redis for token blacklist (faster than MySQL)
- CI/CD pipeline (GitHub Actions)
- Admin dashboard for user management
- Configurable file expiry per upload
Anup Kumar Sharma
Computer Science Engineering Graduate, 2025
Backend Developer โ Security-Focused Engineering
- GitHub: @anupkumar9705
- Docker Hub: anupkumar9705
This project is developed for educational and portfolio demonstration purposes.
Spring Boot ยท React ยท Docker ยท JWT ยท ClamAV ยท Cloudinary ยท VirusTotal