{"id":29,"date":"2026-10-08T16:30:50","date_gmt":"2026-10-08T16:30:50","guid":{"rendered":"https:\/\/boot-code.com\/?p=29"},"modified":"2026-10-08T16:30:52","modified_gmt":"2026-10-08T16:30:52","slug":"spring-todo-web-application-login-security-tutorial-extension","status":"publish","type":"post","link":"https:\/\/boot-code.com\/?p=29","title":{"rendered":"Spring Todo Web Application Login Security Tutorial Extension"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Yes. The next extension should add <strong>user registration + login\/logout + Spring Security + per-user Todo lists<\/strong>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The architecture becomes:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Browser\n   \u2502\n   \u25bc\nSpring Security\n   \u2502\n   \u251c\u2500\u2500 Login\n   \u251c\u2500\u2500 Logout\n   \u2514\u2500\u2500 Authentication\n   \u2502\n   \u25bc\nTodo Controller\n   \u2502\n   \u25bc\nTodo Service\n   \u2502\n   \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n   \u25bc              \u25bc\nUserRepository  TodoRepository\n   \u2502              \u2502\n   \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n          \u25bc\n        MySQL<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">I recommend using <strong>Spring Security 6 \/ Spring Boot 3.x<\/strong>, with passwords stored using <strong>BCrypt<\/strong>, never as plain text.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">I can give you the <strong>complete extension code<\/strong>, replacing the previous project with:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>User<\/code> entity<\/li>\n\n\n\n<li><code>Todo<\/code> \u2192 <code>User<\/code> relationship<\/li>\n\n\n\n<li>Registration page<\/li>\n\n\n\n<li>Login page<\/li>\n\n\n\n<li>Logout<\/li>\n\n\n\n<li>BCrypt password hashing<\/li>\n\n\n\n<li>Spring Security configuration<\/li>\n\n\n\n<li>User-specific todos<\/li>\n\n\n\n<li>Protected <code>\/todos\/**<\/code> pages<\/li>\n\n\n\n<li>REST API authentication<\/li>\n\n\n\n<li>MySQL database changes<\/li>\n\n\n\n<li>Bootstrap UI<\/li>\n\n\n\n<li>Complete Eclipse project structure<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">The important database design will become:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>users\n\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nid\nusername\npassword\nrole\n\n\ntodos\n\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nid\ntitle\ncompleted\nuser_id  \u2192 users.id<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">So, for example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>User: devesh\n   \u2502\n   \u251c\u2500\u2500 Learn Java\n   \u251c\u2500\u2500 Learn Spring\n   \u2514\u2500\u2500 Learn MySQL\n\nUser: rahul\n   \u2502\n   \u251c\u2500\u2500 Learn Python\n   \u2514\u2500\u2500 Learn Django<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Each logged-in user will see <strong>only their own todos<\/strong>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If you want, I can provide the <strong>full copy-paste code for every Java, HTML, <code>pom.xml<\/code>, and <code>application.properties<\/code> file<\/strong> as the next step.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Yes. Below is the <strong>complete upgraded Spring Todo Web Application with Login\/Security<\/strong>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">It extends the previous project with:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Spring Security<\/li>\n\n\n\n<li>Registration<\/li>\n\n\n\n<li>Login\/logout<\/li>\n\n\n\n<li>BCrypt password hashing<\/li>\n\n\n\n<li>User roles<\/li>\n\n\n\n<li>MySQL<\/li>\n\n\n\n<li>User-specific Todo lists<\/li>\n\n\n\n<li>Add\/edit\/delete\/toggle Todo<\/li>\n\n\n\n<li>Thymeleaf + Bootstrap<\/li>\n\n\n\n<li>REST API<\/li>\n\n\n\n<li>Validation<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">1. Final project structure<\/h1>\n\n\n\n<pre class=\"wp-block-code\"><code>todo\/\n\u251c\u2500\u2500 pom.xml\n\u2502\n\u2514\u2500\u2500 src\/main\/\n    \u251c\u2500\u2500 java\/com\/example\/todo\/\n    \u2502\n    \u2502   \u251c\u2500\u2500 TodoApplication.java\n    \u2502   \u2502\n    \u2502   \u251c\u2500\u2500 config\/\n    \u2502   \u2502   \u2514\u2500\u2500 SecurityConfig.java\n    \u2502   \u2502\n    \u2502   \u251c\u2500\u2500 controller\/\n    \u2502   \u2502   \u251c\u2500\u2500 AuthController.java\n    \u2502   \u2502   \u251c\u2500\u2500 TodoController.java\n    \u2502   \u2502   \u2514\u2500\u2500 TodoRestController.java\n    \u2502   \u2502\n    \u2502   \u251c\u2500\u2500 model\/\n    \u2502   \u2502   \u251c\u2500\u2500 User.java\n    \u2502   \u2502   \u2514\u2500\u2500 Todo.java\n    \u2502   \u2502\n    \u2502   \u251c\u2500\u2500 repository\/\n    \u2502   \u2502   \u251c\u2500\u2500 UserRepository.java\n    \u2502   \u2502   \u2514\u2500\u2500 TodoRepository.java\n    \u2502   \u2502\n    \u2502   \u2514\u2500\u2500 service\/\n    \u2502       \u251c\u2500\u2500 UserService.java\n    \u2502       \u2514\u2500\u2500 TodoService.java\n    \u2502\n    \u2514\u2500\u2500 resources\/\n        \u251c\u2500\u2500 templates\/\n        \u2502   \u251c\u2500\u2500 login.html\n        \u2502   \u251c\u2500\u2500 register.html\n        \u2502   \u251c\u2500\u2500 index.html\n        \u2502   \u2514\u2500\u2500 edit.html\n        \u2502\n        \u2514\u2500\u2500 application.properties<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">2. MySQL database<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Start MySQL:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo systemctl start mysql<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Open MySQL:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo mysql<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Create database and user:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE DATABASE todo_db;\n\nCREATE USER 'todo_user'@'localhost'\nIDENTIFIED BY 'todo_password';\n\nGRANT ALL PRIVILEGES ON todo_db.*\nTO 'todo_user'@'localhost';\n\nFLUSH PRIVILEGES;\n\nEXIT;<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">You don&#8217;t need to manually create the <code>users<\/code> or <code>todo<\/code> tables.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">JPA\/Hibernate will create them.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">3. <code>pom.xml<\/code><\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Replace your existing <code>pom.xml<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;?xml version=\"1.0\" encoding=\"UTF-8\"?&gt;\n\n&lt;project xmlns=\"http:\/\/maven.apache.org\/POM\/4.0.0\"\n         xmlns:xsi=\"http:\/\/www.w3.org\/2001\/XMLSchema-instance\"\n         xsi:schemaLocation=\"\n         http:&#47;&#47;maven.apache.org\/POM\/4.0.0\n         https:\/\/maven.apache.org\/xsd\/maven-4.0.0.xsd\"&gt;\n\n    &lt;modelVersion&gt;4.0.0&lt;\/modelVersion&gt;\n\n    &lt;parent&gt;\n        &lt;groupId&gt;org.springframework.boot&lt;\/groupId&gt;\n        &lt;artifactId&gt;spring-boot-starter-parent&lt;\/artifactId&gt;\n        &lt;version&gt;3.5.6&lt;\/version&gt;\n        &lt;relativePath\/&gt;\n    &lt;\/parent&gt;\n\n    &lt;groupId&gt;com.example&lt;\/groupId&gt;\n\n    &lt;artifactId&gt;todo&lt;\/artifactId&gt;\n\n    &lt;version&gt;0.0.1-SNAPSHOT&lt;\/version&gt;\n\n    &lt;name&gt;todo&lt;\/name&gt;\n\n    &lt;description&gt;\n        Spring Boot Todo Application with Security\n    &lt;\/description&gt;\n\n    &lt;properties&gt;\n        &lt;java.version&gt;17&lt;\/java.version&gt;\n    &lt;\/properties&gt;\n\n    &lt;dependencies&gt;\n\n        &lt;!-- Spring Web --&gt;\n        &lt;dependency&gt;\n            &lt;groupId&gt;org.springframework.boot&lt;\/groupId&gt;\n            &lt;artifactId&gt;spring-boot-starter-web&lt;\/artifactId&gt;\n        &lt;\/dependency&gt;\n\n        &lt;!-- Thymeleaf --&gt;\n        &lt;dependency&gt;\n            &lt;groupId&gt;org.springframework.boot&lt;\/groupId&gt;\n            &lt;artifactId&gt;spring-boot-starter-thymeleaf&lt;\/artifactId&gt;\n        &lt;\/dependency&gt;\n\n        &lt;!-- Thymeleaf Spring Security --&gt;\n        &lt;dependency&gt;\n            &lt;groupId&gt;org.thymeleaf.extras&lt;\/groupId&gt;\n            &lt;artifactId&gt;thymeleaf-extras-springsecurity6&lt;\/artifactId&gt;\n        &lt;\/dependency&gt;\n\n        &lt;!-- JPA --&gt;\n        &lt;dependency&gt;\n            &lt;groupId&gt;org.springframework.boot&lt;\/groupId&gt;\n            &lt;artifactId&gt;spring-boot-starter-data-jpa&lt;\/artifactId&gt;\n        &lt;\/dependency&gt;\n\n        &lt;!-- Spring Security --&gt;\n        &lt;dependency&gt;\n            &lt;groupId&gt;org.springframework.boot&lt;\/groupId&gt;\n            &lt;artifactId&gt;spring-boot-starter-security&lt;\/artifactId&gt;\n        &lt;\/dependency&gt;\n\n        &lt;!-- Validation --&gt;\n        &lt;dependency&gt;\n            &lt;groupId&gt;org.springframework.boot&lt;\/groupId&gt;\n            &lt;artifactId&gt;spring-boot-starter-validation&lt;\/artifactId&gt;\n        &lt;\/dependency&gt;\n\n        &lt;!-- MySQL --&gt;\n        &lt;dependency&gt;\n            &lt;groupId&gt;com.mysql&lt;\/groupId&gt;\n            &lt;artifactId&gt;mysql-connector-j&lt;\/artifactId&gt;\n            &lt;scope&gt;runtime&lt;\/scope&gt;\n        &lt;\/dependency&gt;\n\n        &lt;!-- DevTools --&gt;\n        &lt;dependency&gt;\n            &lt;groupId&gt;org.springframework.boot&lt;\/groupId&gt;\n            &lt;artifactId&gt;spring-boot-devtools&lt;\/artifactId&gt;\n            &lt;optional&gt;true&lt;\/optional&gt;\n        &lt;\/dependency&gt;\n\n        &lt;!-- Testing --&gt;\n        &lt;dependency&gt;\n            &lt;groupId&gt;org.springframework.boot&lt;\/groupId&gt;\n            &lt;artifactId&gt;spring-boot-starter-test&lt;\/artifactId&gt;\n            &lt;scope&gt;test&lt;\/scope&gt;\n        &lt;\/dependency&gt;\n\n        &lt;!-- Security Testing --&gt;\n        &lt;dependency&gt;\n            &lt;groupId&gt;org.springframework.security&lt;\/groupId&gt;\n            &lt;artifactId&gt;spring-security-test&lt;\/artifactId&gt;\n            &lt;scope&gt;test&lt;\/scope&gt;\n        &lt;\/dependency&gt;\n\n    &lt;\/dependencies&gt;\n\n    &lt;build&gt;\n\n        &lt;plugins&gt;\n\n            &lt;plugin&gt;\n                &lt;groupId&gt;org.springframework.boot&lt;\/groupId&gt;\n                &lt;artifactId&gt;spring-boot-maven-plugin&lt;\/artifactId&gt;\n            &lt;\/plugin&gt;\n\n        &lt;\/plugins&gt;\n\n    &lt;\/build&gt;\n\n&lt;\/project&gt;<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">4. Main application<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\"><code>TodoApplication.java<\/code><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>package com.example.todo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class TodoApplication {\n\n    public static void main(String&#91;] args) {\n\n        SpringApplication.run(\n            TodoApplication.class,\n            args\n        );\n\n    }\n}<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">5. User entity<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Create:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>model\/User.java<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>package com.example.todo.model;\n\nimport jakarta.persistence.*;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n@Entity\n@Table(name = \"users\")\npublic class User {\n\n    @Id\n    @GeneratedValue(strategy = GenerationType.IDENTITY)\n    private Long id;\n\n    @Column(nullable = false, unique = true)\n    private String username;\n\n    @Column(nullable = false)\n    private String password;\n\n    @Column(nullable = false)\n    private String role = \"USER\";\n\n    @OneToMany(\n        mappedBy = \"user\",\n        cascade = CascadeType.ALL,\n        orphanRemoval = true\n    )\n    private List&lt;Todo&gt; todos = new ArrayList&lt;&gt;();\n\n    public User() {\n    }\n\n    public User(\n            String username,\n            String password,\n            String role) {\n\n        this.username = username;\n        this.password = password;\n        this.role = role;\n    }\n\n    public Long getId() {\n        return id;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n    public void setUsername(String username) {\n        this.username = username;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public void setPassword(String password) {\n        this.password = password;\n    }\n\n    public String getRole() {\n        return role;\n    }\n\n    public void setRole(String role) {\n        this.role = role;\n    }\n\n    public List&lt;Todo&gt; getTodos() {\n        return todos;\n    }\n\n    public void setTodos(List&lt;Todo&gt; todos) {\n        this.todos = todos;\n    }\n}<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">6. Todo entity<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Replace the previous <code>Todo.java<\/code> with:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>package com.example.todo.model;\n\nimport jakarta.persistence.*;\nimport jakarta.validation.constraints.NotBlank;\nimport jakarta.validation.constraints.Size;\n\n@Entity\n@Table(name = \"todos\")\npublic class Todo {\n\n    @Id\n    @GeneratedValue(strategy = GenerationType.IDENTITY)\n    private Long id;\n\n    @NotBlank(message = \"Title is required\")\n    @Size(\n        min = 3,\n        max = 100,\n        message = \"Title must be between 3 and 100 characters\"\n    )\n    private String title;\n\n    private boolean completed;\n\n    @ManyToOne(fetch = FetchType.LAZY)\n    @JoinColumn(name = \"user_id\", nullable = false)\n    private User user;\n\n    public Todo() {\n    }\n\n    public Todo(String title) {\n\n        this.title = title;\n\n        this.completed = false;\n    }\n\n    public Long getId() {\n        return id;\n    }\n\n    public String getTitle() {\n        return title;\n    }\n\n    public void setTitle(String title) {\n        this.title = title;\n    }\n\n    public boolean isCompleted() {\n        return completed;\n    }\n\n    public void setCompleted(boolean completed) {\n        this.completed = completed;\n    }\n\n    public User getUser() {\n        return user;\n    }\n\n    public void setUser(User user) {\n        this.user = user;\n    }\n}<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">7. User repository<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\"><code>repository\/UserRepository.java<\/code><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>package com.example.todo.repository;\n\nimport java.util.Optional;\n\nimport org.springframework.data.jpa.repository.JpaRepository;\n\nimport com.example.todo.model.User;\n\npublic interface UserRepository\n        extends JpaRepository&lt;User, Long&gt; {\n\n    Optional&lt;User&gt; findByUsername(String username);\n\n    boolean existsByUsername(String username);\n}<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">8. Todo repository<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\"><code>repository\/TodoRepository.java<\/code><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>package com.example.todo.repository;\n\nimport java.util.List;\n\nimport org.springframework.data.jpa.repository.JpaRepository;\n\nimport com.example.todo.model.Todo;\nimport com.example.todo.model.User;\n\npublic interface TodoRepository\n        extends JpaRepository&lt;Todo, Long&gt; {\n\n    List&lt;Todo&gt; findByUser(User user);\n\n    java.util.Optional&lt;Todo&gt;\n    findByIdAndUser(Long id, User user);\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This is important.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Instead of:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>findAll()<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">we use:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>findByUser(user)<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Therefore one user cannot see another user&#8217;s todos.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">9. User service<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Create:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>service\/UserService.java<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>package com.example.todo.service;\n\nimport org.springframework.security.crypto.password.PasswordEncoder;\nimport org.springframework.stereotype.Service;\n\nimport com.example.todo.model.User;\nimport com.example.todo.repository.UserRepository;\n\n@Service\npublic class UserService {\n\n    private final UserRepository userRepository;\n\n    private final PasswordEncoder passwordEncoder;\n\n    public UserService(\n            UserRepository userRepository,\n            PasswordEncoder passwordEncoder) {\n\n        this.userRepository = userRepository;\n\n        this.passwordEncoder = passwordEncoder;\n    }\n\n    public User registerUser(\n            String username,\n            String password) {\n\n        if (userRepository.existsByUsername(username)) {\n\n            throw new RuntimeException(\n                \"Username already exists\"\n            );\n\n        }\n\n        String encodedPassword =\n                passwordEncoder.encode(password);\n\n        User user = new User(\n            username,\n            encodedPassword,\n            \"USER\"\n        );\n\n        return userRepository.save(user);\n    }\n}<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">10. Todo service<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Replace the previous service:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>service\/TodoService.java<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">with:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>package com.example.todo.service;\n\nimport java.util.List;\n\nimport org.springframework.stereotype.Service;\n\nimport com.example.todo.model.Todo;\nimport com.example.todo.model.User;\nimport com.example.todo.repository.TodoRepository;\n\n@Service\npublic class TodoService {\n\n    private final TodoRepository todoRepository;\n\n    public TodoService(\n            TodoRepository todoRepository) {\n\n        this.todoRepository = todoRepository;\n    }\n\n    public List&lt;Todo&gt; getTodosForUser(User user) {\n\n        return todoRepository.findByUser(user);\n    }\n\n    public Todo getTodo(\n            Long id,\n            User user) {\n\n        return todoRepository\n                .findByIdAndUser(id, user)\n                .orElseThrow(() -&gt;\n                    new RuntimeException(\n                        \"Todo not found\"\n                    )\n                );\n    }\n\n    public Todo saveTodo(\n            Todo todo,\n            User user) {\n\n        todo.setUser(user);\n\n        return todoRepository.save(todo);\n    }\n\n    public void updateTodo(\n            Long id,\n            Todo details,\n            User user) {\n\n        Todo todo = getTodo(id, user);\n\n        todo.setTitle(details.getTitle());\n\n        todo.setCompleted(\n            details.isCompleted()\n        );\n\n        todoRepository.save(todo);\n    }\n\n    public void deleteTodo(\n            Long id,\n            User user) {\n\n        Todo todo = getTodo(id, user);\n\n        todoRepository.delete(todo);\n    }\n\n    public void toggleTodo(\n            Long id,\n            User user) {\n\n        Todo todo = getTodo(id, user);\n\n        todo.setCompleted(\n            !todo.isCompleted()\n        );\n\n        todoRepository.save(todo);\n    }\n}<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">11. Spring Security configuration<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">This is the most important new class.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Create:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>config\/SecurityConfig.java<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>package com.example.todo.config;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;\nimport org.springframework.security.crypto.password.PasswordEncoder;\nimport org.springframework.security.web.SecurityFilterChain;\n\n@Configuration\npublic class SecurityConfig {\n\n    @Bean\n    public PasswordEncoder passwordEncoder() {\n\n        return new BCryptPasswordEncoder();\n    }\n\n    @Bean\n    public SecurityFilterChain securityFilterChain(\n            HttpSecurity http) throws Exception {\n\n        http\n\n            .authorizeHttpRequests(auth -&gt; auth\n\n                .requestMatchers(\n                    \"\/login\",\n                    \"\/register\",\n                    \"\/css\/**\",\n                    \"\/js\/**\"\n                ).permitAll()\n\n                .requestMatchers(\"\/api\/**\")\n                .authenticated()\n\n                .anyRequest()\n                .authenticated()\n            )\n\n            .formLogin(form -&gt; form\n\n                .loginPage(\"\/login\")\n\n                .defaultSuccessUrl(\n                    \"\/\",\n                    true\n                )\n\n                .permitAll()\n            )\n\n            .logout(logout -&gt; logout\n\n                .logoutSuccessUrl(\n                    \"\/login?logout\"\n                )\n\n                .permitAll()\n            );\n\n        return http.build();\n    }\n}<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">12. Custom UserDetailsService<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Spring Security needs to know how to load users from MySQL.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Create:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>service\/CustomUserDetailsService.java<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>package com.example.todo.service;\n\nimport org.springframework.security.core.userdetails.*;\nimport org.springframework.stereotype.Service;\n\nimport com.example.todo.model.User;\nimport com.example.todo.repository.UserRepository;\n\n@Service\npublic class CustomUserDetailsService\n        implements UserDetailsService {\n\n    private final UserRepository userRepository;\n\n    public CustomUserDetailsService(\n            UserRepository userRepository) {\n\n        this.userRepository = userRepository;\n    }\n\n    @Override\n    public UserDetails loadUserByUsername(\n            String username)\n            throws UsernameNotFoundException {\n\n        User user =\n            userRepository\n                .findByUsername(username)\n                .orElseThrow(() -&gt;\n                    new UsernameNotFoundException(\n                        \"User not found\"\n                    )\n                );\n\n        return User.withUsername(\n                user.getUsername()\n            )\n            .password(user.getPassword())\n            .roles(user.getRole())\n            .build();\n    }\n}<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">13. Authentication controller<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Create:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>controller\/AuthController.java<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>package com.example.todo.controller;\n\nimport org.springframework.stereotype.Controller;\nimport org.springframework.ui.Model;\nimport org.springframework.web.bind.annotation.*;\n\nimport com.example.todo.service.UserService;\n\n@Controller\npublic class AuthController {\n\n    private final UserService userService;\n\n    public AuthController(\n            UserService userService) {\n\n        this.userService = userService;\n    }\n\n    @GetMapping(\"\/login\")\n    public String login(\n            @RequestParam(\n                value = \"error\",\n                required = false\n            )\n            String error,\n\n            @RequestParam(\n                value = \"logout\",\n                required = false\n            )\n            String logout,\n\n            Model model) {\n\n        if (error != null) {\n\n            model.addAttribute(\n                \"error\",\n                \"Invalid username or password\"\n            );\n        }\n\n        if (logout != null) {\n\n            model.addAttribute(\n                \"message\",\n                \"You have been logged out\"\n            );\n        }\n\n        return \"login\";\n    }\n\n    @GetMapping(\"\/register\")\n    public String registerPage(\n            Model model) {\n\n        model.addAttribute(\n            \"username\",\n            \"\"\n        );\n\n        return \"register\";\n    }\n\n    @PostMapping(\"\/register\")\n    public String register(\n            @RequestParam String username,\n            @RequestParam String password,\n            Model model) {\n\n        if (username == null ||\n            username.trim().isEmpty()) {\n\n            model.addAttribute(\n                \"error\",\n                \"Username is required\"\n            );\n\n            return \"register\";\n        }\n\n        if (password == null ||\n            password.length() &lt; 6) {\n\n            model.addAttribute(\n                \"error\",\n                \"Password must contain at least 6 characters\"\n            );\n\n            return \"register\";\n        }\n\n        try {\n\n            userService.registerUser(\n                username.trim(),\n                password\n            );\n\n            return \"redirect:\/login?registered\";\n\n        } catch (RuntimeException e) {\n\n            model.addAttribute(\n                \"error\",\n                e.getMessage()\n            );\n\n            return \"register\";\n        }\n    }\n}<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">14. Todo web controller<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Replace your previous <code>TodoController.java<\/code> with:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>package com.example.todo.controller;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.ui.Model;\nimport org.springframework.validation.BindingResult;\nimport org.springframework.web.bind.annotation.*;\n\nimport com.example.todo.model.Todo;\nimport com.example.todo.model.User;\nimport com.example.todo.repository.UserRepository;\nimport com.example.todo.service.TodoService;\n\nimport jakarta.validation.Valid;\n\n@Controller\npublic class TodoController {\n\n    private final TodoService todoService;\n\n    private final UserRepository userRepository;\n\n    public TodoController(\n            TodoService todoService,\n            UserRepository userRepository) {\n\n        this.todoService = todoService;\n\n        this.userRepository = userRepository;\n    }\n\n    private User getCurrentUser(\n            Authentication authentication) {\n\n        return userRepository\n                .findByUsername(\n                    authentication.getName()\n                )\n                .orElseThrow();\n    }\n\n    @GetMapping(\"\/\")\n    public String home(\n            Model model,\n            Authentication authentication) {\n\n        User user =\n            getCurrentUser(authentication);\n\n        model.addAttribute(\n            \"todos\",\n            todoService.getTodosForUser(user)\n        );\n\n        model.addAttribute(\n            \"todo\",\n            new Todo()\n        );\n\n        model.addAttribute(\n            \"username\",\n            user.getUsername()\n        );\n\n        return \"index\";\n    }\n\n    @PostMapping(\"\/todos\")\n    public String addTodo(\n            @Valid @ModelAttribute(\"todo\")\n            Todo todo,\n\n            BindingResult result,\n\n            Authentication authentication,\n\n            Model model) {\n\n        User user =\n            getCurrentUser(authentication);\n\n        if (result.hasErrors()) {\n\n            model.addAttribute(\n                \"todos\",\n                todoService.getTodosForUser(user)\n            );\n\n            model.addAttribute(\n                \"username\",\n                user.getUsername()\n            );\n\n            return \"index\";\n        }\n\n        todoService.saveTodo(\n            todo,\n            user\n        );\n\n        return \"redirect:\/\";\n    }\n\n    @GetMapping(\"\/todos\/edit\/{id}\")\n    public String editTodo(\n            @PathVariable Long id,\n            Authentication authentication,\n            Model model) {\n\n        User user =\n            getCurrentUser(authentication);\n\n        Todo todo =\n            todoService.getTodo(id, user);\n\n        model.addAttribute(\n            \"todo\",\n            todo\n        );\n\n        return \"edit\";\n    }\n\n    @PostMapping(\"\/todos\/update\/{id}\")\n    public String updateTodo(\n            @PathVariable Long id,\n\n            @Valid @ModelAttribute(\"todo\")\n            Todo todo,\n\n            BindingResult result,\n\n            Authentication authentication) {\n\n        if (result.hasErrors()) {\n\n            return \"edit\";\n        }\n\n        User user =\n            getCurrentUser(authentication);\n\n        todoService.updateTodo(\n            id,\n            todo,\n            user\n        );\n\n        return \"redirect:\/\";\n    }\n\n    @GetMapping(\"\/todos\/delete\/{id}\")\n    public String deleteTodo(\n            @PathVariable Long id,\n            Authentication authentication) {\n\n        User user =\n            getCurrentUser(authentication);\n\n        todoService.deleteTodo(\n            id,\n            user\n        );\n\n        return \"redirect:\/\";\n    }\n\n    @GetMapping(\"\/todos\/toggle\/{id}\")\n    public String toggleTodo(\n            @PathVariable Long id,\n            Authentication authentication) {\n\n        User user =\n            getCurrentUser(authentication);\n\n        todoService.toggleTodo(\n            id,\n            user\n        );\n\n        return \"redirect:\/\";\n    }\n}<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">15. REST controller<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Create:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>controller\/TodoRestController.java<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>package com.example.todo.controller;\n\nimport java.util.List;\n\nimport org.springframework.http.*;\nimport org.springframework.security.core.Authentication;\nimport org.springframework.web.bind.annotation.*;\n\nimport com.example.todo.model.Todo;\nimport com.example.todo.model.User;\nimport com.example.todo.repository.UserRepository;\nimport com.example.todo.service.TodoService;\n\n@RestController\n@RequestMapping(\"\/api\/todos\")\npublic class TodoRestController {\n\n    private final TodoService todoService;\n\n    private final UserRepository userRepository;\n\n    public TodoRestController(\n            TodoService todoService,\n            UserRepository userRepository) {\n\n        this.todoService = todoService;\n\n        this.userRepository = userRepository;\n    }\n\n    private User currentUser(\n            Authentication authentication) {\n\n        return userRepository\n                .findByUsername(\n                    authentication.getName()\n                )\n                .orElseThrow();\n    }\n\n    @GetMapping\n    public List&lt;Todo&gt; getTodos(\n            Authentication authentication) {\n\n        User user =\n            currentUser(authentication);\n\n        return todoService.getTodosForUser(user);\n    }\n\n    @GetMapping(\"\/{id}\")\n    public ResponseEntity&lt;Todo&gt; getTodo(\n            @PathVariable Long id,\n            Authentication authentication) {\n\n        try {\n\n            User user =\n                currentUser(authentication);\n\n            return ResponseEntity.ok(\n                todoService.getTodo(id, user)\n            );\n\n        } catch (RuntimeException e) {\n\n            return ResponseEntity\n                    .notFound()\n                    .build();\n        }\n    }\n\n    @PostMapping\n    public ResponseEntity&lt;Todo&gt; createTodo(\n            @RequestBody Todo todo,\n            Authentication authentication) {\n\n        User user =\n            currentUser(authentication);\n\n        Todo saved =\n            todoService.saveTodo(\n                todo,\n                user\n            );\n\n        return ResponseEntity\n                .status(HttpStatus.CREATED)\n                .body(saved);\n    }\n\n    @PutMapping(\"\/{id}\")\n    public ResponseEntity&lt;Todo&gt; updateTodo(\n            @PathVariable Long id,\n            @RequestBody Todo todo,\n            Authentication authentication) {\n\n        try {\n\n            User user =\n                currentUser(authentication);\n\n            todoService.updateTodo(\n                id,\n                todo,\n                user\n            );\n\n            return ResponseEntity.ok(\n                todoService.getTodo(id, user)\n            );\n\n        } catch (RuntimeException e) {\n\n            return ResponseEntity\n                    .notFound()\n                    .build();\n        }\n    }\n\n    @DeleteMapping(\"\/{id}\")\n    public ResponseEntity&lt;Void&gt; deleteTodo(\n            @PathVariable Long id,\n            Authentication authentication) {\n\n        try {\n\n            User user =\n                currentUser(authentication);\n\n            todoService.deleteTodo(\n                id,\n                user\n            );\n\n            return ResponseEntity\n                    .noContent()\n                    .build();\n\n        } catch (RuntimeException e) {\n\n            return ResponseEntity\n                    .notFound()\n                    .build();\n        }\n    }\n\n    @PatchMapping(\"\/{id}\/toggle\")\n    public ResponseEntity&lt;Todo&gt; toggleTodo(\n            @PathVariable Long id,\n            Authentication authentication) {\n\n        try {\n\n            User user =\n                currentUser(authentication);\n\n            todoService.toggleTodo(\n                id,\n                user\n            );\n\n            return ResponseEntity.ok(\n                todoService.getTodo(id, user)\n            );\n\n        } catch (RuntimeException e) {\n\n            return ResponseEntity\n                    .notFound()\n                    .build();\n        }\n    }\n}<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">16. Login page<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Create:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>src\/main\/resources\/templates\/login.html<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;!DOCTYPE html&gt;\n&lt;html lang=\"en\"&gt;\n\n&lt;head&gt;\n\n    &lt;meta charset=\"UTF-8\"&gt;\n\n    &lt;meta name=\"viewport\"\n          content=\"width=device-width, initial-scale=1\"&gt;\n\n    &lt;title&gt;Login - Todo&lt;\/title&gt;\n\n    &lt;link\n        href=\"https:\/\/cdn.jsdelivr.net\/npm\/bootstrap@5.3.3\/dist\/css\/bootstrap.min.css\"\n        rel=\"stylesheet\"&gt;\n\n&lt;\/head&gt;\n\n&lt;body class=\"bg-light\"&gt;\n\n&lt;div class=\"container mt-5\"&gt;\n\n    &lt;div class=\"row justify-content-center\"&gt;\n\n        &lt;div class=\"col-md-5\"&gt;\n\n            &lt;div class=\"card shadow\"&gt;\n\n                &lt;div class=\"card-header\n                            bg-primary\n                            text-white\"&gt;\n\n                    &lt;h3 class=\"text-center\"&gt;\n                        Todo Login\n                    &lt;\/h3&gt;\n\n                &lt;\/div&gt;\n\n                &lt;div class=\"card-body\"&gt;\n\n                    &lt;div\n                        th:if=\"${error}\"\n                        class=\"alert alert-danger\"\n                        th:text=\"${error}\"&gt;\n                    &lt;\/div&gt;\n\n                    &lt;div\n                        th:if=\"${message}\"\n                        class=\"alert alert-success\"\n                        th:text=\"${message}\"&gt;\n                    &lt;\/div&gt;\n\n                    &lt;div\n                        th:if=\"${param.registered}\"\n                        class=\"alert alert-success\"&gt;\n\n                        Registration successful.\n                        Please login.\n\n                    &lt;\/div&gt;\n\n                    &lt;form\n                        action=\"\/login\"\n                        method=\"post\"&gt;\n\n                        &lt;div class=\"mb-3\"&gt;\n\n                            &lt;label class=\"form-label\"&gt;\n                                Username\n                            &lt;\/label&gt;\n\n                            &lt;input\n                                type=\"text\"\n                                name=\"username\"\n                                class=\"form-control\"\n                                required&gt;\n\n                        &lt;\/div&gt;\n\n                        &lt;div class=\"mb-3\"&gt;\n\n                            &lt;label class=\"form-label\"&gt;\n                                Password\n                            &lt;\/label&gt;\n\n                            &lt;input\n                                type=\"password\"\n                                name=\"password\"\n                                class=\"form-control\"\n                                required&gt;\n\n                        &lt;\/div&gt;\n\n                        &lt;button\n                            type=\"submit\"\n                            class=\"btn btn-primary w-100\"&gt;\n\n                            Login\n\n                        &lt;\/button&gt;\n\n                    &lt;\/form&gt;\n\n                    &lt;hr&gt;\n\n                    &lt;div class=\"text-center\"&gt;\n\n                        &lt;span&gt;\n                            Don't have an account?\n                        &lt;\/span&gt;\n\n                        &lt;a href=\"\/register\"&gt;\n                            Register\n                        &lt;\/a&gt;\n\n                    &lt;\/div&gt;\n\n                &lt;\/div&gt;\n\n            &lt;\/div&gt;\n\n        &lt;\/div&gt;\n\n    &lt;\/div&gt;\n\n&lt;\/div&gt;\n\n&lt;\/body&gt;\n\n&lt;\/html&gt;<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">17. Registration page<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Create:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>src\/main\/resources\/templates\/register.html<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;!DOCTYPE html&gt;\n&lt;html lang=\"en\"&gt;\n\n&lt;head&gt;\n\n    &lt;meta charset=\"UTF-8\"&gt;\n\n    &lt;meta name=\"viewport\"\n          content=\"width=device-width, initial-scale=1\"&gt;\n\n    &lt;title&gt;Register - Todo&lt;\/title&gt;\n\n    &lt;link\n        href=\"https:\/\/cdn.jsdelivr.net\/npm\/bootstrap@5.3.3\/dist\/css\/bootstrap.min.css\"\n        rel=\"stylesheet\"&gt;\n\n&lt;\/head&gt;\n\n&lt;body class=\"bg-light\"&gt;\n\n&lt;div class=\"container mt-5\"&gt;\n\n    &lt;div class=\"row justify-content-center\"&gt;\n\n        &lt;div class=\"col-md-5\"&gt;\n\n            &lt;div class=\"card shadow\"&gt;\n\n                &lt;div class=\"card-header\n                            bg-success\n                            text-white\"&gt;\n\n                    &lt;h3 class=\"text-center\"&gt;\n                        Create Account\n                    &lt;\/h3&gt;\n\n                &lt;\/div&gt;\n\n                &lt;div class=\"card-body\"&gt;\n\n                    &lt;div\n                        th:if=\"${error}\"\n                        class=\"alert alert-danger\"\n                        th:text=\"${error}\"&gt;\n                    &lt;\/div&gt;\n\n                    &lt;form\n                        action=\"\/register\"\n                        method=\"post\"&gt;\n\n                        &lt;div class=\"mb-3\"&gt;\n\n                            &lt;label class=\"form-label\"&gt;\n                                Username\n                            &lt;\/label&gt;\n\n                            &lt;input\n                                type=\"text\"\n                                name=\"username\"\n                                class=\"form-control\"\n                                minlength=\"3\"\n                                required&gt;\n\n                        &lt;\/div&gt;\n\n                        &lt;div class=\"mb-3\"&gt;\n\n                            &lt;label class=\"form-label\"&gt;\n                                Password\n                            &lt;\/label&gt;\n\n                            &lt;input\n                                type=\"password\"\n                                name=\"password\"\n                                class=\"form-control\"\n                                minlength=\"6\"\n                                required&gt;\n\n                            &lt;small class=\"text-muted\"&gt;\n                                Minimum 6 characters\n                            &lt;\/small&gt;\n\n                        &lt;\/div&gt;\n\n                        &lt;button\n                            type=\"submit\"\n                            class=\"btn btn-success w-100\"&gt;\n\n                            Register\n\n                        &lt;\/button&gt;\n\n                    &lt;\/form&gt;\n\n                    &lt;hr&gt;\n\n                    &lt;div class=\"text-center\"&gt;\n\n                        Already have an account?\n\n                        &lt;a href=\"\/login\"&gt;\n                            Login\n                        &lt;\/a&gt;\n\n                    &lt;\/div&gt;\n\n                &lt;\/div&gt;\n\n            &lt;\/div&gt;\n\n        &lt;\/div&gt;\n\n    &lt;\/div&gt;\n\n&lt;\/div&gt;\n\n&lt;\/body&gt;\n\n&lt;\/html&gt;<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">18. Todo home page<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Create\/replace:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>src\/main\/resources\/templates\/index.html<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;!DOCTYPE html&gt;\n&lt;html lang=\"en\"\n      xmlns:th=\"http:\/\/www.thymeleaf.org\"&gt;\n\n&lt;head&gt;\n\n    &lt;meta charset=\"UTF-8\"&gt;\n\n    &lt;meta name=\"viewport\"\n          content=\"width=device-width, initial-scale=1\"&gt;\n\n    &lt;title&gt;My Todos&lt;\/title&gt;\n\n    &lt;link\n        href=\"https:\/\/cdn.jsdelivr.net\/npm\/bootstrap@5.3.3\/dist\/css\/bootstrap.min.css\"\n        rel=\"stylesheet\"&gt;\n\n&lt;\/head&gt;\n\n&lt;body class=\"bg-light\"&gt;\n\n&lt;nav class=\"navbar navbar-dark bg-primary\"&gt;\n\n    &lt;div class=\"container\"&gt;\n\n        &lt;span class=\"navbar-brand\"&gt;\n            Todo Application\n        &lt;\/span&gt;\n\n        &lt;div class=\"d-flex align-items-center\"&gt;\n\n            &lt;span class=\"text-white me-3\"&gt;\n\n                Welcome,\n                &lt;strong th:text=\"${username}\"&gt;\n                    user\n                &lt;\/strong&gt;\n\n            &lt;\/span&gt;\n\n            &lt;form\n                action=\"\/logout\"\n                method=\"post\"&gt;\n\n                &lt;button\n                    type=\"submit\"\n                    class=\"btn btn-light btn-sm\"&gt;\n\n                    Logout\n\n                &lt;\/button&gt;\n\n            &lt;\/form&gt;\n\n        &lt;\/div&gt;\n\n    &lt;\/div&gt;\n\n&lt;\/nav&gt;\n\n\n&lt;div class=\"container mt-5\"&gt;\n\n    &lt;div class=\"row justify-content-center\"&gt;\n\n        &lt;div class=\"col-md-10\"&gt;\n\n            &lt;div class=\"card shadow\"&gt;\n\n                &lt;div class=\"card-header\"&gt;\n\n                    &lt;h3 class=\"mb-0\"&gt;\n                        My Todo List\n                    &lt;\/h3&gt;\n\n                &lt;\/div&gt;\n\n                &lt;div class=\"card-body\"&gt;\n\n\n                    &lt;!-- ADD TODO --&gt;\n\n                    &lt;form\n                        th:action=\"@{\/todos}\"\n                        th:object=\"${todo}\"\n                        method=\"post\"\n                        class=\"mb-4\"&gt;\n\n                        &lt;div class=\"input-group\"&gt;\n\n                            &lt;input\n                                type=\"text\"\n                                th:field=\"*{title}\"\n                                class=\"form-control\"\n                                placeholder=\"Enter a new todo\"&gt;\n\n                            &lt;button\n                                type=\"submit\"\n                                class=\"btn btn-primary\"&gt;\n\n                                Add Todo\n\n                            &lt;\/button&gt;\n\n                        &lt;\/div&gt;\n\n                        &lt;div\n                            th:if=\"${#fields.hasErrors('title')}\"\n                            th:errors=\"*{title}\"\n                            class=\"text-danger mt-2\"&gt;\n\n                        &lt;\/div&gt;\n\n                    &lt;\/form&gt;\n\n\n                    &lt;!-- TODO TABLE --&gt;\n\n                    &lt;table class=\"table table-hover\"&gt;\n\n                        &lt;thead class=\"table-dark\"&gt;\n\n                        &lt;tr&gt;\n\n                            &lt;th&gt;ID&lt;\/th&gt;\n\n                            &lt;th&gt;Todo&lt;\/th&gt;\n\n                            &lt;th&gt;Status&lt;\/th&gt;\n\n                            &lt;th&gt;Actions&lt;\/th&gt;\n\n                        &lt;\/tr&gt;\n\n                        &lt;\/thead&gt;\n\n                        &lt;tbody&gt;\n\n                        &lt;tr th:each=\"todo : ${todos}\"&gt;\n\n                            &lt;td th:text=\"${todo.id}\"&gt;\n                                1\n                            &lt;\/td&gt;\n\n                            &lt;td&gt;\n\n                                &lt;span\n                                    th:text=\"${todo.title}\"\n                                    th:classappend=\"${todo.completed}\n                                    ? 'text-decoration-line-through text-muted'\n                                    : ''\"&gt;\n\n                                    Todo\n\n                                &lt;\/span&gt;\n\n                            &lt;\/td&gt;\n\n                            &lt;td&gt;\n\n                                &lt;span\n                                    th:if=\"${todo.completed}\"\n                                    class=\"badge bg-success\"&gt;\n\n                                    Completed\n\n                                &lt;\/span&gt;\n\n                                &lt;span\n                                    th:unless=\"${todo.completed}\"\n                                    class=\"badge bg-warning text-dark\"&gt;\n\n                                    Pending\n\n                                &lt;\/span&gt;\n\n                            &lt;\/td&gt;\n\n                            &lt;td&gt;\n\n                                &lt;a\n                                    th:href=\"@{\/todos\/toggle\/{id}(id=${todo.id})}\"\n                                    class=\"btn btn-sm btn-success\"&gt;\n\n                                    Toggle\n\n                                &lt;\/a&gt;\n\n                                &lt;a\n                                    th:href=\"@{\/todos\/edit\/{id}(id=${todo.id})}\"\n                                    class=\"btn btn-sm btn-primary\"&gt;\n\n                                    Edit\n\n                                &lt;\/a&gt;\n\n                                &lt;a\n                                    th:href=\"@{\/todos\/delete\/{id}(id=${todo.id})}\"\n                                    class=\"btn btn-sm btn-danger\"\n                                    onclick=\"return confirm('Delete this todo?');\"&gt;\n\n                                    Delete\n\n                                &lt;\/a&gt;\n\n                            &lt;\/td&gt;\n\n                        &lt;\/tr&gt;\n\n\n                        &lt;tr\n                            th:if=\"${#lists.isEmpty(todos)}\"&gt;\n\n                            &lt;td\n                                colspan=\"4\"\n                                class=\"text-center text-muted\"&gt;\n\n                                You don't have any todos yet.\n\n                            &lt;\/td&gt;\n\n                        &lt;\/tr&gt;\n\n                        &lt;\/tbody&gt;\n\n                    &lt;\/table&gt;\n\n                &lt;\/div&gt;\n\n            &lt;\/div&gt;\n\n        &lt;\/div&gt;\n\n    &lt;\/div&gt;\n\n&lt;\/div&gt;\n\n&lt;\/body&gt;\n\n&lt;\/html&gt;<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">19. Edit page<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Create:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>src\/main\/resources\/templates\/edit.html<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;!DOCTYPE html&gt;\n&lt;html lang=\"en\"\n      xmlns:th=\"http:\/\/www.thymeleaf.org\"&gt;\n\n&lt;head&gt;\n\n    &lt;meta charset=\"UTF-8\"&gt;\n\n    &lt;meta name=\"viewport\"\n          content=\"width=device-width, initial-scale=1\"&gt;\n\n    &lt;title&gt;Edit Todo&lt;\/title&gt;\n\n    &lt;link\n        href=\"https:\/\/cdn.jsdelivr.net\/npm\/bootstrap@5.3.3\/dist\/css\/bootstrap.min.css\"\n        rel=\"stylesheet\"&gt;\n\n&lt;\/head&gt;\n\n&lt;body class=\"bg-light\"&gt;\n\n&lt;div class=\"container mt-5\"&gt;\n\n    &lt;div class=\"row justify-content-center\"&gt;\n\n        &lt;div class=\"col-md-6\"&gt;\n\n            &lt;div class=\"card shadow\"&gt;\n\n                &lt;div class=\"card-header bg-primary text-white\"&gt;\n\n                    &lt;h3 class=\"mb-0\"&gt;\n                        Edit Todo\n                    &lt;\/h3&gt;\n\n                &lt;\/div&gt;\n\n                &lt;div class=\"card-body\"&gt;\n\n                    &lt;form\n                        th:action=\"@{\/todos\/update\/{id}(id=${todo.id})}\"\n                        th:object=\"${todo}\"\n                        method=\"post\"&gt;\n\n                        &lt;div class=\"mb-3\"&gt;\n\n                            &lt;label class=\"form-label\"&gt;\n                                Todo\n                            &lt;\/label&gt;\n\n                            &lt;input\n                                type=\"text\"\n                                th:field=\"*{title}\"\n                                class=\"form-control\"&gt;\n\n                            &lt;div\n                                th:if=\"${#fields.hasErrors('title')}\"\n                                th:errors=\"*{title}\"\n                                class=\"text-danger\"&gt;\n\n                            &lt;\/div&gt;\n\n                        &lt;\/div&gt;\n\n                        &lt;div class=\"form-check mb-3\"&gt;\n\n                            &lt;input\n                                type=\"checkbox\"\n                                th:field=\"*{completed}\"\n                                class=\"form-check-input\"&gt;\n\n                            &lt;label class=\"form-check-label\"&gt;\n                                Completed\n                            &lt;\/label&gt;\n\n                        &lt;\/div&gt;\n\n                        &lt;button\n                            type=\"submit\"\n                            class=\"btn btn-primary\"&gt;\n\n                            Update\n\n                        &lt;\/button&gt;\n\n                        &lt;a\n                            href=\"\/\"\n                            class=\"btn btn-secondary\"&gt;\n\n                            Cancel\n\n                        &lt;\/a&gt;\n\n                    &lt;\/form&gt;\n\n                &lt;\/div&gt;\n\n            &lt;\/div&gt;\n\n        &lt;\/div&gt;\n\n    &lt;\/div&gt;\n\n&lt;\/div&gt;\n\n&lt;\/body&gt;\n\n&lt;\/html&gt;<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">20. <code>application.properties<\/code><\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Use:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>spring.application.name=todo\n\n# ==========================================\n# MySQL\n# ==========================================\n\nspring.datasource.url=jdbc:mysql:\/\/localhost:3306\/todo_db\n\nspring.datasource.username=todo_user\n\nspring.datasource.password=todo_password\n\n\n# ==========================================\n# JPA \/ Hibernate\n# ==========================================\n\nspring.jpa.hibernate.ddl-auto=update\n\nspring.jpa.show-sql=true\n\nspring.jpa.properties.hibernate.format_sql=true\n\n\n# ==========================================\n# Thymeleaf\n# ==========================================\n\nspring.thymeleaf.cache=false\n\n\n# ==========================================\n# Server\n# ==========================================\n\nserver.port=8080<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">21. Update Maven project in Eclipse<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">After changing <code>pom.xml<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Right click project\n       \u2193\nMaven\n       \u2193\nUpdate Project\n       \u2193\nCheck your project\n       \u2193\nOK<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Or from terminal:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>.\/mvnw clean install<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Then:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>.\/mvnw spring-boot:run<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">22. Start the application<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Make sure MySQL is running:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo systemctl status mysql<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Then:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>.\/mvnw spring-boot:run<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Open:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>http:&#47;&#47;localhost:8080<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Because the application is secured, you&#8217;ll be redirected to:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/login<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">23. Register a user<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Open:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>http:&#47;&#47;localhost:8080\/register<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Create:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Username: devesh\nPassword: password123<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Click <strong>Register<\/strong>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">You&#8217;ll be redirected to login.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">24. Login<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Enter:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Username: devesh\nPassword: password123<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">After successful authentication:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/login\n   \u2193\nSpring Security\n   \u2193\n\/\n   \u2193\nTodo Application<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">You should see:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>+------------------------------------------------+\n| Todo Application              Welcome, devesh |\n|                                  &#91; Logout ]    |\n+------------------------------------------------+\n| My Todo List                                   |\n|                                                |\n| &#91; Learn Spring Boot             ] &#91;Add Todo]   |\n|                                                |\n| ID | Todo              | Status | Actions      |\n|----|-------------------|--------|--------------|\n| 1  | Learn Java        | Pending| Toggle Edit  |\n| 2  | Learn Spring Boot | Done   | Toggle Edit  |\n+------------------------------------------------+<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">25. Test multi-user security<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">This is an important part of the project.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Create:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>User 1:\nusername = devesh\npassword = password123<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Add:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Learn Java\nLearn Spring Boot\nLearn MySQL<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Logout.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Register:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>User 2:\nusername = rahul\npassword = password123<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Login as <code>rahul<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">You should see:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>My Todo List\n\nNo todos yet.<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Rahul <strong>cannot see Devesh&#8217;s todos<\/strong>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The reason is this repository method:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>findByIdAndUser(id, user)<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">and:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>findByUser(user)<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This is much safer than simply doing:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>findAll()<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">26. What happens to the password?<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Suppose the user registers:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>password123<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">We do:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>passwordEncoder.encode(password);<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The database does <strong>not<\/strong> store:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>password123<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Instead, it stores a BCrypt hash similar to:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$2a$10$...<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">You can inspect it:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>USE todo_db;\n\nSELECT id, username, password, role\nFROM users;<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">You&#8217;ll see something like:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>+----+----------+----------------------+------+\n| id | username | password             | role |\n+----+----------+----------------------+------+\n|  1 | devesh   | $2a$10$.............. | USER |\n+----+----------+----------------------+------+<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Never store real passwords directly in your database.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">27. Database relationship<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Hibernate creates approximately:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>users\n--------------------------------\nid          BIGINT PRIMARY KEY\nusername    VARCHAR\npassword    VARCHAR\nrole        VARCHAR<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">and:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>todos\n--------------------------------\nid          BIGINT PRIMARY KEY\ntitle       VARCHAR\ncompleted   BOOLEAN\nuser_id     BIGINT<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Relationship:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"> <code>       users\n          |\n          | 1\n          |\n          | *\n          v\n        todos<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">In Java:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>@OneToMany\nprivate List&lt;Todo&gt; todos;<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">and:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>@ManyToOne\nprivate User user;<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">28. REST API<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">The REST API is also protected.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>GET     \/api\/todos\nGET     \/api\/todos\/1\nPOST    \/api\/todos\nPUT     \/api\/todos\/1\nPATCH   \/api\/todos\/1\/toggle\nDELETE  \/api\/todos\/1<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">You need to be authenticated to access these endpoints.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>curl http:\/\/localhost:8080\/api\/todos<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Without authentication, Spring Security will reject the request.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">29. Test the API after login<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">For a browser-based form login, testing the REST API with <code>curl<\/code> is slightly different because the authentication is session-based.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For development, you can use Postman:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>POST \/login<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">with:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>username=devesh\npassword=password123<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Then retain the session cookie and call:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>GET \/api\/todos<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">For a production REST application, I would normally change this architecture to <strong>JWT authentication<\/strong> rather than using the browser session.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">30. Important Spring Security flow<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">The login flow is:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"> <code>                   Browser\n                       |\n                       | POST \/login\n                       v\n              Spring Security\n                       |\n                       v\n        CustomUserDetailsService\n                       |\n                       v\n                UserRepository\n                       |\n                       v\n                    MySQL\n                       |\n                       v\n             PasswordEncoder\n                       |\n                       v\n                 BCrypt check\n                       |\n              \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n              |                 |\n           SUCCESS            FAILURE\n              |                 |\n              v                 v\n             \"\/\"             \/login?error<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The important code is:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>@Service\npublic class CustomUserDetailsService\n        implements UserDetailsService<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Spring Security calls:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>loadUserByUsername()<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">which loads the user from MySQL.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Then BCrypt verifies the password.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">31. Important security rule<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Do <strong>not<\/strong> do this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>user.setPassword(password);<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Instead:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>user.setPassword(\n    passwordEncoder.encode(password)\n);<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">And don&#8217;t create a database column containing plain-text passwords.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">32. Run and verify<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Use these commands:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo systemctl start mysql<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>cd ~\/todo<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>.\/mvnw clean<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>.\/mvnw spring-boot:run<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Then:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>http:&#47;&#47;localhost:8080\/register<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Test this sequence:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Register\n   \u2193\nLogin\n   \u2193\nAdd Todo\n   \u2193\nEdit Todo\n   \u2193\nToggle Todo\n   \u2193\nDelete Todo\n   \u2193\nLogout<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Then create a second account and verify that the first user&#8217;s todos aren&#8217;t visible.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Final architecture<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Your application now looks like:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"> <code>                    \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n                     \u2502    Browser    \u2502\n                     \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n                             \u2502\n                             \u25bc\n                  \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n                  \u2502   Spring Security  \u2502\n                  \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n                            \u2502\n                 \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n                 \u2502                     \u2502\n                 \u25bc                     \u25bc\n             Login\/Auth            TodoController\n                 \u2502                     \u2502\n                 \u25bc                     \u25bc\n             UserService          TodoService\n                 \u2502                     \u2502\n                 \u25bc                     \u25bc\n           UserRepository       TodoRepository\n                 \u2502                     \u2502\n                 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n                            \u2502\n                            \u25bc\n                         MySQL<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This is now a solid <strong>Spring Boot MVC + Security + JPA + MySQL CRUD application<\/strong>. The natural next production-level extension is <strong>role-based authorization (<code>USER<\/code>\/<code>ADMIN<\/code>), admin user management, BCrypt password validation, CSRF-safe delete forms, and JWT-based REST authentication<\/strong>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Yes. The next extension should add user registration + login\/logout + Spring Security + per-user Todo lists. The architecture becomes: I recommend using Spring Security 6 \/ Spring Boot 3.x, with passwords stored using BCrypt, never as plain text. I&hellip;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"om_disable_all_campaigns":false,"_monsterinsights_skip_tracking":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[1],"tags":[],"class_list":["post-29","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.3 - aioseo.com -->\n\t<meta name=\"description\" content=\"Yes. The next extension should add user registration + login\/logout + Spring Security + per-user Todo lists. The architecture becomes: Browser \u2502 \u25bc Spring Security \u2502 \u251c\u2500\u2500 Login \u251c\u2500\u2500 Logout \u2514\u2500\u2500 Authentication \u2502 \u25bc Todo Controller \u2502 \u25bc Todo Service \u2502 \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u25bc \u25bc UserRepository TodoRepository \u2502 \u2502 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u25bc MySQL I recommend using Spring\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"DEVESH\"\/>\n\t<link rel=\"canonical\" href=\"https:\/\/boot-code.com\/?p=29\" \/>\n\t\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.3\" \/>\n\t\t<meta property=\"og:locale\" content=\"en_US\" \/>\n\t\t<meta property=\"og:site_name\" content=\"boot-code.com - programming tutorials\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"Spring Todo Web Application Login Security Tutorial Extension - boot-code.com\" \/>\n\t\t<meta property=\"og:description\" content=\"Yes. The next extension should add user registration + login\/logout + Spring Security + per-user Todo lists. The architecture becomes: Browser \u2502 \u25bc Spring Security \u2502 \u251c\u2500\u2500 Login \u251c\u2500\u2500 Logout \u2514\u2500\u2500 Authentication \u2502 \u25bc Todo Controller \u2502 \u25bc Todo Service \u2502 \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u25bc \u25bc UserRepository TodoRepository \u2502 \u2502 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u25bc MySQL I recommend using Spring\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/boot-code.com\/?p=29\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2026-10-08T16:30:50+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2026-10-08T16:30:52+00:00\" \/>\n\t\t<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n\t\t<meta name=\"twitter:title\" content=\"Spring Todo Web Application Login Security Tutorial Extension - boot-code.com\" \/>\n\t\t<meta name=\"twitter:description\" content=\"Yes. The next extension should add user registration + login\/logout + Spring Security + per-user Todo lists. The architecture becomes: Browser \u2502 \u25bc Spring Security \u2502 \u251c\u2500\u2500 Login \u251c\u2500\u2500 Logout \u2514\u2500\u2500 Authentication \u2502 \u25bc Todo Controller \u2502 \u25bc Todo Service \u2502 \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u25bc \u25bc UserRepository TodoRepository \u2502 \u2502 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u25bc MySQL I recommend using Spring\" \/>\n\t\t<script type=\"application\/ld+json\" class=\"aioseo-schema\">\n\t\t\t{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"BlogPosting\",\"@id\":\"https:\\\/\\\/boot-code.com\\\/?p=29#blogposting\",\"name\":\"Spring Todo Web Application Login Security Tutorial Extension - boot-code.com\",\"headline\":\"Spring Todo Web Application Login Security Tutorial Extension\",\"author\":{\"@id\":\"https:\\\/\\\/boot-code.com\\\/?author=1#author\"},\"publisher\":{\"@id\":\"https:\\\/\\\/boot-code.com\\\/#organization\"},\"datePublished\":\"2026-10-08T16:30:50+00:00\",\"dateModified\":\"2026-10-08T16:30:52+00:00\",\"inLanguage\":\"en-US\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/boot-code.com\\\/?p=29#webpage\"},\"isPartOf\":{\"@id\":\"https:\\\/\\\/boot-code.com\\\/?p=29#webpage\"},\"articleSection\":\"Uncategorized\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/boot-code.com\\\/?p=29#breadcrumblist\",\"itemListElement\":[{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/boot-code.com\\\/#listItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/boot-code.com\\\/\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/boot-code.com\\\/?cat=1#listItem\",\"name\":\"Uncategorized\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/boot-code.com\\\/?cat=1#listItem\",\"position\":2,\"name\":\"Uncategorized\",\"item\":\"https:\\\/\\\/boot-code.com\\\/?cat=1\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/boot-code.com\\\/?p=29#listItem\",\"name\":\"Spring Todo Web Application Login Security Tutorial Extension\"},\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/boot-code.com\\\/#listItem\",\"name\":\"Home\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/boot-code.com\\\/?p=29#listItem\",\"position\":3,\"name\":\"Spring Todo Web Application Login Security Tutorial Extension\",\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/boot-code.com\\\/?cat=1#listItem\",\"name\":\"Uncategorized\"}}]},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/boot-code.com\\\/#organization\",\"name\":\"boot-code.com boot-code.com\",\"description\":\"programming tutorials programming tutorials\",\"url\":\"https:\\\/\\\/boot-code.com\\\/\",\"telephone\":\"+919259571202\"},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/boot-code.com\\\/?author=1#author\",\"url\":\"https:\\\/\\\/boot-code.com\\\/?author=1\",\"name\":\"DEVESH\",\"image\":{\"@type\":\"ImageObject\",\"@id\":\"https:\\\/\\\/boot-code.com\\\/?p=29#authorImage\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/b4c9a289323b21a01c3e940f150eb9b8c542587f1abfd8f0e1cc1ffc5e475514?s=96&d=mm&r=g\",\"width\":96,\"height\":96,\"caption\":\"DEVESH\"}},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/boot-code.com\\\/?p=29#webpage\",\"url\":\"https:\\\/\\\/boot-code.com\\\/?p=29\",\"name\":\"Spring Todo Web Application Login Security Tutorial Extension - boot-code.com\",\"description\":\"Yes. The next extension should add user registration + login\\\/logout + Spring Security + per-user Todo lists. The architecture becomes: Browser \\u2502 \\u25bc Spring Security \\u2502 \\u251c\\u2500\\u2500 Login \\u251c\\u2500\\u2500 Logout \\u2514\\u2500\\u2500 Authentication \\u2502 \\u25bc Todo Controller \\u2502 \\u25bc Todo Service \\u2502 \\u251c\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2510 \\u25bc \\u25bc UserRepository TodoRepository \\u2502 \\u2502 \\u2514\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u252c\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2500\\u2518 \\u25bc MySQL I recommend using Spring\",\"inLanguage\":\"en-US\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/boot-code.com\\\/#website\"},\"breadcrumb\":{\"@id\":\"https:\\\/\\\/boot-code.com\\\/?p=29#breadcrumblist\"},\"author\":{\"@id\":\"https:\\\/\\\/boot-code.com\\\/?author=1#author\"},\"creator\":{\"@id\":\"https:\\\/\\\/boot-code.com\\\/?author=1#author\"},\"datePublished\":\"2026-10-08T16:30:50+00:00\",\"dateModified\":\"2026-10-08T16:30:52+00:00\"},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/boot-code.com\\\/#website\",\"url\":\"https:\\\/\\\/boot-code.com\\\/\",\"name\":\"boot-code.com\",\"description\":\"programming tutorials\",\"inLanguage\":\"en-US\",\"publisher\":{\"@id\":\"https:\\\/\\\/boot-code.com\\\/#organization\"}}]}\n\t\t<\/script>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"Spring Todo Web Application Login Security Tutorial Extension - boot-code.com","description":"Yes. The next extension should add user registration + login\/logout + Spring Security + per-user Todo lists. The architecture becomes: Browser \u2502 \u25bc Spring Security \u2502 \u251c\u2500\u2500 Login \u251c\u2500\u2500 Logout \u2514\u2500\u2500 Authentication \u2502 \u25bc Todo Controller \u2502 \u25bc Todo Service \u2502 \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u25bc \u25bc UserRepository TodoRepository \u2502 \u2502 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u25bc MySQL I recommend using Spring","canonical_url":"https:\/\/boot-code.com\/?p=29","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"BlogPosting","@id":"https:\/\/boot-code.com\/?p=29#blogposting","name":"Spring Todo Web Application Login Security Tutorial Extension - boot-code.com","headline":"Spring Todo Web Application Login Security Tutorial Extension","author":{"@id":"https:\/\/boot-code.com\/?author=1#author"},"publisher":{"@id":"https:\/\/boot-code.com\/#organization"},"datePublished":"2026-10-08T16:30:50+00:00","dateModified":"2026-10-08T16:30:52+00:00","inLanguage":"en-US","mainEntityOfPage":{"@id":"https:\/\/boot-code.com\/?p=29#webpage"},"isPartOf":{"@id":"https:\/\/boot-code.com\/?p=29#webpage"},"articleSection":"Uncategorized"},{"@type":"BreadcrumbList","@id":"https:\/\/boot-code.com\/?p=29#breadcrumblist","itemListElement":[{"@type":"ListItem","@id":"https:\/\/boot-code.com\/#listItem","position":1,"name":"Home","item":"https:\/\/boot-code.com\/","nextItem":{"@type":"ListItem","@id":"https:\/\/boot-code.com\/?cat=1#listItem","name":"Uncategorized"}},{"@type":"ListItem","@id":"https:\/\/boot-code.com\/?cat=1#listItem","position":2,"name":"Uncategorized","item":"https:\/\/boot-code.com\/?cat=1","nextItem":{"@type":"ListItem","@id":"https:\/\/boot-code.com\/?p=29#listItem","name":"Spring Todo Web Application Login Security Tutorial Extension"},"previousItem":{"@type":"ListItem","@id":"https:\/\/boot-code.com\/#listItem","name":"Home"}},{"@type":"ListItem","@id":"https:\/\/boot-code.com\/?p=29#listItem","position":3,"name":"Spring Todo Web Application Login Security Tutorial Extension","previousItem":{"@type":"ListItem","@id":"https:\/\/boot-code.com\/?cat=1#listItem","name":"Uncategorized"}}]},{"@type":"Organization","@id":"https:\/\/boot-code.com\/#organization","name":"boot-code.com boot-code.com","description":"programming tutorials programming tutorials","url":"https:\/\/boot-code.com\/","telephone":"+919259571202"},{"@type":"Person","@id":"https:\/\/boot-code.com\/?author=1#author","url":"https:\/\/boot-code.com\/?author=1","name":"DEVESH","image":{"@type":"ImageObject","@id":"https:\/\/boot-code.com\/?p=29#authorImage","url":"https:\/\/secure.gravatar.com\/avatar\/b4c9a289323b21a01c3e940f150eb9b8c542587f1abfd8f0e1cc1ffc5e475514?s=96&d=mm&r=g","width":96,"height":96,"caption":"DEVESH"}},{"@type":"WebPage","@id":"https:\/\/boot-code.com\/?p=29#webpage","url":"https:\/\/boot-code.com\/?p=29","name":"Spring Todo Web Application Login Security Tutorial Extension - boot-code.com","description":"Yes. The next extension should add user registration + login\/logout + Spring Security + per-user Todo lists. The architecture becomes: Browser \u2502 \u25bc Spring Security \u2502 \u251c\u2500\u2500 Login \u251c\u2500\u2500 Logout \u2514\u2500\u2500 Authentication \u2502 \u25bc Todo Controller \u2502 \u25bc Todo Service \u2502 \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u25bc \u25bc UserRepository TodoRepository \u2502 \u2502 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u25bc MySQL I recommend using Spring","inLanguage":"en-US","isPartOf":{"@id":"https:\/\/boot-code.com\/#website"},"breadcrumb":{"@id":"https:\/\/boot-code.com\/?p=29#breadcrumblist"},"author":{"@id":"https:\/\/boot-code.com\/?author=1#author"},"creator":{"@id":"https:\/\/boot-code.com\/?author=1#author"},"datePublished":"2026-10-08T16:30:50+00:00","dateModified":"2026-10-08T16:30:52+00:00"},{"@type":"WebSite","@id":"https:\/\/boot-code.com\/#website","url":"https:\/\/boot-code.com\/","name":"boot-code.com","description":"programming tutorials","inLanguage":"en-US","publisher":{"@id":"https:\/\/boot-code.com\/#organization"}}]},"og:locale":"en_US","og:site_name":"boot-code.com - programming tutorials","og:type":"article","og:title":"Spring Todo Web Application Login Security Tutorial Extension - boot-code.com","og:description":"Yes. The next extension should add user registration + login\/logout + Spring Security + per-user Todo lists. The architecture becomes: Browser \u2502 \u25bc Spring Security \u2502 \u251c\u2500\u2500 Login \u251c\u2500\u2500 Logout \u2514\u2500\u2500 Authentication \u2502 \u25bc Todo Controller \u2502 \u25bc Todo Service \u2502 \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u25bc \u25bc UserRepository TodoRepository \u2502 \u2502 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u25bc MySQL I recommend using Spring","og:url":"https:\/\/boot-code.com\/?p=29","article:published_time":"2026-10-08T16:30:50+00:00","article:modified_time":"2026-10-08T16:30:52+00:00","twitter:card":"summary_large_image","twitter:title":"Spring Todo Web Application Login Security Tutorial Extension - boot-code.com","twitter:description":"Yes. The next extension should add user registration + login\/logout + Spring Security + per-user Todo lists. The architecture becomes: Browser \u2502 \u25bc Spring Security \u2502 \u251c\u2500\u2500 Login \u251c\u2500\u2500 Logout \u2514\u2500\u2500 Authentication \u2502 \u25bc Todo Controller \u2502 \u25bc Todo Service \u2502 \u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u25bc \u25bc UserRepository TodoRepository \u2502 \u2502 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u25bc MySQL I recommend using Spring"},"aioseo_meta_data":{"post_id":"29","title":null,"description":null,"keywords":null,"keyphrases":{"focus":{"keyphrase":"","score":0,"analysis":{"keyphraseInTitle":{"score":0,"maxScore":9,"error":1}}},"additional":[]},"focus_keyword":null,"additional_keywords":null,"truseo_locale":null,"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_custom_url":null,"og_image_custom_fields":null,"og_image_url":null,"og_image_width":null,"og_image_height":null,"og_video":"","og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_image_url":null,"twitter_title":null,"twitter_description":null,"schema_type":"default","schema_type_options":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"BlogPosting","isEnabled":true},"graphs":[]},"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":"-1","robots_max_videopreview":"-1","robots_max_imagepreview":"large","priority":null,"frequency":"default","local_seo":null,"limit_modified_date":false,"ai":{"faqs":[],"keyPoints":[],"schemas":[],"titles":[],"descriptions":[],"socialPosts":{"email":{"subject":"","preview":"","content":""},"linkedin":[],"twitter":[],"facebook":[],"instagram":[]}},"breadcrumb_settings":null,"seo_analyzer_scan_date":null,"created":"2026-10-08 16:30:25","updated":"2026-10-08 16:46:50"},"aioseo_breadcrumb":"<div class=\"aioseo-breadcrumbs\"><span class=\"aioseo-breadcrumb\">\n\t\t\t<a href=\"https:\/\/boot-code.com\/\" title=\"Home\">Home<\/a>\n\t\t<\/span><span class=\"aioseo-breadcrumb-separator\">\u00bb<\/span><span class=\"aioseo-breadcrumb\">\n\t\t\t<a href=\"https:\/\/boot-code.com\/?cat=1\" title=\"Uncategorized\">Uncategorized<\/a>\n\t\t<\/span><span class=\"aioseo-breadcrumb-separator\">\u00bb<\/span><span class=\"aioseo-breadcrumb\">\n\t\t\tSpring Todo Web Application Login Security Tutorial Extension\n\t\t<\/span><\/div>","aioseo_breadcrumb_json":[{"label":"Home","link":"https:\/\/boot-code.com\/"},{"label":"Uncategorized","link":"https:\/\/boot-code.com\/?cat=1"},{"label":"Spring Todo Web Application Login Security Tutorial Extension","link":"https:\/\/boot-code.com\/?p=29"}],"jetpack_sharing_enabled":true,"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/boot-code.com\/index.php?rest_route=\/wp\/v2\/posts\/29","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/boot-code.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/boot-code.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/boot-code.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/boot-code.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=29"}],"version-history":[{"count":1,"href":"https:\/\/boot-code.com\/index.php?rest_route=\/wp\/v2\/posts\/29\/revisions"}],"predecessor-version":[{"id":30,"href":"https:\/\/boot-code.com\/index.php?rest_route=\/wp\/v2\/posts\/29\/revisions\/30"}],"wp:attachment":[{"href":"https:\/\/boot-code.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=29"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/boot-code.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=29"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/boot-code.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=29"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}