Code review fixes
This commit is contained in:
parent
c1b13d0931
commit
07b5c1f010
@ -4,10 +4,10 @@ import org.springframework.boot.SpringApplication;
|
|||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
public class LogoutDemoApplication {
|
public class CustomLogoutApplication {
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
SpringApplication.run(LogoutDemoApplication.class, args);
|
SpringApplication.run(CustomLogoutApplication.class, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -1,6 +1,7 @@
|
|||||||
package com.baeldung.customlogouthandler;
|
package com.baeldung.customlogouthandler;
|
||||||
|
|
||||||
import com.baeldung.customlogouthandler.web.CustomLogoutHandler;
|
import javax.sql.DataSource;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.http.HttpMethod;
|
import org.springframework.http.HttpMethod;
|
||||||
@ -11,7 +12,7 @@ import org.springframework.security.config.annotation.web.configuration.EnableWe
|
|||||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||||
import org.springframework.security.web.authentication.logout.HttpStatusReturningLogoutSuccessHandler;
|
import org.springframework.security.web.authentication.logout.HttpStatusReturningLogoutSuccessHandler;
|
||||||
|
|
||||||
import javax.sql.DataSource;
|
import com.baeldung.customlogouthandler.web.CustomLogoutHandler;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableWebSecurity
|
@EnableWebSecurity
|
||||||
@ -25,27 +26,30 @@ public class MvcConfiguration extends WebSecurityConfigurerAdapter {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void configure(HttpSecurity http) throws Exception {
|
protected void configure(HttpSecurity http) throws Exception {
|
||||||
http
|
http.httpBasic()
|
||||||
.httpBasic().and()
|
.and()
|
||||||
.authorizeRequests()
|
.authorizeRequests()
|
||||||
.antMatchers(HttpMethod.GET, "/user/**").hasRole("USER")
|
.antMatchers(HttpMethod.GET, "/user/**")
|
||||||
.and()
|
.hasRole("USER")
|
||||||
.logout()
|
.and()
|
||||||
|
.logout()
|
||||||
.logoutUrl("/user/logout")
|
.logoutUrl("/user/logout")
|
||||||
.addLogoutHandler(logoutHandler)
|
.addLogoutHandler(logoutHandler)
|
||||||
.logoutSuccessHandler((new HttpStatusReturningLogoutSuccessHandler(HttpStatus.OK)))
|
.logoutSuccessHandler((new HttpStatusReturningLogoutSuccessHandler(HttpStatus.OK)))
|
||||||
.permitAll()
|
.permitAll()
|
||||||
.and()
|
.and()
|
||||||
.csrf().disable()
|
.csrf()
|
||||||
.formLogin().disable();
|
.disable()
|
||||||
|
.formLogin()
|
||||||
|
.disable();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
|
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
|
||||||
auth.jdbcAuthentication()
|
auth.jdbcAuthentication()
|
||||||
.dataSource(dataSource)
|
.dataSource(dataSource)
|
||||||
.usersByUsernameQuery("select login, password, true from users where login=?")
|
.usersByUsernameQuery("select login, password, true from users where login=?")
|
||||||
.authoritiesByUsernameQuery("select login, role from users where login=?");
|
.authoritiesByUsernameQuery("select login, role from users where login=?");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
package com.baeldung.customlogouthandler.services;
|
package com.baeldung.customlogouthandler.services;
|
||||||
|
|
||||||
import com.baeldung.customlogouthandler.user.User;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import org.springframework.stereotype.Service;
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
|
||||||
import javax.persistence.EntityManager;
|
import javax.persistence.EntityManager;
|
||||||
import javax.persistence.PersistenceContext;
|
import javax.persistence.PersistenceContext;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
import java.util.concurrent.ConcurrentMap;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import com.baeldung.customlogouthandler.user.User;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class UserCache {
|
public class UserCache {
|
||||||
@ -16,18 +18,18 @@ public class UserCache {
|
|||||||
|
|
||||||
private final ConcurrentMap<String, User> store = new ConcurrentHashMap<>(256);
|
private final ConcurrentMap<String, User> store = new ConcurrentHashMap<>(256);
|
||||||
|
|
||||||
public User getByLogin(String login) {
|
public User getByUserName(String userName) {
|
||||||
return store.computeIfAbsent(login, k -> entityManager.createQuery("from User where login=:login", User.class)
|
return store.computeIfAbsent(userName, k -> entityManager.createQuery("from User where login=:login", User.class)
|
||||||
.setParameter("login", k)
|
.setParameter("login", k)
|
||||||
.getSingleResult());
|
.getSingleResult());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void evictUser(String login) {
|
public void evictUser(String userName) {
|
||||||
store.remove(login);
|
store.remove(userName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int size() {
|
public int size() {
|
||||||
return this.store.size();
|
return store.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -5,8 +5,9 @@ import org.springframework.security.core.context.SecurityContextHolder;
|
|||||||
|
|
||||||
public class UserUtils {
|
public class UserUtils {
|
||||||
|
|
||||||
public static String getAuthenticatedUserLogin() {
|
public static String getAuthenticatedUserName() {
|
||||||
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
|
Authentication auth = SecurityContextHolder.getContext()
|
||||||
|
.getAuthentication();
|
||||||
return auth != null ? ((org.springframework.security.core.userdetails.User) auth.getPrincipal()).getUsername() : null;
|
return auth != null ? ((org.springframework.security.core.userdetails.User) auth.getPrincipal()).getUsername() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
package com.baeldung.customlogouthandler.web;
|
package com.baeldung.customlogouthandler.web;
|
||||||
|
|
||||||
import com.baeldung.customlogouthandler.services.UserCache;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import com.baeldung.customlogouthandler.user.UserUtils;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.web.authentication.logout.LogoutHandler;
|
import org.springframework.security.web.authentication.logout.LogoutHandler;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import com.baeldung.customlogouthandler.services.UserCache;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import com.baeldung.customlogouthandler.user.UserUtils;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class CustomLogoutHandler implements LogoutHandler {
|
public class CustomLogoutHandler implements LogoutHandler {
|
||||||
@ -20,8 +21,8 @@ public class CustomLogoutHandler implements LogoutHandler {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
|
public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
|
||||||
String login = UserUtils.getAuthenticatedUserLogin();
|
String userName = UserUtils.getAuthenticatedUserName();
|
||||||
userCache.evictUser(login);
|
userCache.evictUser(userName);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
package com.baeldung.customlogouthandler.web;
|
package com.baeldung.customlogouthandler.web;
|
||||||
|
|
||||||
import com.baeldung.customlogouthandler.services.UserCache;
|
|
||||||
import com.baeldung.customlogouthandler.user.User;
|
|
||||||
import com.baeldung.customlogouthandler.user.UserUtils;
|
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.ResponseBody;
|
import org.springframework.web.bind.annotation.ResponseBody;
|
||||||
|
|
||||||
|
import com.baeldung.customlogouthandler.services.UserCache;
|
||||||
|
import com.baeldung.customlogouthandler.user.User;
|
||||||
|
import com.baeldung.customlogouthandler.user.UserUtils;
|
||||||
|
|
||||||
@Controller
|
@Controller
|
||||||
@RequestMapping(path = "/user")
|
@RequestMapping(path = "/user")
|
||||||
@ -22,8 +22,8 @@ public class UserController {
|
|||||||
@GetMapping(path = "/language")
|
@GetMapping(path = "/language")
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
public String getLanguage() {
|
public String getLanguage() {
|
||||||
String login = UserUtils.getAuthenticatedUserLogin();
|
String userName = UserUtils.getAuthenticatedUserName();
|
||||||
User user = userCache.getByLogin(login);
|
User user = userCache.getByUserName(userName);
|
||||||
return user.getLanguage();
|
return user.getLanguage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package com.baeldung.customlogouthandler;
|
package com.baeldung.customlogouthandler;
|
||||||
|
|
||||||
import com.baeldung.customlogouthandler.services.UserCache;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
@ -11,96 +12,97 @@ import org.springframework.http.HttpEntity;
|
|||||||
import org.springframework.http.HttpHeaders;
|
import org.springframework.http.HttpHeaders;
|
||||||
import org.springframework.http.HttpMethod;
|
import org.springframework.http.HttpMethod;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.test.context.TestPropertySource;
|
||||||
import org.springframework.test.context.jdbc.Sql;
|
import org.springframework.test.context.jdbc.Sql;
|
||||||
import org.springframework.test.context.jdbc.SqlGroup;
|
import org.springframework.test.context.jdbc.SqlGroup;
|
||||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import com.baeldung.customlogouthandler.services.UserCache;
|
||||||
|
|
||||||
@RunWith(SpringJUnit4ClassRunner.class)
|
@RunWith(SpringJUnit4ClassRunner.class)
|
||||||
@SpringBootTest(classes = {LogoutDemoApplication.class, MvcConfiguration.class}, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
@SpringBootTest(classes = { CustomLogoutApplication.class }, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||||
@SqlGroup({
|
@SqlGroup({ @Sql(value = "classpath:customlogouthandler/before.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD), @Sql(value = "classpath:customlogouthandler/after.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD) })
|
||||||
@Sql(value = "classpath:customlogouthandler/before.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD),
|
@TestPropertySource(locations="classpath:customlogouthandler/application.properties")
|
||||||
@Sql(value = "classpath:customlogouthandler/after.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
|
|
||||||
})
|
|
||||||
class CustomLogoutHandlerIntegrationTest {
|
class CustomLogoutHandlerIntegrationTest {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private TestRestTemplate restTemplate;
|
private TestRestTemplate restTemplate;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private UserCache userCache;
|
private UserCache userCache;
|
||||||
|
|
||||||
@LocalServerPort
|
@LocalServerPort
|
||||||
private int port;
|
private int port;
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void whenLogin_thenUseUserCache() throws Exception {
|
public void whenLogin_thenUseUserCache() {
|
||||||
// User cache should be empty on start
|
// User cache should be empty on start
|
||||||
assertThat(userCache.size()).isEqualTo(0);
|
assertThat(userCache.size()).isEqualTo(0);
|
||||||
|
|
||||||
// Request using first login
|
// Request using first login
|
||||||
ResponseEntity<String> response = restTemplate
|
ResponseEntity<String> response = restTemplate.withBasicAuth("user", "pass")
|
||||||
.withBasicAuth("user", "pass")
|
.getForEntity(getLanguageUrl(), String.class);
|
||||||
.getForEntity(getLanguageUrl(), String.class);
|
|
||||||
|
|
||||||
assertThat(response.getBody()).contains("english");
|
assertThat(response.getBody()).contains("english");
|
||||||
|
|
||||||
// User cache must contain the user
|
// User cache must contain the user
|
||||||
assertThat(userCache.size()).isEqualTo(1);
|
assertThat(userCache.size()).isEqualTo(1);
|
||||||
|
|
||||||
// Getting the session cookie
|
// Getting the session cookie
|
||||||
HttpHeaders requestHeaders = new HttpHeaders();
|
HttpHeaders requestHeaders = new HttpHeaders();
|
||||||
requestHeaders.add("Cookie", response.getHeaders().getFirst(HttpHeaders.SET_COOKIE));
|
requestHeaders.add("Cookie", response.getHeaders()
|
||||||
|
.getFirst(HttpHeaders.SET_COOKIE));
|
||||||
|
|
||||||
// Request with the session cookie
|
// Request with the session cookie
|
||||||
response = restTemplate
|
response = restTemplate.exchange(getLanguageUrl(), HttpMethod.GET, new HttpEntity<String>(requestHeaders), String.class);
|
||||||
.exchange(getLanguageUrl(), HttpMethod.GET, new HttpEntity(requestHeaders), String.class);
|
assertThat(response.getBody()).contains("english");
|
||||||
assertThat(response.getBody()).contains("english");
|
|
||||||
|
|
||||||
// Logging out using the session cookies
|
// Logging out using the session cookies
|
||||||
response = restTemplate.exchange(getLogoutUrl(), HttpMethod.GET, new HttpEntity(requestHeaders), String.class);
|
response = restTemplate.exchange(getLogoutUrl(), HttpMethod.GET, new HttpEntity<String>(requestHeaders), String.class);
|
||||||
assertThat(response.getStatusCode().value()).isEqualTo(200);
|
assertThat(response.getStatusCode()
|
||||||
}
|
.value()).isEqualTo(200);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void whenLogout_thenCacheIsEmpty() throws Exception {
|
public void whenLogout_thenCacheIsEmpty() {
|
||||||
// User cache should be empty on start
|
// User cache should be empty on start
|
||||||
assertThat(userCache.size()).isEqualTo(0);
|
assertThat(userCache.size()).isEqualTo(0);
|
||||||
|
|
||||||
// Request using first login
|
// Request using first login
|
||||||
ResponseEntity<String> response = restTemplate
|
ResponseEntity<String> response = restTemplate.withBasicAuth("user", "pass")
|
||||||
.withBasicAuth("user", "pass")
|
.getForEntity(getLanguageUrl(), String.class);
|
||||||
.getForEntity(getLanguageUrl(), String.class);
|
|
||||||
|
|
||||||
assertThat(response.getBody()).contains("english");
|
assertThat(response.getBody()).contains("english");
|
||||||
|
|
||||||
// User cache must contain the user
|
// User cache must contain the user
|
||||||
assertThat(userCache.size()).isEqualTo(1);
|
assertThat(userCache.size()).isEqualTo(1);
|
||||||
|
|
||||||
// Getting the session cookie
|
// Getting the session cookie
|
||||||
HttpHeaders requestHeaders = new HttpHeaders();
|
HttpHeaders requestHeaders = new HttpHeaders();
|
||||||
requestHeaders.add("Cookie", response.getHeaders().getFirst(HttpHeaders.SET_COOKIE));
|
requestHeaders.add("Cookie", response.getHeaders()
|
||||||
|
.getFirst(HttpHeaders.SET_COOKIE));
|
||||||
|
|
||||||
// Logging out using the session cookies
|
// Logging out using the session cookies
|
||||||
response = restTemplate.exchange(getLogoutUrl(), HttpMethod.GET, new HttpEntity(requestHeaders), String.class);
|
response = restTemplate.exchange(getLogoutUrl(), HttpMethod.GET, new HttpEntity<String>(requestHeaders), String.class);
|
||||||
assertThat(response.getStatusCode().value()).isEqualTo(200);
|
assertThat(response.getStatusCode()
|
||||||
|
.value()).isEqualTo(200);
|
||||||
|
|
||||||
// User cache must be empty now
|
// User cache must be empty now
|
||||||
// this is the reaction on custom logout filter execution
|
// this is the reaction on custom logout filter execution
|
||||||
assertThat(userCache.size()).isEqualTo(0);
|
assertThat(userCache.size()).isEqualTo(0);
|
||||||
|
|
||||||
// Assert unathorized request
|
// Assert unauthorized request
|
||||||
response = restTemplate.exchange(getLanguageUrl(), HttpMethod.GET, new HttpEntity(requestHeaders), String.class);
|
response = restTemplate.exchange(getLanguageUrl(), HttpMethod.GET, new HttpEntity<String>(requestHeaders), String.class);
|
||||||
assertThat(response.getStatusCode().value()).isEqualTo(401);
|
assertThat(response.getStatusCode()
|
||||||
}
|
.value()).isEqualTo(401);
|
||||||
|
}
|
||||||
|
|
||||||
private String getLanguageUrl() {
|
private String getLanguageUrl() {
|
||||||
return "http://localhost:" + port + "/user/language";
|
return "http://localhost:" + port + "/user/language";
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getLogoutUrl() {
|
private String getLogoutUrl() {
|
||||||
return "http://localhost:" + port + "/user/logout";
|
return "http://localhost:" + port + "/user/logout";
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
spring.datasource.url=jdbc:postgresql://localhost:5432/develop
|
spring.datasource.url=jdbc:postgresql://localhost:5432/test
|
||||||
spring.datasource.username=develop
|
spring.datasource.username=test
|
||||||
spring.datasource.password=develop
|
spring.datasource.password=test
|
||||||
|
|
||||||
spring.jpa.hibernate.ddl-auto=create
|
spring.jpa.hibernate.ddl-auto=create
|
||||||
|
Loading…
x
Reference in New Issue
Block a user