✅ Spring Boot To‑Do App — Project Documentation¶
A secure, database‑backed Spring Boot application that lets authenticated users create, list, update, and delete personal to‑dos.
The stack includes Spring MVC, Spring Security, Spring Data JPA, Hibernate, PostgreSQL, JSP views, and Bean Validation.
✨ Features¶
- User login with in‑memory users (BCrypt‑hashed passwords).
- To‑Do CRUD (create, read, update, delete) with JPA/Hibernate and PostgreSQL.
- Server‑side validation on the
Todoentity (description length). - JSP views with prefix/suffix config and a simple welcome page that shows the logged‑in username.
- Two controller implementations provided: in‑memory service and a production JPA variant.
🧱 Architecture & Modules¶
1) Domain Model — Todo Entity¶
@Entity @Table(name = "todoT")
public class Todo {
@Id @GeneratedValue private int id;
private String username;
@Size(min=5, message="enter at least 10 characters")
private String description;
private LocalDate targetDate;
private boolean done;
// getters/setters/ctors/toString...
}
todoT with @Entity/@Table.- Validation:
@Size(min=5, message="enter at least 10 characters"). (Note the message says 10 while min=5—see Known quirks.)
2) Persistence — Spring Data JPA¶
public interface TodoRepository extends JpaRepository<Todo,Integer> {
List<Todo> findByUsername(String username);
}
JpaRepository, plus a derived query to get todos for the logged‑in user.
3) Web Layer — Controllers¶
a) JPA Controller (active)¶
@Controller @SessionAttributes("name")
public class TodoControllerJPA {
@RequestMapping("list-todos")
String listAllTodos(ModelMap model) { ... }
@RequestMapping(value="add-todo", method=GET)
String showNewTodoPage(ModelMap model) { ... }
@RequestMapping(value="add-todo", method=POST)
String addNewTodoPage(ModelMap model, @Valid Todo todo, BindingResult result) { ... }
@RequestMapping("delete-todo")
String deleteTodo(@RequestParam int id) { ... }
@RequestMapping(value="update-todo", method=GET)
String showUpdatePage(@RequestParam int id, ModelMap model) { ... }
@RequestMapping(value="update-todo", method=POST)
String updateTodo(@Valid Todo todo, BindingResult result, ModelMap model) { ... }
private String getLoggedInUsername(ModelMap model) { ... }
}
SecurityContextHolder to scope data by username.- Persists changes via
TodoRepository.save(...).
b) In‑Memory Controller (example/legacy)¶
- Same endpoints as above, but backed by
TodoService(static list). Currently commented out with//@Controller. Useful for demos or tests without DB.
Welcome Controller¶
- Maps
/→welcomePage.jsp, adds the logged‑in username to the model.
4) Service Layer (demo) — TodoService¶
- Stores todos in a static list, provides
findByUserName,addTodo,deleteTodo,findById,updateTodo. - Intended for non‑JPA mode; the JPA controller supersedes it in production.
Note:
findByUserNameuses==for string comparison andaddTodoignores the incoming date; both are noted under Known quirks.
5) Security — SpringSecurityConfiguration¶
- Users:
Jack/password,Ferfero/ferfer(BCrypt‑encoded at startup). - AuthN/AuthZ: all routes require authentication; default form login is enabled.
- CSRF disabled and frame options disabled (useful during H2/dev, though here DB is Postgres).
@Bean InMemoryUserDetailsManager createUserDetailsManager() { ... }
@Bean SecurityFilterChain filterChain(HttpSecurity http) { ... }
@Bean PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); }
6) Configuration — application.properties¶
spring.mvc.view.prefix=WEB-INF/jsp/
spring.mvc.view.suffix=.jsp
spring.mvc.format.date=yyyy-MM-dd
spring.jpa.defer-datasource-initialization=true
spring.jpa.show-sql=true
spring.datasource.url=jdbc:postgresql://localhost:5432/todo
spring.datasource.username=postgres
spring.datasource.password=Abdurahmon2004
spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
yyyy-MM-dd.- PostgreSQL connection + Hibernate dialect;
ddl-auto=update.- SQL logged to console.
⚠️ Security Reminder: The file contains a plain‑text DB password. Move secrets to environment variables or a secrets manager before production use.
Sample Data (schema.sql / data.sql semantics)¶
insert into todo(id, username, description, target_date, done) values
(10003, 'Jack', 'Prepare presentation slides', CURRENT_DATE, false);
...
Jack.
🧩 Endpoints (HTTP + Views)¶
| Method | Path | Purpose | View |
|---|---|---|---|
| GET | / |
Welcome page | welcomePage |
| GET | /list-todos |
List current user’s todos | listTodos |
| GET | /add-todo |
Show new todo form | todo |
| POST | /add-todo |
Create todo (validate) | redirect→list |
| GET | /update-todo?id={id} |
Show edit form | todo |
| POST | /update-todo |
Update todo (validate) | redirect→list |
| GET | /delete-todo?id={id} |
Delete by id | redirect→list |
All endpoints require authentication; unauthenticated users are redirected to the login form.
🚀 Getting Started¶
Prerequisites¶
- JDK 17+
- Maven 3.9+
- PostgreSQL running locally (
tododatabase created; user/password match application.properties).
Run¶
mvn spring-boot:run
http://localhost:8080/ → login with Jack / password (or Ferfero / ferfer).
🧪 Validation & Forms¶
- The
Todoform binds to the entity with@Valid; ifBindingResulthas errors, thetodo.jspform is re‑shown. - Description must satisfy
@Size(min=5, message="enter at least 10 characters"). Adjust message/min to be consistent.
🛠 Known Quirks / Fix‑Ups¶
1) Validation message mismatch
- @Size(min=5, message="enter at least 10 characters") → choose either min=10 or fix message to 5.
2) String comparison in TodoService.findByUserName
- Uses == instead of .equals(...). Replace with:
todo -> username.equals(todo.getUsername())
3) TodoService.addTodo ignores provided date
- Currently uses LocalDate.now() regardless of input. Use the passed localDate.
4) CSRF disabled
- Re‑enable CSRF for production or protect state‑changing endpoints via tokens.
5) Plain‑text DB password
- Externalize to env vars or Spring Cloud Config / Vault.
📦 Packaging & Deployment¶
Build an executable jar:
mvn clean package
java -jar target/myFirstApp-*.jar
--spring.datasource.* properties at runtime. fileciteturn3file0
🔐 Users & Roles¶
Users are declared in‐memory with roles USER and ADMIN. Passwords are BCrypt encoded on startup:
User.builder().passwordEncoder(enc).username("Jack").password("password").roles("USER","ADMIN").build();
🗺 Data Model Notes¶
idis generated; ensure JSP forms include hiddenidfield during updates.doneis a boolean; render as checkbox in JSP.targetDateformat is configured toyyyy-MM-ddto match HTML date inputs. fileciteturn3file0
📚 Useful Extensions¶
- Replace JSP with Thymeleaf.
- Add pagination/sorting with
Pageable. - Add REST API (JSON) alongside MVC.
- Add integration tests with Testcontainers for Postgres.
- Connect Spring Security to a database or OAuth 2.0 provider. fileciteturn3file0