Spring Boot
Capstone – Article 10: Securing Expenses to the Logged-In User (JWT Context)
By Utility Zone · 2026-01-27T18:33:02.004595
1. Introduction
So far, expense APIs work — but they are not fully secure yet.
Problem:
- We used a temporary hardcoded
userId - This breaks real-world security rules
In this article, we will:
- Extract logged-in user identity from JWT
- Resolve the user from database
- Ensure expenses are always tied to the authenticated user
This step is critical for data security.
2. Why This Step Matters
Without proper user binding:
- Users could access others’ data
- Authorization becomes meaningless
- Application fails security reviews
A secure backend must:
✔ Trust JWT, not request data
✔ Enforce ownership at service layer
3. Getting Logged-In User from SecurityContext
Spring Security stores authentication details in:
SecurityContextHolder.getContext().getAuthentication()
From our JWT filter, we already set:
- Principal = email
- Authorities = roles
We will now use this email.
4. Creating Security Utility Helper
Create package:
com.example.expensetracker.security
Create class:
package com.example.expensetracker.security;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
public class SecurityUtil {
private SecurityUtil() {}
public static String getCurrentUserEmail() {
Authentication auth =
SecurityContextHolder.getContext().getAuthentication();
return auth != null ? auth.getName() : null;
}
}
This gives us the email from JWT.
5. Resolving User from Email
Update UserRepository (already exists):
Optional<User> findByEmail(String email);
We’ll use this to fetch the logged-in user.
6. Updating ExpenseService
6.1 Remove userId Parameter
Before (❌):
createExpense(Long userId, ExpenseRequest request)
After (✔):
createExpense(ExpenseRequest request)
6.2 Secure Expense Creation
@Service
public class ExpenseService {
private final ExpenseRepository expenseRepository;
private final UserRepository userRepository;
public ExpenseService(ExpenseRepository expenseRepository,
UserRepository userRepository) {
this.expenseRepository = expenseRepository;
this.userRepository = userRepository;
}
public ExpenseResponse createExpense(ExpenseRequest request) {
String email = SecurityUtil.getCurrentUserEmail();
User user = userRepository.findByEmail(email)
.orElseThrow(() -> new UserNotFoundException("User not found"));
Expense expense = new Expense();
expense.setAmount(request.getAmount());
expense.setDescription(request.getDescription());
expense.setCategory(request.getCategory());
expense.setExpenseDate(request.getExpenseDate());
expense.setUser(user);
return mapToResponse(expenseRepository.save(expense));
}
public List<ExpenseResponse> getMyExpenses() {
String email = SecurityUtil.getCurrentUserEmail();
User user = userRepository.findByEmail(email)
.orElseThrow(() -> new UserNotFoundException("User not found"));
return expenseRepository.findByUserId(user.getId())
.stream()
.map(this::mapToResponse)
.toList();
}
}
✔ No hardcoded IDs
✔ JWT identity enforced
✔ Ownership guaranteed
7. Updating ExpenseController
@RestController
@RequestMapping("/expenses")
public class ExpenseController {
private final ExpenseService expenseService;
public ExpenseController(ExpenseService expenseService) {
this.expenseService = expenseService;
}
@PostMapping
public ExpenseResponse createExpense(@Valid @RequestBody ExpenseRequest request) {
return expenseService.createExpense(request);
}
@GetMapping
public List<ExpenseResponse> getMyExpenses() {
return expenseService.getMyExpenses();
}
}
Controller is now completely clean.
8. Testing Secure Ownership
Scenario 1: User A
- Login as User A
- Create expense
- Fetch expenses → sees own data
Scenario 2: User B
- Login as User B
- Fetch expenses → sees only their data
No overlap allowed.
9. What Happens If Token Is Missing?
- No JWT → 401 Unauthorized
- Invalid JWT → 401
- Valid JWT → access granted
Spring Security handles this automatically.
10. Common Mistakes
❌ Passing userId in request
❌ Trusting frontend user info
❌ Accessing SecurityContext in controller
❌ Hardcoding user references
11. Git Commit (Important)
git add .
git commit -m "Secure expense APIs to logged-in user via JWT"
12. What You Should Have Now
At this point:
- Expenses are user-scoped
- JWT identity is enforced
- Security is real and robust
This is real-world backend behavior.
13. What’s Next?
➡ Capstone – Article 11: Expense Categories & Advanced Queries
- Category management
- Filtering by date/category
- Pagination
Type Next when you’re ready 🚀