Skip to content

Commit c46ffa4

Browse files
committed
Add 'Remember Me' feature and corresponding tests
1 parent 12d0891 commit c46ffa4

File tree

5 files changed

+70
-6
lines changed

5 files changed

+70
-6
lines changed

build_and_run_app.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@ cleanup() {
1919
}
2020

2121
# Trap the EXIT signal to perform cleanup
22-
trap cleanup EXIT
22+
# trap cleanup EXIT
2323

2424
set -e # Exit immediately if a command exits with a non-zero status.
2525
mvn clean package -Dmaven.test.skip=true
26-
docker run -d -p 6379:6379 --name redis_container redis
26+
docker run -d -p 6379:6379 --name redis_container redis | exit 0
2727
java $SEARCH_FEATURE -jar target/salesmanager-*-SNAPSHOT.jar --spring.redis.host=localhost --spring.redis.port=6379 --spring.redis.mode=standalone --server.port=8086 --spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.H2Dialect

src/main/java/net/codejava/AppController.java

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
import java.time.format.DateTimeFormatter;
5050
import java.time.LocalDateTime;
5151
import java.time.ZoneId;
52+
import javax.servlet.http.Cookie;
5253

5354
@EnableJpaRepositories(basePackages = "net.codejava")
5455
@Controller
@@ -165,21 +166,27 @@ public String loginGet(Model model) {
165166
}
166167

167168
@RequestMapping(value = "/login", method = RequestMethod.POST)
168-
public String loginPost(HttpServletRequest request, Model model) {
169+
public String loginPost(HttpServletRequest request, HttpServletResponse response, Model model) {
169170
String username = request.getParameter("username");
170171
String password = request.getParameter("password");
172+
boolean rememberMe = "on".equals(request.getParameter("rememberMe"));
171173

172-
// Authenticate the user
173174
Authentication auth = new UsernamePasswordAuthenticationToken(username, password);
174175
try {
175176
auth = authenticationManager.authenticate(auth);
176177
SecurityContextHolder.getContext().setAuthentication(auth);
178+
179+
if (rememberMe) {
180+
Cookie rememberMeCookie = new Cookie("rememberMe", "true");
181+
rememberMeCookie.setMaxAge(7 * 24 * 60 * 60); // 7 days
182+
rememberMeCookie.setHttpOnly(true);
183+
response.addCookie(rememberMeCookie);
184+
}
177185
} catch (BadCredentialsException e) {
178186
model.addAttribute("error", "Invalid username or password.");
179187
return "login";
180188
}
181189

182-
// User is authenticated, redirect to landing page
183190
return "redirect:/";
184191
}
185192

src/main/java/net/codejava/SalesDAO.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@ public List<Sale> list(int limit, int offset) {
3030
return listSale;
3131
}
3232

33+
public List<Sale> listAll() {
34+
String sql = "SELECT * FROM sales ORDER BY serial_number ASC";
35+
return jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(Sale.class));
36+
}
37+
3338
public void save(Sale sale) throws DuplicateKeyException {
3439
try {
3540
System.out.println(sale); // log the Sale object

src/main/java/net/codejava/SecurityConfig.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,11 @@ protected void configure(HttpSecurity http) throws Exception {
4444
.logout()
4545
.logoutUrl("/logout") // This is the URL to send the user to once they have logged out
4646
.invalidateHttpSession(true)
47-
.permitAll();
47+
.permitAll()
48+
.and()
49+
.rememberMe()
50+
.key("uniqueAndSecret")
51+
.tokenValiditySeconds(7 * 24 * 60 * 60); // 7 days
4852
}
4953

5054
@Bean
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package net.codejava;
2+
3+
import org.junit.jupiter.api.Test;
4+
import org.springframework.beans.factory.annotation.Autowired;
5+
import org.springframework.boot.test.context.SpringBootTest;
6+
import org.springframework.mock.web.MockHttpServletRequest;
7+
import org.springframework.mock.web.MockHttpServletResponse;
8+
import org.springframework.security.core.context.SecurityContextHolder;
9+
import org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices;
10+
11+
import javax.servlet.http.Cookie;
12+
13+
import static org.junit.jupiter.api.Assertions.*;
14+
15+
@SpringBootTest
16+
public class AppControllerTest {
17+
18+
@Autowired
19+
private AppController appController;
20+
21+
@Autowired
22+
private PersistentTokenBasedRememberMeServices rememberMeServices;
23+
24+
@Test
25+
public void testRememberMeFunctionality() throws Exception {
26+
MockHttpServletRequest request = new MockHttpServletRequest();
27+
MockHttpServletResponse response = new MockHttpServletResponse();
28+
29+
// Simulate login request with 'Remember Me' checked
30+
request.setParameter("username", "testuser");
31+
request.setParameter("password", "testpassword");
32+
request.setParameter("rememberMe", "on");
33+
34+
String view = appController.loginPost(request, response, null);
35+
36+
// Assert that the user is redirected to the home page
37+
assertEquals("redirect:/", view);
38+
39+
// Assert that the 'Remember Me' cookie is set
40+
Cookie rememberMeCookie = response.getCookie("rememberMe");
41+
assertNotNull(rememberMeCookie);
42+
assertEquals("true", rememberMeCookie.getValue());
43+
assertTrue(rememberMeCookie.getMaxAge() > 0);
44+
45+
// Assert that the security context is populated
46+
assertNotNull(SecurityContextHolder.getContext().getAuthentication());
47+
}
48+
}

0 commit comments

Comments
 (0)