From 0d84b1f0c1f29ed35628a5f3c2bf4546251e3269 Mon Sep 17 00:00:00 2001 From: linhvovn Date: Fri, 29 Dec 2017 01:24:21 +0800 Subject: [PATCH 01/22] [tlinh2110@gmail.com] Restructure to Method Security --- .../methodsecurity/annotation/IsViewer.java | 15 ++ .../config/MethodSecurityConfig.java | 4 +- .../entity/CustomUser.java | 2 +- .../repository/UserRoleRepository.java | 20 ++- .../service/CustomUserDetailsService.java | 4 +- .../service/UserRoleService.java | 107 ++++++++++++ .../service/UserRoleService.java | 29 ---- .../methodsecurity/TestMethodSecurity.java | 163 ++++++++++++++++++ .../TestWithMockUserAtClassLevel.java | 6 +- .../TestWithUserDetails.java | 15 +- .../WithMockJohnViewer.java | 2 +- .../TestMethodSecurity.java | 57 ------ 12 files changed, 323 insertions(+), 101 deletions(-) create mode 100644 spring-security-core/src/main/java/org/baeldung/methodsecurity/annotation/IsViewer.java rename spring-security-core/src/main/java/org/baeldung/{testmethodsecurity => methodsecurity}/config/MethodSecurityConfig.java (85%) rename spring-security-core/src/main/java/org/baeldung/{testmethodsecurity => methodsecurity}/entity/CustomUser.java (91%) rename spring-security-core/src/main/java/org/baeldung/{testmethodsecurity => methodsecurity}/repository/UserRoleRepository.java (71%) rename spring-security-core/src/main/java/org/baeldung/{testmethodsecurity => methodsecurity}/service/CustomUserDetailsService.java (80%) create mode 100644 spring-security-core/src/main/java/org/baeldung/methodsecurity/service/UserRoleService.java delete mode 100644 spring-security-core/src/main/java/org/baeldung/testmethodsecurity/service/UserRoleService.java create mode 100644 spring-security-core/src/test/java/org/baeldung/methodsecurity/TestMethodSecurity.java rename spring-security-core/src/test/java/org/baeldung/{testmethodsecurity => methodsecurity}/TestWithMockUserAtClassLevel.java (82%) rename spring-security-core/src/test/java/org/baeldung/{testmethodsecurity => methodsecurity}/TestWithUserDetails.java (64%) rename spring-security-core/src/test/java/org/baeldung/{testmethodsecurity => methodsecurity}/WithMockJohnViewer.java (84%) delete mode 100644 spring-security-core/src/test/java/org/baeldung/testmethodsecurity/TestMethodSecurity.java diff --git a/spring-security-core/src/main/java/org/baeldung/methodsecurity/annotation/IsViewer.java b/spring-security-core/src/main/java/org/baeldung/methodsecurity/annotation/IsViewer.java new file mode 100644 index 0000000000..da933fb19f --- /dev/null +++ b/spring-security-core/src/main/java/org/baeldung/methodsecurity/annotation/IsViewer.java @@ -0,0 +1,15 @@ +package org.baeldung.methodsecurity.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.security.access.prepost.PreAuthorize; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@PreAuthorize("hasRole('VIEWER')") +public @interface IsViewer +{ +} \ No newline at end of file diff --git a/spring-security-core/src/main/java/org/baeldung/testmethodsecurity/config/MethodSecurityConfig.java b/spring-security-core/src/main/java/org/baeldung/methodsecurity/config/MethodSecurityConfig.java similarity index 85% rename from spring-security-core/src/main/java/org/baeldung/testmethodsecurity/config/MethodSecurityConfig.java rename to spring-security-core/src/main/java/org/baeldung/methodsecurity/config/MethodSecurityConfig.java index 1b2227f9be..4749c730dc 100644 --- a/spring-security-core/src/main/java/org/baeldung/testmethodsecurity/config/MethodSecurityConfig.java +++ b/spring-security-core/src/main/java/org/baeldung/methodsecurity/config/MethodSecurityConfig.java @@ -1,11 +1,11 @@ -package org.baeldung.testmethodsecurity.config; +package org.baeldung.methodsecurity.config; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration; @Configuration -@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true) +@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true) public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration { } diff --git a/spring-security-core/src/main/java/org/baeldung/testmethodsecurity/entity/CustomUser.java b/spring-security-core/src/main/java/org/baeldung/methodsecurity/entity/CustomUser.java similarity index 91% rename from spring-security-core/src/main/java/org/baeldung/testmethodsecurity/entity/CustomUser.java rename to spring-security-core/src/main/java/org/baeldung/methodsecurity/entity/CustomUser.java index b145a82c80..19a7719373 100644 --- a/spring-security-core/src/main/java/org/baeldung/testmethodsecurity/entity/CustomUser.java +++ b/spring-security-core/src/main/java/org/baeldung/methodsecurity/entity/CustomUser.java @@ -1,4 +1,4 @@ -package org.baeldung.testmethodsecurity.entity; +package org.baeldung.methodsecurity.entity; import java.util.Collection; diff --git a/spring-security-core/src/main/java/org/baeldung/testmethodsecurity/repository/UserRoleRepository.java b/spring-security-core/src/main/java/org/baeldung/methodsecurity/repository/UserRoleRepository.java similarity index 71% rename from spring-security-core/src/main/java/org/baeldung/testmethodsecurity/repository/UserRoleRepository.java rename to spring-security-core/src/main/java/org/baeldung/methodsecurity/repository/UserRoleRepository.java index 565b46262d..82e74f0cd0 100644 --- a/spring-security-core/src/main/java/org/baeldung/testmethodsecurity/repository/UserRoleRepository.java +++ b/spring-security-core/src/main/java/org/baeldung/methodsecurity/repository/UserRoleRepository.java @@ -1,11 +1,11 @@ -package org.baeldung.testmethodsecurity.repository; +package org.baeldung.methodsecurity.repository; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import org.baeldung.testmethodsecurity.entity.CustomUser; +import org.baeldung.methodsecurity.entity.CustomUser; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UsernameNotFoundException; @@ -38,4 +38,20 @@ public class UserRoleRepository { throw new UsernameNotFoundException("User "+username+" cannot be found"); } + public boolean isValidUsername(String username){ + return DB_BASED_USER_MAPPING.containsKey(username); + } + + public boolean isValidRole(String roleName){ + return roleName.startsWith("ROLE_"); + } + + public List getAllUsernames(){ + List usernames = new ArrayList<>(); + usernames.add("jane"); + usernames.add("john"); + usernames.add("jack"); + return usernames; + } + } diff --git a/spring-security-core/src/main/java/org/baeldung/testmethodsecurity/service/CustomUserDetailsService.java b/spring-security-core/src/main/java/org/baeldung/methodsecurity/service/CustomUserDetailsService.java similarity index 80% rename from spring-security-core/src/main/java/org/baeldung/testmethodsecurity/service/CustomUserDetailsService.java rename to spring-security-core/src/main/java/org/baeldung/methodsecurity/service/CustomUserDetailsService.java index a5adcd3408..91171468bb 100644 --- a/spring-security-core/src/main/java/org/baeldung/testmethodsecurity/service/CustomUserDetailsService.java +++ b/spring-security-core/src/main/java/org/baeldung/methodsecurity/service/CustomUserDetailsService.java @@ -1,6 +1,6 @@ -package org.baeldung.testmethodsecurity.service; +package org.baeldung.methodsecurity.service; -import org.baeldung.testmethodsecurity.repository.UserRoleRepository; +import org.baeldung.methodsecurity.repository.UserRoleRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; diff --git a/spring-security-core/src/main/java/org/baeldung/methodsecurity/service/UserRoleService.java b/spring-security-core/src/main/java/org/baeldung/methodsecurity/service/UserRoleService.java new file mode 100644 index 0000000000..3afd56110a --- /dev/null +++ b/spring-security-core/src/main/java/org/baeldung/methodsecurity/service/UserRoleService.java @@ -0,0 +1,107 @@ +package org.baeldung.methodsecurity.service; + +import java.util.List; +import java.util.stream.Collectors; + +import javax.annotation.security.RolesAllowed; + +import org.baeldung.methodsecurity.annotation.IsViewer; +import org.baeldung.methodsecurity.entity.CustomUser; +import org.baeldung.methodsecurity.repository.UserRoleRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.annotation.Secured; +import org.springframework.security.access.prepost.PostAuthorize; +import org.springframework.security.access.prepost.PostFilter; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.security.access.prepost.PreFilter; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Service; + +@Service +public class UserRoleService { + + @Autowired + UserRoleRepository userRoleRepository; + + @Secured("ROLE_VIEWER") + public String getUsername(){ + SecurityContext securityContext = SecurityContextHolder.getContext(); + return securityContext.getAuthentication().getName(); + } + + @Secured({"ROLE_VIEWER","ROLE_EDITOR"}) + public boolean isValidUsername(String username){ + return userRoleRepository.isValidUsername(username); + } + + @RolesAllowed("ROLE_VIEWER") + public String getUsername2(){ + SecurityContext securityContext = SecurityContextHolder.getContext(); + return securityContext.getAuthentication().getName(); + } + + @RolesAllowed({"ROLE_VIEWER","ROLE_EDITOR"}) + public boolean isValidUsername2(String username){ + return userRoleRepository.isValidUsername(username); + } + + @PreAuthorize("hasRole('ROLE_VIEWER')") + public String getUsernameInUpperCase(){ + return getUsername().toUpperCase(); + } + + @PreAuthorize("hasAuthority('SYS_ADMIN')") + public String getUsernameInLowerCase(){ + return getUsername().toLowerCase(); + } + + @PreAuthorize("hasRole('ROLE_VIEWER') or hasRole('ROLE_EDITOR')") + public boolean isValidUsername3(String username){ + return userRoleRepository.isValidUsername(username); + } + + @PreAuthorize("#username == authentication.principal.username") + public String getMyRoles(String username){ + SecurityContext securityContext = SecurityContextHolder.getContext(); + return securityContext + .getAuthentication() + .getAuthorities() + .stream().map(auth -> auth.getAuthority()) + .collect(Collectors.joining(",")); + } + + @PostAuthorize("returnObject.username == authentication.principal.nickName") + public CustomUser loadUserDetail(String username){ + return userRoleRepository.loadUserByUserName(username); + } + + @PreFilter("filterObject != authentication.principal.username") + public String joinUsernames(List usernames){ + return usernames.stream().collect(Collectors.joining(";")); + } + + @PreFilter(value="filterObject != authentication.principal.username",filterTarget="usernames") + public String joinUsernamesAndRoles(List usernames,List roles){ + return usernames.stream().collect(Collectors.joining(";")) + +":"+roles.stream().collect(Collectors.joining(";")); + } + + @PostFilter("filterObject != authentication.principal.username") + public List getAllUsernamesExceptCurrent(){ + return userRoleRepository.getAllUsernames(); + } + + @IsViewer + public String getUsername4(){ + SecurityContext securityContext = SecurityContextHolder.getContext(); + return securityContext.getAuthentication().getName(); + } + + @PreAuthorize("#username == authentication.principal.username") + @PostAuthorize("returnObject.username == authentication.principal.nickName") + public CustomUser securedLoadUserDetail(String username){ + return userRoleRepository.loadUserByUserName(username); + } + +} diff --git a/spring-security-core/src/main/java/org/baeldung/testmethodsecurity/service/UserRoleService.java b/spring-security-core/src/main/java/org/baeldung/testmethodsecurity/service/UserRoleService.java deleted file mode 100644 index f4dc6cf335..0000000000 --- a/spring-security-core/src/main/java/org/baeldung/testmethodsecurity/service/UserRoleService.java +++ /dev/null @@ -1,29 +0,0 @@ -package org.baeldung.testmethodsecurity.service; - -import org.baeldung.testmethodsecurity.entity.CustomUser; -import org.baeldung.testmethodsecurity.repository.UserRoleRepository; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.access.prepost.PostAuthorize; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.security.core.context.SecurityContext; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.stereotype.Service; - -@Service -public class UserRoleService { - - @Autowired - UserRoleRepository userRoleRepository; - - @PreAuthorize("hasRole('ROLE_VIEWER') or hasAuthority('SYS_ADMIN')") - public String getUsername(){ - SecurityContext securityContext = SecurityContextHolder.getContext(); - return securityContext.getAuthentication().getName(); - } - - @PostAuthorize("returnObject.username == authentication.principal.nickName") - public CustomUser loadUserDetail(String username){ - return userRoleRepository.loadUserByUserName(username); - } - -} diff --git a/spring-security-core/src/test/java/org/baeldung/methodsecurity/TestMethodSecurity.java b/spring-security-core/src/test/java/org/baeldung/methodsecurity/TestMethodSecurity.java new file mode 100644 index 0000000000..dcc77fbab5 --- /dev/null +++ b/spring-security-core/src/test/java/org/baeldung/methodsecurity/TestMethodSecurity.java @@ -0,0 +1,163 @@ +package org.baeldung.methodsecurity; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.List; + +import org.baeldung.methodsecurity.service.UserRoleService; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException; +import org.springframework.security.test.context.support.WithAnonymousUser; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@ContextConfiguration +public class TestMethodSecurity{ + + @Autowired + UserRoleService userRoleService; + + @Configuration + @ComponentScan("org.baeldung.methodsecurity.*") + public static class SpringConfig { + + } + + @Test(expected=AuthenticationCredentialsNotFoundException.class) + public void givenNoSecurity_whenCallGetUsername_thenReturnException(){ + String userName = userRoleService.getUsername(); + assertEquals("john", userName); + } + + @Test + @WithMockUser(username="john",roles={"VIEWER"}) + public void givenRoleViewer_whenCallGetUsername_thenReturnUsername(){ + String userName = userRoleService.getUsername(); + assertEquals("john", userName); + } + + @Test + @WithMockUser(username="john",roles={"EDITOR"}) + public void givenUsernameJohn_whenCallIsValidUsername_thenReturnTrue(){ + boolean isValid = userRoleService.isValidUsername("john"); + assertEquals(true, isValid); + } + + @Test(expected = AccessDeniedException.class) + @WithMockUser(username = "john", roles = { "ADMIN" }) + public void givenRoleAdmin_whenCallGetUsername_thenReturnAccessDenied() { + userRoleService.getUsername(); + } + + @Test(expected = AccessDeniedException.class) + @WithMockUser(username = "john", roles = {"USER"}) + public void givenRoleUser_whenCallGetUsername2_thenReturnAccessDenied() { + userRoleService.getUsername2(); + } + + @Test + @WithMockUser(username="john",roles={"VIEWER","EDITOR"}) + public void givenRoleViewer_whenCallGetUsername2_thenReturnUsername(){ + String userName = userRoleService.getUsername2(); + assertEquals("john", userName); + } + + @Test + @WithMockUser(username="john",roles={"VIEWER"}) + public void givenUsernameJerry_whenCallIsValidUsername2_thenReturnFalse(){ + boolean isValid = userRoleService.isValidUsername2("jerry"); + assertEquals(false, isValid); + } + + @Test + @WithMockUser(username="JOHN",authorities={"SYS_ADMIN"}) + public void givenAuthoritySysAdmin_whenCallGetUsernameInLowerCase_thenReturnUsername(){ + String username = userRoleService.getUsernameInLowerCase(); + assertEquals("john", username); + } + + @Test + @WithMockUser(username="john",roles={"ADMIN","USER","VIEWER"}) + public void givenUserJohn_whenCallGetMyRolesWithJohn_thenReturnRoles(){ + String roles = userRoleService.getMyRoles("john"); + assertEquals("ROLE_ADMIN,ROLE_USER,ROLE_VIEWER", roles); + } + + @Test(expected=AccessDeniedException.class) + @WithMockUser(username="john",roles={"ADMIN","USER","VIEWER"}) + public void givenUserJane_whenCallGetMyRolesWithJane_thenAccessDenied(){ + userRoleService.getMyRoles("jane"); + } + + @Test(expected=AccessDeniedException.class) + @WithAnonymousUser + public void givenAnomynousUser_whenCallGetUsername_thenAccessDenied(){ + userRoleService.getUsername(); + } + + @Test + @WithMockJohnViewer + public void givenMockedJohnViewer_whenCallGetUsername_thenReturnUsername(){ + String userName = userRoleService.getUsername(); + assertEquals("john", userName); + } + + @Test + @WithMockUser(username="jane") + public void givenListContainCurrentUsername_whenJoinUsernames_thenReturnUsernames(){ + List usernames = new ArrayList<>(); + usernames.add("jane"); + usernames.add("john"); + usernames.add("jack"); + String containCurrentUser = userRoleService.joinUsernames(usernames); + assertEquals("john;jack", containCurrentUser); + + } + + @Test + @WithMockUser(username="john") + public void givenListNotContainCurrentUsername_whenCallContainCurrentUser_thenReturnAccessDenied(){ + List usernames = new ArrayList<>(); + usernames.add("jane"); + usernames.add("john"); + usernames.add("jack"); + + List roles = new ArrayList<>(); + roles.add("ROLE_ADMIN"); + roles.add("ROLE_TEST"); + + String containCurrentUser = userRoleService.joinUsernamesAndRoles(usernames,roles); + assertEquals("jane;jack:ROLE_ADMIN;ROLE_TEST", containCurrentUser); + } + + @Test + @WithMockUser(username="john") + public void givenUserJohn_whenCallGetAllUsernamesExceptCurrent_thenReturnOtherusernames(){ + List others = userRoleService.getAllUsernamesExceptCurrent(); + assertEquals(2, others.size()); + assertTrue(others.contains("jane")); + assertTrue(others.contains("jack")); + } + + @Test + @WithMockUser(username="john",roles={"VIEWER"}) + public void givenRoleViewer_whenCallGetUsername4_thenReturnUsername(){ + String userName = userRoleService.getUsername4(); + assertEquals("john", userName); + } + + @Test(expected=AccessDeniedException.class) + @WithMockUser(username="john") + public void givenDefaultRole_whenCallGetUsername4_thenAccessDenied(){ + userRoleService.getUsername4(); + } +} \ No newline at end of file diff --git a/spring-security-core/src/test/java/org/baeldung/testmethodsecurity/TestWithMockUserAtClassLevel.java b/spring-security-core/src/test/java/org/baeldung/methodsecurity/TestWithMockUserAtClassLevel.java similarity index 82% rename from spring-security-core/src/test/java/org/baeldung/testmethodsecurity/TestWithMockUserAtClassLevel.java rename to spring-security-core/src/test/java/org/baeldung/methodsecurity/TestWithMockUserAtClassLevel.java index a348a7799d..319aee63a6 100644 --- a/spring-security-core/src/test/java/org/baeldung/testmethodsecurity/TestWithMockUserAtClassLevel.java +++ b/spring-security-core/src/test/java/org/baeldung/methodsecurity/TestWithMockUserAtClassLevel.java @@ -1,8 +1,8 @@ -package org.baeldung.testmethodsecurity; +package org.baeldung.methodsecurity; import static org.junit.Assert.assertEquals; -import org.baeldung.testmethodsecurity.service.UserRoleService; +import org.baeldung.methodsecurity.service.UserRoleService; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; @@ -27,7 +27,7 @@ public class TestWithMockUserAtClassLevel { UserRoleService userService; @Configuration - @ComponentScan("org.baeldung.testmethodsecurity.*") + @ComponentScan("org.baeldung.methodsecurity.*") public static class SpringConfig { } diff --git a/spring-security-core/src/test/java/org/baeldung/testmethodsecurity/TestWithUserDetails.java b/spring-security-core/src/test/java/org/baeldung/methodsecurity/TestWithUserDetails.java similarity index 64% rename from spring-security-core/src/test/java/org/baeldung/testmethodsecurity/TestWithUserDetails.java rename to spring-security-core/src/test/java/org/baeldung/methodsecurity/TestWithUserDetails.java index ed8ed8cc85..3f60281380 100644 --- a/spring-security-core/src/test/java/org/baeldung/testmethodsecurity/TestWithUserDetails.java +++ b/spring-security-core/src/test/java/org/baeldung/methodsecurity/TestWithUserDetails.java @@ -1,9 +1,9 @@ -package org.baeldung.testmethodsecurity; +package org.baeldung.methodsecurity; import static org.junit.Assert.assertEquals; -import org.baeldung.testmethodsecurity.entity.CustomUser; -import org.baeldung.testmethodsecurity.service.UserRoleService; +import org.baeldung.methodsecurity.entity.CustomUser; +import org.baeldung.methodsecurity.service.UserRoleService; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; @@ -21,7 +21,7 @@ public class TestWithUserDetails { UserRoleService userService; @Configuration - @ComponentScan("org.baeldung.testmethodsecurity.*") + @ComponentScan("org.baeldung.methodsecurity.*") public static class SpringConfig { } @@ -32,4 +32,11 @@ public class TestWithUserDetails { CustomUser user = userService.loadUserDetail("jane"); assertEquals("jane",user.getNickName()); } + + @Test + @WithUserDetails(value="jane",userDetailsServiceBeanName="userDetailService") + public void whenJohn_callSecuredLoadUserDetail_thenOK(){ + CustomUser user = userService.securedLoadUserDetail("john"); + assertEquals("jane",user.getNickName()); + } } diff --git a/spring-security-core/src/test/java/org/baeldung/testmethodsecurity/WithMockJohnViewer.java b/spring-security-core/src/test/java/org/baeldung/methodsecurity/WithMockJohnViewer.java similarity index 84% rename from spring-security-core/src/test/java/org/baeldung/testmethodsecurity/WithMockJohnViewer.java rename to spring-security-core/src/test/java/org/baeldung/methodsecurity/WithMockJohnViewer.java index 994fe2e69b..5e1e882f3d 100644 --- a/spring-security-core/src/test/java/org/baeldung/testmethodsecurity/WithMockJohnViewer.java +++ b/spring-security-core/src/test/java/org/baeldung/methodsecurity/WithMockJohnViewer.java @@ -1,4 +1,4 @@ -package org.baeldung.testmethodsecurity; +package org.baeldung.methodsecurity; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; diff --git a/spring-security-core/src/test/java/org/baeldung/testmethodsecurity/TestMethodSecurity.java b/spring-security-core/src/test/java/org/baeldung/testmethodsecurity/TestMethodSecurity.java deleted file mode 100644 index 671229c726..0000000000 --- a/spring-security-core/src/test/java/org/baeldung/testmethodsecurity/TestMethodSecurity.java +++ /dev/null @@ -1,57 +0,0 @@ -package org.baeldung.testmethodsecurity; - -import static org.junit.Assert.assertEquals; - -import org.baeldung.testmethodsecurity.service.UserRoleService; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.ComponentScan; -import org.springframework.context.annotation.Configuration; -import org.springframework.security.access.AccessDeniedException; -import org.springframework.security.test.context.support.WithAnonymousUser; -import org.springframework.security.test.context.support.WithMockUser; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringRunner; - -@RunWith(SpringRunner.class) -@ContextConfiguration -public class TestMethodSecurity{ - - @Autowired - UserRoleService userRoleService; - - @Configuration - @ComponentScan("org.baeldung.testmethodsecurity.*") - public static class SpringConfig { - - } - - @Test - @WithMockUser(username="john",roles={"VIEWER"}) - public void givenRoleViewer_whenCallGetUsername_thenReturnUsername(){ - String userName = userRoleService.getUsername(); - assertEquals("john", userName); - } - - @Test - @WithMockUser(username="john",authorities={"SYS_ADMIN"}) - public void givenAuthoritySysAdmin_whenCallGetUsername_thenReturnUsername(){ - String userName = userRoleService.getUsername(); - assertEquals("john", userName); - } - - @Test(expected=AccessDeniedException.class) - @WithAnonymousUser - public void givenAnomynousUser_whenCallGetUsername_thenAccessDenied(){ - userRoleService.getUsername(); - } - - @Test - @WithMockJohnViewer - public void givenMockedJohnViewer_whenCallGetUsername_thenReturnUsername(){ - String userName = userRoleService.getUsername(); - assertEquals("john", userName); - } - -} \ No newline at end of file From 1b7e6957bbec07913762252edefe8e74e395cf45 Mon Sep 17 00:00:00 2001 From: linhvovn Date: Sun, 31 Dec 2017 22:56:35 +0800 Subject: [PATCH 02/22] [BAEL1411-tlinh2110] Add Class Level Security Example --- .../methodsecurity/service/SystemService.java | 18 +++++++ .../service/UserRoleService.java | 2 +- .../TestClassLevelSecurity.java | 49 +++++++++++++++++++ .../methodsecurity/TestMethodSecurity.java | 1 + .../methodsecurity/TestWithUserDetails.java | 18 ++++++- 5 files changed, 85 insertions(+), 3 deletions(-) create mode 100644 spring-security-core/src/main/java/org/baeldung/methodsecurity/service/SystemService.java create mode 100644 spring-security-core/src/test/java/org/baeldung/methodsecurity/TestClassLevelSecurity.java diff --git a/spring-security-core/src/main/java/org/baeldung/methodsecurity/service/SystemService.java b/spring-security-core/src/main/java/org/baeldung/methodsecurity/service/SystemService.java new file mode 100644 index 0000000000..5f29d7dee6 --- /dev/null +++ b/spring-security-core/src/main/java/org/baeldung/methodsecurity/service/SystemService.java @@ -0,0 +1,18 @@ +package org.baeldung.methodsecurity.service; + +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.stereotype.Service; + +@Service +@PreAuthorize("hasRole('ROLE_ADMIN')") +public class SystemService { + + public String getSystemYear(){ + return "2017"; + } + + public String getSystemDate(){ + return "31-12-2017"; + } + +} diff --git a/spring-security-core/src/main/java/org/baeldung/methodsecurity/service/UserRoleService.java b/spring-security-core/src/main/java/org/baeldung/methodsecurity/service/UserRoleService.java index 3afd56110a..7379ee5223 100644 --- a/spring-security-core/src/main/java/org/baeldung/methodsecurity/service/UserRoleService.java +++ b/spring-security-core/src/main/java/org/baeldung/methodsecurity/service/UserRoleService.java @@ -103,5 +103,5 @@ public class UserRoleService { public CustomUser securedLoadUserDetail(String username){ return userRoleRepository.loadUserByUserName(username); } - + } diff --git a/spring-security-core/src/test/java/org/baeldung/methodsecurity/TestClassLevelSecurity.java b/spring-security-core/src/test/java/org/baeldung/methodsecurity/TestClassLevelSecurity.java new file mode 100644 index 0000000000..502fd50c46 --- /dev/null +++ b/spring-security-core/src/test/java/org/baeldung/methodsecurity/TestClassLevelSecurity.java @@ -0,0 +1,49 @@ +package org.baeldung.methodsecurity; + +import static org.junit.Assert.*; + +import org.baeldung.methodsecurity.service.SystemService; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@ContextConfiguration +public class TestClassLevelSecurity { + + @Autowired + SystemService systemService; + + @Configuration + @ComponentScan("org.baeldung.methodsecurity.*") + public static class SpringConfig { + + } + + @Test + @WithMockUser(username="john",roles={"ADMIN"}) + public void givenRoleAdmin_whenCallGetSystemYear_return2017(){ + String systemYear = systemService.getSystemYear(); + assertEquals("2017",systemYear); + } + + @Test(expected=AccessDeniedException.class) + @WithMockUser(username="john",roles={"VIEWER"}) + public void givenRoleViewer_whenCallGetSystemYear_returnAccessDenied(){ + String systemYear = systemService.getSystemYear(); + assertEquals("2017",systemYear); + } + + @Test + @WithMockUser(username="john",roles={"ADMIN"}) + public void givenRoleAdmin_whenCallGetSystemDate_returnDate(){ + String systemYear = systemService.getSystemDate(); + assertEquals("31-12-2017",systemYear); + } +} diff --git a/spring-security-core/src/test/java/org/baeldung/methodsecurity/TestMethodSecurity.java b/spring-security-core/src/test/java/org/baeldung/methodsecurity/TestMethodSecurity.java index dcc77fbab5..4e4b665fb2 100644 --- a/spring-security-core/src/test/java/org/baeldung/methodsecurity/TestMethodSecurity.java +++ b/spring-security-core/src/test/java/org/baeldung/methodsecurity/TestMethodSecurity.java @@ -160,4 +160,5 @@ public class TestMethodSecurity{ public void givenDefaultRole_whenCallGetUsername4_thenAccessDenied(){ userRoleService.getUsername4(); } + } \ No newline at end of file diff --git a/spring-security-core/src/test/java/org/baeldung/methodsecurity/TestWithUserDetails.java b/spring-security-core/src/test/java/org/baeldung/methodsecurity/TestWithUserDetails.java index 3f60281380..6c1d2ab62c 100644 --- a/spring-security-core/src/test/java/org/baeldung/methodsecurity/TestWithUserDetails.java +++ b/spring-security-core/src/test/java/org/baeldung/methodsecurity/TestWithUserDetails.java @@ -9,6 +9,7 @@ import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; +import org.springframework.security.access.AccessDeniedException; import org.springframework.security.test.context.support.WithUserDetails; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringRunner; @@ -35,8 +36,21 @@ public class TestWithUserDetails { @Test @WithUserDetails(value="jane",userDetailsServiceBeanName="userDetailService") - public void whenJohn_callSecuredLoadUserDetail_thenOK(){ - CustomUser user = userService.securedLoadUserDetail("john"); + public void givenJane_callSecuredLoadUserDetailWithJane_thenOK(){ + CustomUser user = userService.securedLoadUserDetail("jane"); assertEquals("jane",user.getNickName()); + assertEquals("jane",user.getUsername()); + } + + @Test(expected=AccessDeniedException.class) + @WithUserDetails(value="john",userDetailsServiceBeanName="userDetailService") + public void givenJohn_callSecuredLoadUserDetailWithJane_thenAccessDenied(){ + userService.securedLoadUserDetail("jane"); + } + + @Test(expected=AccessDeniedException.class) + @WithUserDetails(value="john",userDetailsServiceBeanName="userDetailService") + public void givenJohn_callSecuredLoadUserDetailWithJohn_thenAccessDenied(){ + userService.securedLoadUserDetail("john"); } } From 2bac6f88e71615c714f9a740c94bd98c44386533 Mon Sep 17 00:00:00 2001 From: linhvovn Date: Sun, 31 Dec 2017 23:32:33 +0800 Subject: [PATCH 03/22] [BAEL-1411] Format code --- .../methodsecurity/annotation/IsViewer.java | 3 +- .../methodsecurity/entity/CustomUser.java | 8 +- .../repository/UserRoleRepository.java | 42 +++---- .../service/UserRoleService.java | 73 ++++++----- .../methodsecurity/TestMethodSecurity.java | 116 +++++++++--------- .../TestWithMockUserAtClassLevel.java | 12 +- .../methodsecurity/TestWithUserDetails.java | 38 +++--- 7 files changed, 145 insertions(+), 147 deletions(-) diff --git a/spring-security-core/src/main/java/org/baeldung/methodsecurity/annotation/IsViewer.java b/spring-security-core/src/main/java/org/baeldung/methodsecurity/annotation/IsViewer.java index da933fb19f..711784adbb 100644 --- a/spring-security-core/src/main/java/org/baeldung/methodsecurity/annotation/IsViewer.java +++ b/spring-security-core/src/main/java/org/baeldung/methodsecurity/annotation/IsViewer.java @@ -10,6 +10,5 @@ import org.springframework.security.access.prepost.PreAuthorize; @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @PreAuthorize("hasRole('VIEWER')") -public @interface IsViewer -{ +public @interface IsViewer { } \ No newline at end of file diff --git a/spring-security-core/src/main/java/org/baeldung/methodsecurity/entity/CustomUser.java b/spring-security-core/src/main/java/org/baeldung/methodsecurity/entity/CustomUser.java index 19a7719373..fb9174befa 100644 --- a/spring-security-core/src/main/java/org/baeldung/methodsecurity/entity/CustomUser.java +++ b/spring-security-core/src/main/java/org/baeldung/methodsecurity/entity/CustomUser.java @@ -6,15 +6,15 @@ import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.userdetails.User; @SuppressWarnings("serial") -public class CustomUser extends User{ - +public class CustomUser extends User { + private String nickName; public CustomUser(String username, String password, Collection authorities) { super(username, password, authorities); } - - public CustomUser(String username, String password, Collection authorities,String nickName) { + + public CustomUser(String username, String password, Collection authorities, String nickName) { super(username, password, authorities); this.nickName = nickName; } diff --git a/spring-security-core/src/main/java/org/baeldung/methodsecurity/repository/UserRoleRepository.java b/spring-security-core/src/main/java/org/baeldung/methodsecurity/repository/UserRoleRepository.java index 82e74f0cd0..fc1a32289d 100644 --- a/spring-security-core/src/main/java/org/baeldung/methodsecurity/repository/UserRoleRepository.java +++ b/spring-security-core/src/main/java/org/baeldung/methodsecurity/repository/UserRoleRepository.java @@ -13,45 +13,45 @@ import org.springframework.stereotype.Service; @Service public class UserRoleRepository { - - static Map DB_BASED_USER_MAPPING; - - static{ + + static Map DB_BASED_USER_MAPPING; + + static { DB_BASED_USER_MAPPING = new LinkedHashMap<>(); - DB_BASED_USER_MAPPING.put("jane", new CustomUser("jane","1234", getGrantedAuthorities("ROLE_USER","ROLE_VIEWER"),"jane")); - DB_BASED_USER_MAPPING.put("john", new CustomUser("john","1234", getGrantedAuthorities("ROLE_EDITOR","ROLE_ADMIN"),"jane")); - DB_BASED_USER_MAPPING.put("jack", new CustomUser("jack","1234", getGrantedAuthorities("ROLE_USER","ROLE_REVIEWER"),"jane")); + DB_BASED_USER_MAPPING.put("jane", new CustomUser("jane", "1234", getGrantedAuthorities("ROLE_USER", "ROLE_VIEWER"), "jane")); + DB_BASED_USER_MAPPING.put("john", new CustomUser("john", "1234", getGrantedAuthorities("ROLE_EDITOR", "ROLE_ADMIN"), "jane")); + DB_BASED_USER_MAPPING.put("jack", new CustomUser("jack", "1234", getGrantedAuthorities("ROLE_USER", "ROLE_REVIEWER"), "jane")); } - - private static List getGrantedAuthorities(String...roles){ + + private static List getGrantedAuthorities(String... roles) { ArrayList authorities = new ArrayList<>(); - for (String role : roles){ + for (String role : roles) { authorities.add(new SimpleGrantedAuthority(role)); } return authorities; } - - public CustomUser loadUserByUserName(String username){ - if (DB_BASED_USER_MAPPING.containsKey(username)){ + + public CustomUser loadUserByUserName(String username) { + if (DB_BASED_USER_MAPPING.containsKey(username)) { return DB_BASED_USER_MAPPING.get(username); } - throw new UsernameNotFoundException("User "+username+" cannot be found"); + throw new UsernameNotFoundException("User " + username + " cannot be found"); } - - public boolean isValidUsername(String username){ + + public boolean isValidUsername(String username) { return DB_BASED_USER_MAPPING.containsKey(username); } - - public boolean isValidRole(String roleName){ + + public boolean isValidRole(String roleName) { return roleName.startsWith("ROLE_"); } - - public List getAllUsernames(){ + + public List getAllUsernames() { List usernames = new ArrayList<>(); usernames.add("jane"); usernames.add("john"); usernames.add("jack"); return usernames; } - + } diff --git a/spring-security-core/src/main/java/org/baeldung/methodsecurity/service/UserRoleService.java b/spring-security-core/src/main/java/org/baeldung/methodsecurity/service/UserRoleService.java index 7379ee5223..c980334e82 100644 --- a/spring-security-core/src/main/java/org/baeldung/methodsecurity/service/UserRoleService.java +++ b/spring-security-core/src/main/java/org/baeldung/methodsecurity/service/UserRoleService.java @@ -20,88 +20,87 @@ import org.springframework.stereotype.Service; @Service public class UserRoleService { - + @Autowired UserRoleRepository userRoleRepository; - + @Secured("ROLE_VIEWER") - public String getUsername(){ + public String getUsername() { SecurityContext securityContext = SecurityContextHolder.getContext(); return securityContext.getAuthentication().getName(); } - - @Secured({"ROLE_VIEWER","ROLE_EDITOR"}) - public boolean isValidUsername(String username){ + + @Secured({ "ROLE_VIEWER", "ROLE_EDITOR" }) + public boolean isValidUsername(String username) { return userRoleRepository.isValidUsername(username); } - + @RolesAllowed("ROLE_VIEWER") - public String getUsername2(){ + public String getUsername2() { SecurityContext securityContext = SecurityContextHolder.getContext(); return securityContext.getAuthentication().getName(); } - - @RolesAllowed({"ROLE_VIEWER","ROLE_EDITOR"}) - public boolean isValidUsername2(String username){ + + @RolesAllowed({ "ROLE_VIEWER", "ROLE_EDITOR" }) + public boolean isValidUsername2(String username) { return userRoleRepository.isValidUsername(username); } - + @PreAuthorize("hasRole('ROLE_VIEWER')") - public String getUsernameInUpperCase(){ + public String getUsernameInUpperCase() { return getUsername().toUpperCase(); } - + @PreAuthorize("hasAuthority('SYS_ADMIN')") - public String getUsernameInLowerCase(){ + public String getUsernameLC() { return getUsername().toLowerCase(); } - + @PreAuthorize("hasRole('ROLE_VIEWER') or hasRole('ROLE_EDITOR')") - public boolean isValidUsername3(String username){ + public boolean isValidUsername3(String username) { return userRoleRepository.isValidUsername(username); } - + @PreAuthorize("#username == authentication.principal.username") - public String getMyRoles(String username){ + public String getMyRoles(String username) { SecurityContext securityContext = SecurityContextHolder.getContext(); return securityContext .getAuthentication() .getAuthorities() - .stream().map(auth -> auth.getAuthority()) - .collect(Collectors.joining(",")); + .stream() + .map(auth -> auth.getAuthority()).collect(Collectors.joining(",")); } - + @PostAuthorize("returnObject.username == authentication.principal.nickName") - public CustomUser loadUserDetail(String username){ + public CustomUser loadUserDetail(String username) { return userRoleRepository.loadUserByUserName(username); } - + @PreFilter("filterObject != authentication.principal.username") - public String joinUsernames(List usernames){ + public String joinUsernames(List usernames) { return usernames.stream().collect(Collectors.joining(";")); } - - @PreFilter(value="filterObject != authentication.principal.username",filterTarget="usernames") - public String joinUsernamesAndRoles(List usernames,List roles){ - return usernames.stream().collect(Collectors.joining(";")) - +":"+roles.stream().collect(Collectors.joining(";")); + + @PreFilter(value = "filterObject != authentication.principal.username", filterTarget = "usernames") + public String joinUsernamesAndRoles(List usernames, List roles) { + return usernames.stream().collect(Collectors.joining(";")) + ":" + roles.stream().collect(Collectors.joining(";")); } - + @PostFilter("filterObject != authentication.principal.username") - public List getAllUsernamesExceptCurrent(){ + public List getAllUsernamesExceptCurrent() { return userRoleRepository.getAllUsernames(); } - + @IsViewer - public String getUsername4(){ + public String getUsername4() { SecurityContext securityContext = SecurityContextHolder.getContext(); return securityContext.getAuthentication().getName(); } - + @PreAuthorize("#username == authentication.principal.username") @PostAuthorize("returnObject.username == authentication.principal.nickName") - public CustomUser securedLoadUserDetail(String username){ + public CustomUser securedLoadUserDetail(String username) { return userRoleRepository.loadUserByUserName(username); } - + } diff --git a/spring-security-core/src/test/java/org/baeldung/methodsecurity/TestMethodSecurity.java b/spring-security-core/src/test/java/org/baeldung/methodsecurity/TestMethodSecurity.java index 4e4b665fb2..bc66c0b081 100644 --- a/spring-security-core/src/test/java/org/baeldung/methodsecurity/TestMethodSecurity.java +++ b/spring-security-core/src/test/java/org/baeldung/methodsecurity/TestMethodSecurity.java @@ -21,144 +21,144 @@ import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @ContextConfiguration -public class TestMethodSecurity{ - +public class TestMethodSecurity { + @Autowired UserRoleService userRoleService; - + @Configuration @ComponentScan("org.baeldung.methodsecurity.*") public static class SpringConfig { } - - @Test(expected=AuthenticationCredentialsNotFoundException.class) - public void givenNoSecurity_whenCallGetUsername_thenReturnException(){ + + @Test(expected = AuthenticationCredentialsNotFoundException.class) + public void givenNoSecurity_whenCallGetUsername_thenReturnException() { String userName = userRoleService.getUsername(); assertEquals("john", userName); } - + @Test - @WithMockUser(username="john",roles={"VIEWER"}) - public void givenRoleViewer_whenCallGetUsername_thenReturnUsername(){ + @WithMockUser(username = "john", roles = { "VIEWER" }) + public void givenRoleViewer_whenCallGetUsername_thenReturnUsername() { String userName = userRoleService.getUsername(); assertEquals("john", userName); } - + @Test - @WithMockUser(username="john",roles={"EDITOR"}) - public void givenUsernameJohn_whenCallIsValidUsername_thenReturnTrue(){ + @WithMockUser(username = "john", roles = { "EDITOR" }) + public void givenUsernameJohn_whenCallIsValidUsername_thenReturnTrue() { boolean isValid = userRoleService.isValidUsername("john"); assertEquals(true, isValid); } - + @Test(expected = AccessDeniedException.class) @WithMockUser(username = "john", roles = { "ADMIN" }) public void givenRoleAdmin_whenCallGetUsername_thenReturnAccessDenied() { userRoleService.getUsername(); } - + @Test(expected = AccessDeniedException.class) - @WithMockUser(username = "john", roles = {"USER"}) + @WithMockUser(username = "john", roles = { "USER" }) public void givenRoleUser_whenCallGetUsername2_thenReturnAccessDenied() { userRoleService.getUsername2(); } - + @Test - @WithMockUser(username="john",roles={"VIEWER","EDITOR"}) - public void givenRoleViewer_whenCallGetUsername2_thenReturnUsername(){ + @WithMockUser(username = "john", roles = { "VIEWER", "EDITOR" }) + public void givenRoleViewer_whenCallGetUsername2_thenReturnUsername() { String userName = userRoleService.getUsername2(); assertEquals("john", userName); } - + @Test - @WithMockUser(username="john",roles={"VIEWER"}) - public void givenUsernameJerry_whenCallIsValidUsername2_thenReturnFalse(){ + @WithMockUser(username = "john", roles = { "VIEWER" }) + public void givenUsernameJerry_whenCallIsValidUsername2_thenReturnFalse() { boolean isValid = userRoleService.isValidUsername2("jerry"); assertEquals(false, isValid); } - + @Test - @WithMockUser(username="JOHN",authorities={"SYS_ADMIN"}) - public void givenAuthoritySysAdmin_whenCallGetUsernameInLowerCase_thenReturnUsername(){ - String username = userRoleService.getUsernameInLowerCase(); + @WithMockUser(username = "JOHN", authorities = { "SYS_ADMIN" }) + public void givenAuthoritySysAdmin_whenCallGetUsernameInLowerCase_thenReturnUsername() { + String username = userRoleService.getUsernameLC(); assertEquals("john", username); } - + @Test - @WithMockUser(username="john",roles={"ADMIN","USER","VIEWER"}) - public void givenUserJohn_whenCallGetMyRolesWithJohn_thenReturnRoles(){ + @WithMockUser(username = "john", roles = { "ADMIN", "USER", "VIEWER" }) + public void givenUserJohn_whenCallGetMyRolesWithJohn_thenReturnRoles() { String roles = userRoleService.getMyRoles("john"); assertEquals("ROLE_ADMIN,ROLE_USER,ROLE_VIEWER", roles); } - - @Test(expected=AccessDeniedException.class) - @WithMockUser(username="john",roles={"ADMIN","USER","VIEWER"}) - public void givenUserJane_whenCallGetMyRolesWithJane_thenAccessDenied(){ + + @Test(expected = AccessDeniedException.class) + @WithMockUser(username = "john", roles = { "ADMIN", "USER", "VIEWER" }) + public void givenUserJane_whenCallGetMyRolesWithJane_thenAccessDenied() { userRoleService.getMyRoles("jane"); } - - @Test(expected=AccessDeniedException.class) + + @Test(expected = AccessDeniedException.class) @WithAnonymousUser - public void givenAnomynousUser_whenCallGetUsername_thenAccessDenied(){ + public void givenAnomynousUser_whenCallGetUsername_thenAccessDenied() { userRoleService.getUsername(); } - + @Test @WithMockJohnViewer - public void givenMockedJohnViewer_whenCallGetUsername_thenReturnUsername(){ + public void givenMockedJohnViewer_whenCallGetUsername_thenReturnUsername() { String userName = userRoleService.getUsername(); assertEquals("john", userName); } - + @Test - @WithMockUser(username="jane") - public void givenListContainCurrentUsername_whenJoinUsernames_thenReturnUsernames(){ + @WithMockUser(username = "jane") + public void givenListContainCurrentUsername_whenJoinUsernames_thenReturnUsernames() { List usernames = new ArrayList<>(); usernames.add("jane"); usernames.add("john"); usernames.add("jack"); String containCurrentUser = userRoleService.joinUsernames(usernames); assertEquals("john;jack", containCurrentUser); - + } - + @Test - @WithMockUser(username="john") - public void givenListNotContainCurrentUsername_whenCallContainCurrentUser_thenReturnAccessDenied(){ + @WithMockUser(username = "john") + public void givenListNotContainCurrentUsername_whenCallContainCurrentUser_thenReturnAccessDenied() { List usernames = new ArrayList<>(); usernames.add("jane"); usernames.add("john"); usernames.add("jack"); - + List roles = new ArrayList<>(); roles.add("ROLE_ADMIN"); roles.add("ROLE_TEST"); - - String containCurrentUser = userRoleService.joinUsernamesAndRoles(usernames,roles); + + String containCurrentUser = userRoleService.joinUsernamesAndRoles(usernames, roles); assertEquals("jane;jack:ROLE_ADMIN;ROLE_TEST", containCurrentUser); } - + @Test - @WithMockUser(username="john") - public void givenUserJohn_whenCallGetAllUsernamesExceptCurrent_thenReturnOtherusernames(){ + @WithMockUser(username = "john") + public void givenUserJohn_whenCallGetAllUsernamesExceptCurrent_thenReturnOtherusernames() { List others = userRoleService.getAllUsernamesExceptCurrent(); assertEquals(2, others.size()); assertTrue(others.contains("jane")); assertTrue(others.contains("jack")); } - + @Test - @WithMockUser(username="john",roles={"VIEWER"}) - public void givenRoleViewer_whenCallGetUsername4_thenReturnUsername(){ + @WithMockUser(username = "john", roles = { "VIEWER" }) + public void givenRoleViewer_whenCallGetUsername4_thenReturnUsername() { String userName = userRoleService.getUsername4(); assertEquals("john", userName); } - - @Test(expected=AccessDeniedException.class) - @WithMockUser(username="john") - public void givenDefaultRole_whenCallGetUsername4_thenAccessDenied(){ + + @Test(expected = AccessDeniedException.class) + @WithMockUser(username = "john") + public void givenDefaultRole_whenCallGetUsername4_thenAccessDenied() { userRoleService.getUsername4(); } - + } \ No newline at end of file diff --git a/spring-security-core/src/test/java/org/baeldung/methodsecurity/TestWithMockUserAtClassLevel.java b/spring-security-core/src/test/java/org/baeldung/methodsecurity/TestWithMockUserAtClassLevel.java index 319aee63a6..4df1af8ca9 100644 --- a/spring-security-core/src/test/java/org/baeldung/methodsecurity/TestWithMockUserAtClassLevel.java +++ b/spring-security-core/src/test/java/org/baeldung/methodsecurity/TestWithMockUserAtClassLevel.java @@ -14,18 +14,18 @@ import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @ContextConfiguration -@WithMockUser(username="john",roles={"VIEWER"}) +@WithMockUser(username = "john", roles = { "VIEWER" }) public class TestWithMockUserAtClassLevel { - + @Test - public void givenRoleViewer_whenCallGetUsername_thenReturnUsername(){ + public void givenRoleViewer_whenCallGetUsername_thenReturnUsername() { String currentUserName = userService.getUsername(); - assertEquals("john",currentUserName); + assertEquals("john", currentUserName); } - + @Autowired UserRoleService userService; - + @Configuration @ComponentScan("org.baeldung.methodsecurity.*") public static class SpringConfig { diff --git a/spring-security-core/src/test/java/org/baeldung/methodsecurity/TestWithUserDetails.java b/spring-security-core/src/test/java/org/baeldung/methodsecurity/TestWithUserDetails.java index 6c1d2ab62c..3ef5996554 100644 --- a/spring-security-core/src/test/java/org/baeldung/methodsecurity/TestWithUserDetails.java +++ b/spring-security-core/src/test/java/org/baeldung/methodsecurity/TestWithUserDetails.java @@ -17,40 +17,40 @@ import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @ContextConfiguration public class TestWithUserDetails { - + @Autowired UserRoleService userService; - + @Configuration @ComponentScan("org.baeldung.methodsecurity.*") public static class SpringConfig { } - + @Test - @WithUserDetails(value="john",userDetailsServiceBeanName="userDetailService") - public void whenJohn_callLoadUserDetail_thenOK(){ + @WithUserDetails(value = "john", userDetailsServiceBeanName = "userDetailService") + public void whenJohn_callLoadUserDetail_thenOK() { CustomUser user = userService.loadUserDetail("jane"); - assertEquals("jane",user.getNickName()); + assertEquals("jane", user.getNickName()); } - + @Test - @WithUserDetails(value="jane",userDetailsServiceBeanName="userDetailService") - public void givenJane_callSecuredLoadUserDetailWithJane_thenOK(){ + @WithUserDetails(value = "jane", userDetailsServiceBeanName = "userDetailService") + public void givenJane_callSecuredLoadUserDetailWithJane_thenOK() { CustomUser user = userService.securedLoadUserDetail("jane"); - assertEquals("jane",user.getNickName()); - assertEquals("jane",user.getUsername()); + assertEquals("jane", user.getNickName()); + assertEquals("jane", user.getUsername()); } - - @Test(expected=AccessDeniedException.class) - @WithUserDetails(value="john",userDetailsServiceBeanName="userDetailService") - public void givenJohn_callSecuredLoadUserDetailWithJane_thenAccessDenied(){ + + @Test(expected = AccessDeniedException.class) + @WithUserDetails(value = "john", userDetailsServiceBeanName = "userDetailService") + public void givenJohn_callSecuredLoadUserDetailWithJane_thenAccessDenied() { userService.securedLoadUserDetail("jane"); } - - @Test(expected=AccessDeniedException.class) - @WithUserDetails(value="john",userDetailsServiceBeanName="userDetailService") - public void givenJohn_callSecuredLoadUserDetailWithJohn_thenAccessDenied(){ + + @Test(expected = AccessDeniedException.class) + @WithUserDetails(value = "john", userDetailsServiceBeanName = "userDetailService") + public void givenJohn_callSecuredLoadUserDetailWithJohn_thenAccessDenied() { userService.securedLoadUserDetail("john"); } } From b0d331f2ddea55aad2a6bb22095a02ba04e53d0c Mon Sep 17 00:00:00 2001 From: linhvovn Date: Mon, 8 Jan 2018 01:27:53 +0800 Subject: [PATCH 04/22] [BAEL-1411:tlinh2110] Add example for PostAuthorize --- .../methodsecurity/service/UserRoleService.java | 12 +++++++----- .../baeldung/methodsecurity/TestMethodSecurity.java | 13 +++++++++++++ 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/spring-security-core/src/main/java/org/baeldung/methodsecurity/service/UserRoleService.java b/spring-security-core/src/main/java/org/baeldung/methodsecurity/service/UserRoleService.java index c980334e82..30bbdbc10f 100644 --- a/spring-security-core/src/main/java/org/baeldung/methodsecurity/service/UserRoleService.java +++ b/spring-security-core/src/main/java/org/baeldung/methodsecurity/service/UserRoleService.java @@ -64,11 +64,13 @@ public class UserRoleService { @PreAuthorize("#username == authentication.principal.username") public String getMyRoles(String username) { SecurityContext securityContext = SecurityContextHolder.getContext(); - return securityContext - .getAuthentication() - .getAuthorities() - .stream() - .map(auth -> auth.getAuthority()).collect(Collectors.joining(",")); + return securityContext.getAuthentication().getAuthorities().stream().map(auth -> auth.getAuthority()).collect(Collectors.joining(",")); + } + + @PostAuthorize("#username == authentication.principal.username") + public String getMyRoles2(String username) { + SecurityContext securityContext = SecurityContextHolder.getContext(); + return securityContext.getAuthentication().getAuthorities().stream().map(auth -> auth.getAuthority()).collect(Collectors.joining(",")); } @PostAuthorize("returnObject.username == authentication.principal.nickName") diff --git a/spring-security-core/src/test/java/org/baeldung/methodsecurity/TestMethodSecurity.java b/spring-security-core/src/test/java/org/baeldung/methodsecurity/TestMethodSecurity.java index bc66c0b081..2f48bce1fd 100644 --- a/spring-security-core/src/test/java/org/baeldung/methodsecurity/TestMethodSecurity.java +++ b/spring-security-core/src/test/java/org/baeldung/methodsecurity/TestMethodSecurity.java @@ -97,6 +97,19 @@ public class TestMethodSecurity { public void givenUserJane_whenCallGetMyRolesWithJane_thenAccessDenied() { userRoleService.getMyRoles("jane"); } + + @Test + @WithMockUser(username = "john", roles = { "ADMIN", "USER", "VIEWER" }) + public void givenUserJohn_whenCallGetMyRoles2WithJohn_thenReturnRoles() { + String roles = userRoleService.getMyRoles2("john"); + assertEquals("ROLE_ADMIN,ROLE_USER,ROLE_VIEWER", roles); + } + + @Test(expected = AccessDeniedException.class) + @WithMockUser(username = "john", roles = { "ADMIN", "USER", "VIEWER" }) + public void givenUserJane_whenCallGetMyRoles2WithJane_thenAccessDenied() { + userRoleService.getMyRoles2("jane"); + } @Test(expected = AccessDeniedException.class) @WithAnonymousUser From 70638b90f140b30acd84e83507e3b62825faeb5a Mon Sep 17 00:00:00 2001 From: linhvovn Date: Thu, 11 Jan 2018 23:02:48 +0800 Subject: [PATCH 05/22] [tlinh2110] Change test name --- .../java/org/baeldung/methodsecurity/TestMethodSecurity.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/spring-security-core/src/test/java/org/baeldung/methodsecurity/TestMethodSecurity.java b/spring-security-core/src/test/java/org/baeldung/methodsecurity/TestMethodSecurity.java index 2f48bce1fd..309ee3076d 100644 --- a/spring-security-core/src/test/java/org/baeldung/methodsecurity/TestMethodSecurity.java +++ b/spring-security-core/src/test/java/org/baeldung/methodsecurity/TestMethodSecurity.java @@ -133,12 +133,11 @@ public class TestMethodSecurity { usernames.add("jack"); String containCurrentUser = userRoleService.joinUsernames(usernames); assertEquals("john;jack", containCurrentUser); - } @Test @WithMockUser(username = "john") - public void givenListNotContainCurrentUsername_whenCallContainCurrentUser_thenReturnAccessDenied() { + public void givenListContainCurrentUsername_whenCallJoinUsernamesAndRoles_thenReturnUsernameAndRoles() { List usernames = new ArrayList<>(); usernames.add("jane"); usernames.add("john"); From 07aad444ae0052331349e53253ae0347e5912e28 Mon Sep 17 00:00:00 2001 From: Ahmad Alsanie Date: Fri, 12 Jan 2018 23:05:49 +0200 Subject: [PATCH 06/22] BAEL-1473 Intoduction to Spliterator in Java (#3400) --- .../com/baeldung/spliteratorAPI/Article.java | 44 +++++++++++++++++ .../com/baeldung/spliteratorAPI/Author.java | 33 +++++++++++++ .../com/baeldung/spliteratorAPI/Executor.java | 45 ++++++++++++++++++ .../spliteratorAPI/RelatedAuthorCounter.java | 27 +++++++++++ .../RelatedAuthorSpliterator.java | 47 +++++++++++++++++++ .../com/baeldung/spliteratorAPI/Task.java | 26 ++++++++++ 6 files changed, 222 insertions(+) create mode 100644 core-java-8/src/main/java/com/baeldung/spliteratorAPI/Article.java create mode 100644 core-java-8/src/main/java/com/baeldung/spliteratorAPI/Author.java create mode 100644 core-java-8/src/main/java/com/baeldung/spliteratorAPI/Executor.java create mode 100644 core-java-8/src/main/java/com/baeldung/spliteratorAPI/RelatedAuthorCounter.java create mode 100644 core-java-8/src/main/java/com/baeldung/spliteratorAPI/RelatedAuthorSpliterator.java create mode 100644 core-java-8/src/main/java/com/baeldung/spliteratorAPI/Task.java diff --git a/core-java-8/src/main/java/com/baeldung/spliteratorAPI/Article.java b/core-java-8/src/main/java/com/baeldung/spliteratorAPI/Article.java new file mode 100644 index 0000000000..402ec6ec5f --- /dev/null +++ b/core-java-8/src/main/java/com/baeldung/spliteratorAPI/Article.java @@ -0,0 +1,44 @@ +package com.baeldung.spliteratorAPI; + +import java.util.List; + +public class Article { + private List listOfAuthors; + private int id; + private String name; + + public Article(String name) { + this.name = name; + } + + public Article(List listOfAuthors, int id) { + super(); + this.listOfAuthors = listOfAuthors; + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public List getListOfAuthors() { + return listOfAuthors; + } + + public void setListOfAuthors(List listOfAuthors) { + this.listOfAuthors = listOfAuthors; + } + +} \ No newline at end of file diff --git a/core-java-8/src/main/java/com/baeldung/spliteratorAPI/Author.java b/core-java-8/src/main/java/com/baeldung/spliteratorAPI/Author.java new file mode 100644 index 0000000000..40c381f4a6 --- /dev/null +++ b/core-java-8/src/main/java/com/baeldung/spliteratorAPI/Author.java @@ -0,0 +1,33 @@ +package com.baeldung.spliteratorAPI; + +public class Author { + private String name; + private int relatedArticleId; + + public Author(String name, int relatedArticleId) { + this.name = name; + this.relatedArticleId = relatedArticleId; + } + + public int getRelatedArticleId() { + return relatedArticleId; + } + + public void setRelatedArticleId(int relatedArticleId) { + this.relatedArticleId = relatedArticleId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public String toString() { + return "[name: " + name + ", relatedId: " + relatedArticleId + "]"; + } +} + diff --git a/core-java-8/src/main/java/com/baeldung/spliteratorAPI/Executor.java b/core-java-8/src/main/java/com/baeldung/spliteratorAPI/Executor.java new file mode 100644 index 0000000000..b906acfad9 --- /dev/null +++ b/core-java-8/src/main/java/com/baeldung/spliteratorAPI/Executor.java @@ -0,0 +1,45 @@ +package com.baeldung.spliteratorAPI; + +import java.util.Arrays; +import java.util.List; +import java.util.Spliterator; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +public class Executor { + public void executeCustomSpliterator() { + Article article = new Article(Arrays.asList(new Author("Ahmad", 0), new Author("Eugen", 0), new Author("Alice", 1), new Author("Alice", 1), new Author("Mike", 0), new Author("Alice", 1), new Author("Mike", 0), new Author("Alice", 1), + new Author("Mike", 0), new Author("Alice", 1), new Author("Mike", 0), new Author("Mike", 0), new Author("Alice", 1), new Author("Mike", 0), new Author("Alice", 1), new Author("Mike", 0), new Author("Alice", 1), new Author("Mike", 0), + new Author("Alice", 1), new Author("Mike", 0), new Author("Michał", 0), new Author("Loredana", 1)), 0); + Stream stream = IntStream.range(0, article.getListOfAuthors() + .size()) + .mapToObj(article.getListOfAuthors()::get); + System.out.println("count= " + countAutors(stream.parallel())); + Spliterator spliterator = new RelatedAuthorSpliterator(article.getListOfAuthors()); + Stream stream2 = StreamSupport.stream(spliterator, true); + System.out.println("count= " + countAutors(stream2.parallel())); + } + + public void executeSpliterator() { + Spliterator
split1 = generateElements().spliterator(); + Spliterator
split2 = split1.trySplit(); + ExecutorService service = Executors.newCachedThreadPool(); + service.execute(new Task(split1)); + service.execute(new Task(split2)); + } + + private static int countAutors(Stream stream) { + RelatedAuthorCounter wordCounter = stream.reduce(new RelatedAuthorCounter(0, true), RelatedAuthorCounter::accumulate, RelatedAuthorCounter::combine); + return wordCounter.getCounter(); + } + + private List
generateElements() { + return Stream.generate(() -> new Article("Java")) + .limit(35000) + .collect(Collectors.toList()); + } +} diff --git a/core-java-8/src/main/java/com/baeldung/spliteratorAPI/RelatedAuthorCounter.java b/core-java-8/src/main/java/com/baeldung/spliteratorAPI/RelatedAuthorCounter.java new file mode 100644 index 0000000000..b7120b3af2 --- /dev/null +++ b/core-java-8/src/main/java/com/baeldung/spliteratorAPI/RelatedAuthorCounter.java @@ -0,0 +1,27 @@ +package com.baeldung.spliteratorAPI; + +public class RelatedAuthorCounter { + private final int counter; + private final boolean isRelated; + + public RelatedAuthorCounter(int counter, boolean isRelated) { + this.counter = counter; + this.isRelated = isRelated; + } + + public RelatedAuthorCounter accumulate(Author author) { + if (author.getRelatedArticleId() == 0) { + return isRelated ? this : new RelatedAuthorCounter(counter, true); + } else { + return isRelated ? new RelatedAuthorCounter(counter + 1, false) : this; + } + } + + public RelatedAuthorCounter combine(RelatedAuthorCounter RelatedAuthorCounter) { + return new RelatedAuthorCounter(counter + RelatedAuthorCounter.counter, RelatedAuthorCounter.isRelated); + } + + public int getCounter() { + return counter; + } +} diff --git a/core-java-8/src/main/java/com/baeldung/spliteratorAPI/RelatedAuthorSpliterator.java b/core-java-8/src/main/java/com/baeldung/spliteratorAPI/RelatedAuthorSpliterator.java new file mode 100644 index 0000000000..ae91c6fe6c --- /dev/null +++ b/core-java-8/src/main/java/com/baeldung/spliteratorAPI/RelatedAuthorSpliterator.java @@ -0,0 +1,47 @@ +package com.baeldung.spliteratorAPI; + +import java.util.List; +import java.util.Spliterator; +import java.util.function.Consumer; + +public class RelatedAuthorSpliterator implements Spliterator { + private final List list; + private int current = 0; + + public RelatedAuthorSpliterator(List list) { + this.list = list; + } + + @Override + public boolean tryAdvance(Consumer action) { + action.accept(list.get(current++)); + return current < list.size(); + } + + @Override + public Spliterator trySplit() { + int currentSize = list.size() - current; + if (currentSize < 10) { + return null; + } + for (int splitPos = currentSize / 2 + current; splitPos < list.size(); splitPos++) { + if (list.get(splitPos) + .getRelatedArticleId() == 0) { + Spliterator spliterator = new RelatedAuthorSpliterator(list.subList(current, splitPos)); + current = splitPos; + return spliterator; + } + } + return null; + } + + @Override + public long estimateSize() { + return list.size() - current; + } + + @Override + public int characteristics() { + return SIZED + CONCURRENT; + } +} diff --git a/core-java-8/src/main/java/com/baeldung/spliteratorAPI/Task.java b/core-java-8/src/main/java/com/baeldung/spliteratorAPI/Task.java new file mode 100644 index 0000000000..108fdc52aa --- /dev/null +++ b/core-java-8/src/main/java/com/baeldung/spliteratorAPI/Task.java @@ -0,0 +1,26 @@ +package com.baeldung.spliteratorAPI; + +import java.util.Spliterator; + +public class Task implements Runnable { + private Spliterator
spliterator; + private final static String SUFFIX = "- published by Baeldung"; + + public Task(Spliterator
spliterator) { + this.spliterator = spliterator; + } + + @Override + public void run() { + int current = 0; + while (spliterator.tryAdvance(article -> { + article.setName(article.getName() + .concat(SUFFIX)); + })) { + current++; + } + ; + System.out.println(Thread.currentThread() + .getName() + ":" + current); + } +} From 6f3710b9ae11b1564b2fe33a81ee32a209fe2fb6 Mon Sep 17 00:00:00 2001 From: Doha2012 Date: Sat, 13 Jan 2018 15:58:51 +0200 Subject: [PATCH 07/22] move stream API to core-java-8 (#3403) * make sure modules using java8 * move url matching code * upgrade boot parent * minor cleanup * fix blocking tests * add core-java-io module * move stream API to core-java-8 --- core-java-8/README.md | 3 +++ core-java-8/pom.xml | 20 +++++++++++++++++++ .../com/baeldung/stream/StreamIndices.java | 0 .../IterableStreamConversionUnitTest.java | 0 .../baeldung/stream/StreamIndicesTest.java | 0 .../string/StringToCharStreamUnitTest.java | 0 core-java-io/README.md | 3 --- 7 files changed, 23 insertions(+), 3 deletions(-) rename {core-java-io => core-java-8}/src/main/java/com/baeldung/stream/StreamIndices.java (100%) rename {core-java-io => core-java-8}/src/test/java/com/baeldung/java/conversion/IterableStreamConversionUnitTest.java (100%) rename {core-java-io => core-java-8}/src/test/java/com/baeldung/stream/StreamIndicesTest.java (100%) rename {core-java-io => core-java-8}/src/test/java/com/baeldung/string/StringToCharStreamUnitTest.java (100%) diff --git a/core-java-8/README.md b/core-java-8/README.md index 862d8c2224..53e8e1a44a 100644 --- a/core-java-8/README.md +++ b/core-java-8/README.md @@ -34,3 +34,6 @@ - [Copy a File with Java](http://www.baeldung.com/java-copy-file) - [Generating Prime Numbers in Java](http://www.baeldung.com/java-generate-prime-numbers) - [Static and Default Methods in Interfaces in Java](http://www.baeldung.com/java-static-default-methods) +- [Iterable to Stream in Java](http://www.baeldung.com/java-iterable-to-stream) +- [Converting String to Stream of chars](http://www.baeldung.com/java-string-to-stream) +- [How to Iterate Over a Stream With Indices](http://www.baeldung.com/java-stream-indices) diff --git a/core-java-8/pom.xml b/core-java-8/pom.xml index 17d330b3b8..75728ecd13 100644 --- a/core-java-8/pom.xml +++ b/core-java-8/pom.xml @@ -101,6 +101,23 @@ 1.19 + + com.codepoetics + protonpack + ${protonpack.version} + + + + io.vavr + vavr + ${vavr.version} + + + + one.util + streamex + ${streamex.version} + @@ -267,6 +284,9 @@ 4.01 1.10 1.16.12 + 0.9.0 + 1.13 + 0.6.5 3.6.1 diff --git a/core-java-io/src/main/java/com/baeldung/stream/StreamIndices.java b/core-java-8/src/main/java/com/baeldung/stream/StreamIndices.java similarity index 100% rename from core-java-io/src/main/java/com/baeldung/stream/StreamIndices.java rename to core-java-8/src/main/java/com/baeldung/stream/StreamIndices.java diff --git a/core-java-io/src/test/java/com/baeldung/java/conversion/IterableStreamConversionUnitTest.java b/core-java-8/src/test/java/com/baeldung/java/conversion/IterableStreamConversionUnitTest.java similarity index 100% rename from core-java-io/src/test/java/com/baeldung/java/conversion/IterableStreamConversionUnitTest.java rename to core-java-8/src/test/java/com/baeldung/java/conversion/IterableStreamConversionUnitTest.java diff --git a/core-java-io/src/test/java/com/baeldung/stream/StreamIndicesTest.java b/core-java-8/src/test/java/com/baeldung/stream/StreamIndicesTest.java similarity index 100% rename from core-java-io/src/test/java/com/baeldung/stream/StreamIndicesTest.java rename to core-java-8/src/test/java/com/baeldung/stream/StreamIndicesTest.java diff --git a/core-java-io/src/test/java/com/baeldung/string/StringToCharStreamUnitTest.java b/core-java-8/src/test/java/com/baeldung/string/StringToCharStreamUnitTest.java similarity index 100% rename from core-java-io/src/test/java/com/baeldung/string/StringToCharStreamUnitTest.java rename to core-java-8/src/test/java/com/baeldung/string/StringToCharStreamUnitTest.java diff --git a/core-java-io/README.md b/core-java-io/README.md index 79974ab523..52485acfd5 100644 --- a/core-java-io/README.md +++ b/core-java-io/README.md @@ -12,11 +12,8 @@ - [Java – Directory Size](http://www.baeldung.com/java-folder-size) - [Differences Between the Java WatchService API and the Apache Commons IO Monitor Library](http://www.baeldung.com/java-watchservice-vs-apache-commons-io-monitor-library) - [Calculate the Size of a File in Java](http://www.baeldung.com/java-file-size) -- [Iterable to Stream in Java](http://www.baeldung.com/java-iterable-to-stream) - [Comparing getPath(), getAbsolutePath(), and getCanonicalPath() in Java](http://www.baeldung.com/java-path) - [Using Java MappedByteBuffer](http://www.baeldung.com/java-mapped-byte-buffer) -- [Converting String to Stream of chars](http://www.baeldung.com/java-string-to-stream) -- [How to Iterate Over a Stream With Indices](http://www.baeldung.com/java-stream-indices) - [Copy a File with Java](http://www.baeldung.com/java-copy-file) - [Java – Append Data to a File](http://www.baeldung.com/java-append-to-file) - [FileNotFoundException in Java](http://www.baeldung.com/java-filenotfound-exception) From 7420aa7f6fd5c7661f69cea55e1ffc1d325e6848 Mon Sep 17 00:00:00 2001 From: Loredana Crusoveanu Date: Sat, 13 Jan 2018 18:18:41 +0200 Subject: [PATCH 08/22] move actuator 2 to spring-5-reactive --- spring-5-reactive/pom.xml | 4 ++++ .../actuator/DownstreamServiceReactiveHealthIndicator.java | 2 +- .../com/baeldung/reactive}/actuator/FeaturesEndpoint.java | 2 +- .../reactive}/actuator/InfoWebEndpointExtension.java | 2 +- .../src/main/resources/application.properties | 5 ++++- .../reactive}/actuator/ActuatorInfoIntegrationTest.java | 7 ++++--- spring-5/pom.xml | 4 ---- .../main/java/com/baeldung/security/SecurityConfig.java | 1 - spring-5/src/main/resources/application.properties | 2 -- 9 files changed, 15 insertions(+), 14 deletions(-) rename {spring-5/src/main/java/com/baeldung => spring-5-reactive/src/main/java/com/baeldung/reactive}/actuator/DownstreamServiceReactiveHealthIndicator.java (94%) rename {spring-5/src/main/java/com/baeldung => spring-5-reactive/src/main/java/com/baeldung/reactive}/actuator/FeaturesEndpoint.java (96%) rename {spring-5/src/main/java/com/baeldung => spring-5-reactive/src/main/java/com/baeldung/reactive}/actuator/InfoWebEndpointExtension.java (96%) rename {spring-5/src/test/java/com/baeldung => spring-5-reactive/src/test/java/com/baeldung/reactive}/actuator/ActuatorInfoIntegrationTest.java (88%) diff --git a/spring-5-reactive/pom.xml b/spring-5-reactive/pom.xml index e5b35de2f5..ec4312ab21 100644 --- a/spring-5-reactive/pom.xml +++ b/spring-5-reactive/pom.xml @@ -43,6 +43,10 @@ javax.json.bind javax.json.bind-api + + org.springframework.boot + spring-boot-starter-actuator + diff --git a/spring-5/src/main/java/com/baeldung/actuator/DownstreamServiceReactiveHealthIndicator.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/actuator/DownstreamServiceReactiveHealthIndicator.java similarity index 94% rename from spring-5/src/main/java/com/baeldung/actuator/DownstreamServiceReactiveHealthIndicator.java rename to spring-5-reactive/src/main/java/com/baeldung/reactive/actuator/DownstreamServiceReactiveHealthIndicator.java index 5f36330ff6..7360def71e 100644 --- a/spring-5/src/main/java/com/baeldung/actuator/DownstreamServiceReactiveHealthIndicator.java +++ b/spring-5-reactive/src/main/java/com/baeldung/reactive/actuator/DownstreamServiceReactiveHealthIndicator.java @@ -1,4 +1,4 @@ -package com.baeldung.actuator; +package com.baeldung.reactive.actuator; import org.springframework.boot.actuate.health.Health; import org.springframework.boot.actuate.health.ReactiveHealthIndicator; diff --git a/spring-5/src/main/java/com/baeldung/actuator/FeaturesEndpoint.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/actuator/FeaturesEndpoint.java similarity index 96% rename from spring-5/src/main/java/com/baeldung/actuator/FeaturesEndpoint.java rename to spring-5-reactive/src/main/java/com/baeldung/reactive/actuator/FeaturesEndpoint.java index 2ed32501ae..b2bc1e037f 100644 --- a/spring-5/src/main/java/com/baeldung/actuator/FeaturesEndpoint.java +++ b/spring-5-reactive/src/main/java/com/baeldung/reactive/actuator/FeaturesEndpoint.java @@ -1,4 +1,4 @@ -package com.baeldung.actuator; +package com.baeldung.reactive.actuator; import org.springframework.boot.actuate.endpoint.annotation.*; import org.springframework.stereotype.Component; diff --git a/spring-5/src/main/java/com/baeldung/actuator/InfoWebEndpointExtension.java b/spring-5-reactive/src/main/java/com/baeldung/reactive/actuator/InfoWebEndpointExtension.java similarity index 96% rename from spring-5/src/main/java/com/baeldung/actuator/InfoWebEndpointExtension.java rename to spring-5-reactive/src/main/java/com/baeldung/reactive/actuator/InfoWebEndpointExtension.java index acd92d1846..86502f0ab3 100644 --- a/spring-5/src/main/java/com/baeldung/actuator/InfoWebEndpointExtension.java +++ b/spring-5-reactive/src/main/java/com/baeldung/reactive/actuator/InfoWebEndpointExtension.java @@ -1,4 +1,4 @@ -package com.baeldung.actuator; +package com.baeldung.reactive.actuator; import org.springframework.boot.actuate.endpoint.annotation.ReadOperation; import org.springframework.boot.actuate.endpoint.web.WebEndpointResponse; diff --git a/spring-5-reactive/src/main/resources/application.properties b/spring-5-reactive/src/main/resources/application.properties index 4b49e8e8a2..5b9a0ae1ce 100644 --- a/spring-5-reactive/src/main/resources/application.properties +++ b/spring-5-reactive/src/main/resources/application.properties @@ -1 +1,4 @@ -logging.level.root=INFO \ No newline at end of file +logging.level.root=INFO + +management.endpoints.web.expose=* +info.app.name=Spring Boot 2 actuator Application \ No newline at end of file diff --git a/spring-5/src/test/java/com/baeldung/actuator/ActuatorInfoIntegrationTest.java b/spring-5-reactive/src/test/java/com/baeldung/reactive/actuator/ActuatorInfoIntegrationTest.java similarity index 88% rename from spring-5/src/test/java/com/baeldung/actuator/ActuatorInfoIntegrationTest.java rename to spring-5-reactive/src/test/java/com/baeldung/reactive/actuator/ActuatorInfoIntegrationTest.java index 964cf1a1ea..3020e86723 100644 --- a/spring-5/src/test/java/com/baeldung/actuator/ActuatorInfoIntegrationTest.java +++ b/spring-5-reactive/src/test/java/com/baeldung/reactive/actuator/ActuatorInfoIntegrationTest.java @@ -1,6 +1,5 @@ -package com.baeldung.actuator; +package com.baeldung.reactive.actuator; -import com.baeldung.jsonb.Spring5Application; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; @@ -10,12 +9,14 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.test.context.junit4.SpringRunner; +import com.baeldung.reactive.Spring5ReactiveApplication; + import java.io.IOException; import static org.junit.Assert.assertEquals; @RunWith(SpringRunner.class) -@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = Spring5Application.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = Spring5ReactiveApplication.class) public class ActuatorInfoIntegrationTest { @Autowired diff --git a/spring-5/pom.xml b/spring-5/pom.xml index 4c2df68f1b..19dd65d78f 100644 --- a/spring-5/pom.xml +++ b/spring-5/pom.xml @@ -43,10 +43,6 @@ org.springframework.boot spring-boot-starter-hateoas - - org.springframework.boot - spring-boot-starter-actuator - org.projectreactor reactor-spring diff --git a/spring-5/src/main/java/com/baeldung/security/SecurityConfig.java b/spring-5/src/main/java/com/baeldung/security/SecurityConfig.java index d31f1552fc..a9e44a2eee 100644 --- a/spring-5/src/main/java/com/baeldung/security/SecurityConfig.java +++ b/spring-5/src/main/java/com/baeldung/security/SecurityConfig.java @@ -17,7 +17,6 @@ public class SecurityConfig { public SecurityWebFilterChain securitygWebFilterChain(ServerHttpSecurity http) { return http.authorizeExchange() .pathMatchers("/admin").hasAuthority("ROLE_ADMIN") - .pathMatchers("/actuator/**").permitAll() .anyExchange().authenticated() .and().formLogin() .and().build(); diff --git a/spring-5/src/main/resources/application.properties b/spring-5/src/main/resources/application.properties index a7e3ec0d5a..ccec014c2b 100644 --- a/spring-5/src/main/resources/application.properties +++ b/spring-5/src/main/resources/application.properties @@ -1,5 +1,3 @@ server.port=8081 -management.endpoints.web.expose=* -info.app.name=Spring Boot 2 actuator Application logging.level.root=INFO \ No newline at end of file From 5a40e7ec48d0b6949054109b0fe2d7eb144cd24f Mon Sep 17 00:00:00 2001 From: Loredana Crusoveanu Date: Sat, 13 Jan 2018 18:39:56 +0200 Subject: [PATCH 09/22] Update pom.xml --- spring-5-reactive/pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/spring-5-reactive/pom.xml b/spring-5-reactive/pom.xml index ec4312ab21..36eaaa1530 100644 --- a/spring-5-reactive/pom.xml +++ b/spring-5-reactive/pom.xml @@ -202,4 +202,5 @@ 1.0 + From cd4b5267895f64478140876b485d352fc3fb96ee Mon Sep 17 00:00:00 2001 From: Denis Date: Sat, 13 Jan 2018 19:57:53 +0300 Subject: [PATCH 10/22] BAEL-1451 Writing a Jenkins plugin (#3396) * BAEL-1451 Writing a Jenkins plugin A sample Jenkins plugin which builds basic project stats * BAEL-1451 Writing a Jenkins plugin Formatting --- jenkins/hello-world/pom.xml | 89 +++++++++++++ .../jenkins/helloworld/ProjectStats.java | 20 +++ .../helloworld/ProjectStatsBuildWrapper.java | 123 ++++++++++++++++++ .../hello-world/src/main/resources/stats.html | 20 +++ 4 files changed, 252 insertions(+) create mode 100644 jenkins/hello-world/pom.xml create mode 100644 jenkins/hello-world/src/main/java/com/baeldung/jenkins/helloworld/ProjectStats.java create mode 100644 jenkins/hello-world/src/main/java/com/baeldung/jenkins/helloworld/ProjectStatsBuildWrapper.java create mode 100644 jenkins/hello-world/src/main/resources/stats.html diff --git a/jenkins/hello-world/pom.xml b/jenkins/hello-world/pom.xml new file mode 100644 index 0000000000..15cf2f8e34 --- /dev/null +++ b/jenkins/hello-world/pom.xml @@ -0,0 +1,89 @@ + + + 4.0.0 + + org.jenkins-ci.plugins + plugin + 2.33 + + + jenkins-hello-world + 1.0-SNAPSHOT + hpi + + + 2.7.3 + + Hello World Plugin + A sample Jenkins Hello World plugin + https://wiki.jenkins-ci.org/display/JENKINS/TODO+Plugin + + + MIT License + http://opensource.org/licenses/MIT + + + + + org.jenkins-ci.plugins + structs + 1.7 + + + org.jenkins-ci.plugins.workflow + workflow-step-api + 2.12 + test + + + org.jenkins-ci.plugins.workflow + workflow-cps + 2.39 + test + + + org.jenkins-ci.plugins.workflow + workflow-job + 2.11.2 + test + + + org.jenkins-ci.plugins.workflow + workflow-basic-steps + 2.6 + test + + + org.jenkins-ci.plugins.workflow + workflow-durable-task-step + 2.13 + test + + + org.jenkins-ci.plugins.workflow + workflow-api + 2.20 + test + + + org.jenkins-ci.plugins.workflow + workflow-support + 2.14 + test + + + + + + repo.jenkins-ci.org + https://repo.jenkins-ci.org/public/ + + + + + + repo.jenkins-ci.org + https://repo.jenkins-ci.org/public/ + + + diff --git a/jenkins/hello-world/src/main/java/com/baeldung/jenkins/helloworld/ProjectStats.java b/jenkins/hello-world/src/main/java/com/baeldung/jenkins/helloworld/ProjectStats.java new file mode 100644 index 0000000000..67af636bb4 --- /dev/null +++ b/jenkins/hello-world/src/main/java/com/baeldung/jenkins/helloworld/ProjectStats.java @@ -0,0 +1,20 @@ +package com.baeldung.jenkins.helloworld; + +public class ProjectStats { + + private final int classesNumber; + private final int linesNumber; + + public ProjectStats(int classesNumber, int linesNumber) { + this.classesNumber = classesNumber; + this.linesNumber = linesNumber; + } + + public int getClassesNumber() { + return classesNumber; + } + + public int getLinesNumber() { + return linesNumber; + } +} diff --git a/jenkins/hello-world/src/main/java/com/baeldung/jenkins/helloworld/ProjectStatsBuildWrapper.java b/jenkins/hello-world/src/main/java/com/baeldung/jenkins/helloworld/ProjectStatsBuildWrapper.java new file mode 100644 index 0000000000..9a7213c76f --- /dev/null +++ b/jenkins/hello-world/src/main/java/com/baeldung/jenkins/helloworld/ProjectStatsBuildWrapper.java @@ -0,0 +1,123 @@ +package com.baeldung.jenkins.helloworld; + +import hudson.Extension; +import hudson.FilePath; +import hudson.Launcher; +import hudson.model.AbstractBuild; +import hudson.model.AbstractProject; +import hudson.model.BuildListener; +import hudson.tasks.BuildWrapper; +import hudson.tasks.BuildWrapperDescriptor; +import org.kohsuke.stapler.DataBoundConstructor; + +import javax.annotation.Nonnull; +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.util.Stack; + +public class ProjectStatsBuildWrapper extends BuildWrapper { + + private static final String REPORT_TEMPLATE_PATH = "/stats.html"; + private static final String PROJECT_NAME_VAR = "$PROJECT_NAME$"; + private static final String CLASSES_NUMBER_VAR = "$CLASSES_NUMBER$"; + private static final String LINES_NUMBER_VAR = "$LINES_NUMBER$"; + + @DataBoundConstructor + public ProjectStatsBuildWrapper() { + } + + @Override + public Environment setUp(AbstractBuild build, final Launcher launcher, BuildListener listener) { + return new Environment() { + @Override + public boolean tearDown(AbstractBuild build, BuildListener listener) + throws IOException, InterruptedException + { + ProjectStats stats = buildStats(build.getWorkspace()); + String report = generateReport(build.getProject().getDisplayName(), stats); + File artifactsDir = build.getArtifactsDir(); + if (!artifactsDir.isDirectory()) { + boolean success = artifactsDir.mkdirs(); + if (!success) { + listener.getLogger().println("Can't create artifacts directory at " + + artifactsDir.getAbsolutePath()); + } + } + String path = artifactsDir.getCanonicalPath() + REPORT_TEMPLATE_PATH; + try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(path), + StandardCharsets.UTF_8))) { + writer.write(report); + } + return super.tearDown(build, listener); + } + }; + } + + private static ProjectStats buildStats(FilePath root) throws IOException, InterruptedException { + int classesNumber = 0; + int linesNumber = 0; + Stack toProcess = new Stack<>(); + toProcess.push(root); + while (!toProcess.isEmpty()) { + FilePath path = toProcess.pop(); + if (path.isDirectory()) { + toProcess.addAll(path.list()); + } else if (path.getName().endsWith(".java")) { + classesNumber++; + linesNumber += countLines(path); + } + } + return new ProjectStats(classesNumber, linesNumber); + } + + private static int countLines(FilePath path) throws IOException, InterruptedException { + byte[] buffer = new byte[1024]; + int result = 1; + try (InputStream in = path.read()) { + while (true) { + int read = in.read(buffer); + if (read < 0) { + return result; + } + for (int i = 0; i < read; i++) { + if (buffer[i] == '\n') { + result++; + } + } + } + } + } + + private static String generateReport(String projectName, ProjectStats stats) throws IOException { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + try (InputStream in = ProjectStatsBuildWrapper.class.getResourceAsStream(REPORT_TEMPLATE_PATH)) { + byte[] buffer = new byte[1024]; + int read; + while ((read = in.read(buffer)) >= 0) { + bOut.write(buffer, 0, read); + } + } + String content = new String(bOut.toByteArray(), StandardCharsets.UTF_8); + content = content.replace(PROJECT_NAME_VAR, projectName); + content = content.replace(CLASSES_NUMBER_VAR, String.valueOf(stats.getClassesNumber())); + content = content.replace(LINES_NUMBER_VAR, String.valueOf(stats.getLinesNumber())); + return content; + } + + @Extension + public static final class DescriptorImpl extends BuildWrapperDescriptor { + + @Override + public boolean isApplicable(AbstractProject item) { + return true; + } + + @Nonnull + @Override + public String getDisplayName() { + return "Construct project stats during build"; + } + + } + +} diff --git a/jenkins/hello-world/src/main/resources/stats.html b/jenkins/hello-world/src/main/resources/stats.html new file mode 100644 index 0000000000..2a14b5de3f --- /dev/null +++ b/jenkins/hello-world/src/main/resources/stats.html @@ -0,0 +1,20 @@ + + + + + $PROJECT_NAME$ + + +Project $PROJECT_NAME$: + + + + + + + + + +
Classes numberLines number
$CLASSES_NUMBER$$LINES_NUMBER$
+ + \ No newline at end of file From b41ebab6b8dc0c7a95873329633dc68cd649db24 Mon Sep 17 00:00:00 2001 From: felipeazv Date: Fri, 12 Jan 2018 23:04:21 +0100 Subject: [PATCH 11/22] BAEL-1475: Reactive WebSockets with Spring 5 --- spring-reactive-websocket/pom.xml | 92 +++++++++++++++++++ .../src/main/java/com/baeldung/Event.java | 11 +++ .../ReactiveWebSocketApplication.java | 38 ++++++++ .../ReactiveWebSocketConfiguration.java | 34 +++++++ .../baeldung/ReactiveWebSocketHandler.java | 71 ++++++++++++++ .../resources/static/client-websocket.html | 34 +++++++ 6 files changed, 280 insertions(+) create mode 100644 spring-reactive-websocket/pom.xml create mode 100644 spring-reactive-websocket/src/main/java/com/baeldung/Event.java create mode 100644 spring-reactive-websocket/src/main/java/com/baeldung/ReactiveWebSocketApplication.java create mode 100644 spring-reactive-websocket/src/main/java/com/baeldung/ReactiveWebSocketConfiguration.java create mode 100644 spring-reactive-websocket/src/main/java/com/baeldung/ReactiveWebSocketHandler.java create mode 100644 spring-reactive-websocket/src/main/resources/static/client-websocket.html diff --git a/spring-reactive-websocket/pom.xml b/spring-reactive-websocket/pom.xml new file mode 100644 index 0000000000..b234e0345e --- /dev/null +++ b/spring-reactive-websocket/pom.xml @@ -0,0 +1,92 @@ + + + 4.0.0 + + spring-reactive-websocket + 0.0.1-SNAPSHOT + jar + + spring-reactive-websocket + Example from article: Reactive WebSockets with Spring 5 + + + com.baeldung + parent-modules + 1.0.0-SNAPSHOT + + + + UTF-8 + UTF-8 + 1.8 + + + + + org.springframework.boot + spring-boot-starter-integration + 2.0.0.M7 + + + org.springframework.boot + spring-boot-starter-webflux + 2.0.0.M7 + + + org.projectlombok + lombok + compile + RELEASE + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + + spring-snapshots + Spring Snapshots + https://repo.spring.io/snapshot + + true + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + diff --git a/spring-reactive-websocket/src/main/java/com/baeldung/Event.java b/spring-reactive-websocket/src/main/java/com/baeldung/Event.java new file mode 100644 index 0000000000..20d678c214 --- /dev/null +++ b/spring-reactive-websocket/src/main/java/com/baeldung/Event.java @@ -0,0 +1,11 @@ +package com.baeldung; + +import lombok.AllArgsConstructor; +import lombok.Data; + +@Data +@AllArgsConstructor +public class Event { + private String eventId; + private String eventDt; +} diff --git a/spring-reactive-websocket/src/main/java/com/baeldung/ReactiveWebSocketApplication.java b/spring-reactive-websocket/src/main/java/com/baeldung/ReactiveWebSocketApplication.java new file mode 100644 index 0000000000..f8952d750d --- /dev/null +++ b/spring-reactive-websocket/src/main/java/com/baeldung/ReactiveWebSocketApplication.java @@ -0,0 +1,38 @@ +package com.baeldung; + +import java.net.URI; +import java.time.Duration; + +import org.springframework.boot.CommandLineRunner; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; +import org.springframework.web.reactive.socket.WebSocketMessage; +import org.springframework.web.reactive.socket.client.ReactorNettyWebSocketClient; +import org.springframework.web.reactive.socket.client.WebSocketClient; + +import reactor.core.publisher.Mono; + +@SpringBootApplication +public class ReactiveWebSocketApplication { + public static void main(String[] args) { + SpringApplication.run(ReactiveWebSocketApplication.class, args); + } + + /** + * Spring Reactive WebSocket Client + * **/ + @Bean + CommandLineRunner runner() { + return run -> { + WebSocketClient client = new ReactorNettyWebSocketClient(); + client.execute(URI.create("ws://localhost:8080/event-emitter"), session -> session.send(Mono.just(session.textMessage("event-me-from-spring-reactive-client"))) + .thenMany(session.receive() + .map(WebSocketMessage::getPayloadAsText) + .log()) + .then()) + .block(); +// .block(Duration.ofSeconds(10L));//force timeout after given duration + }; + } +} diff --git a/spring-reactive-websocket/src/main/java/com/baeldung/ReactiveWebSocketConfiguration.java b/spring-reactive-websocket/src/main/java/com/baeldung/ReactiveWebSocketConfiguration.java new file mode 100644 index 0000000000..6729e09273 --- /dev/null +++ b/spring-reactive-websocket/src/main/java/com/baeldung/ReactiveWebSocketConfiguration.java @@ -0,0 +1,34 @@ +package com.baeldung; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.reactive.HandlerMapping; +import org.springframework.web.reactive.handler.SimpleUrlHandlerMapping; +import org.springframework.web.reactive.socket.WebSocketHandler; +import org.springframework.web.reactive.socket.server.support.WebSocketHandlerAdapter; +import java.util.HashMap; +import java.util.Map; + +@Configuration +public class ReactiveWebSocketConfiguration { + + @Autowired + private WebSocketHandler webSocketHandler; + + @Bean + public HandlerMapping webSocketHandlerMapping() { + Map map = new HashMap<>(); + map.put("/event-emitter", webSocketHandler); + + SimpleUrlHandlerMapping handlerMapping = new SimpleUrlHandlerMapping(); + handlerMapping.setOrder(1); + handlerMapping.setUrlMap(map); + return handlerMapping; + } + + @Bean + public WebSocketHandlerAdapter handlerAdapter() { + return new WebSocketHandlerAdapter(); + } +} \ No newline at end of file diff --git a/spring-reactive-websocket/src/main/java/com/baeldung/ReactiveWebSocketHandler.java b/spring-reactive-websocket/src/main/java/com/baeldung/ReactiveWebSocketHandler.java new file mode 100644 index 0000000000..4a548322b3 --- /dev/null +++ b/spring-reactive-websocket/src/main/java/com/baeldung/ReactiveWebSocketHandler.java @@ -0,0 +1,71 @@ +package com.baeldung; + +import org.springframework.web.reactive.socket.WebSocketSession; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.socket.WebSocketHandler; +import org.springframework.web.reactive.socket.WebSocketMessage; + +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import javax.annotation.PostConstruct; +import java.time.Duration; +import java.time.LocalDateTime; +import java.util.UUID; + +@Component +public class ReactiveWebSocketHandler implements WebSocketHandler { + + private Flux eventFlux; + private Flux intervalFlux; + + /** + * Here we prepare a Flux that will emit a message every second + */ + @PostConstruct + private void init() throws InterruptedException { + + eventFlux = Flux.generate(e -> { + Event event = new Event(UUID.randomUUID() + .toString(), + LocalDateTime.now() + .toString()); + e.next(event); + }); + + intervalFlux = Flux.interval(Duration.ofMillis(1000L)) + .zipWith(eventFlux, (time, event) -> event); + + } + + /** + * On each new client session, send the message flux to the client. + * Spring subscribes to the flux and send every new flux event to the WebSocketSession object + * @param session + * @return Mono + */ + @Override + public Mono handle(WebSocketSession webSocketSession) { + ObjectMapper json = new ObjectMapper(); + return webSocketSession.send(intervalFlux.map(event -> { + try { + String jsonEvent = json.writeValueAsString(event); + System.out.println(jsonEvent); + return jsonEvent; + } catch (JsonProcessingException e) { + e.printStackTrace(); + return ""; + } + }) + .map(webSocketSession::textMessage)) + + .and(webSocketSession.receive() + .map(WebSocketMessage::getPayloadAsText) + .log()); + } + +} diff --git a/spring-reactive-websocket/src/main/resources/static/client-websocket.html b/spring-reactive-websocket/src/main/resources/static/client-websocket.html new file mode 100644 index 0000000000..3f840e8bd4 --- /dev/null +++ b/spring-reactive-websocket/src/main/resources/static/client-websocket.html @@ -0,0 +1,34 @@ + + + + +Baeldung: Spring 5 Reactive Client WebSocket (Browser) + + + +
+ + + \ No newline at end of file From 466340e96f49897519ffe0d94eef2ccd3320fa2a Mon Sep 17 00:00:00 2001 From: felipeazv Date: Fri, 12 Jan 2018 23:12:20 +0100 Subject: [PATCH 12/22] BAEL-1475: Reactive WebSockets with Spring 5 --- spring-reactive-websocket/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-reactive-websocket/pom.xml b/spring-reactive-websocket/pom.xml index b234e0345e..846cece177 100644 --- a/spring-reactive-websocket/pom.xml +++ b/spring-reactive-websocket/pom.xml @@ -8,7 +8,7 @@ jar spring-reactive-websocket - Example from article: Reactive WebSockets with Spring 5 + Reactive WebSockets with Spring 5 com.baeldung From 33f7b1a3b983157dbfb17f14272e190ec792f7f7 Mon Sep 17 00:00:00 2001 From: Nikhil Khatwani Date: Sun, 14 Jan 2018 04:26:24 +0530 Subject: [PATCH 13/22] Changes for BAEL-1469 Upgrade the Spring MVC article (#3407) --- spring-mvc-xml/pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spring-mvc-xml/pom.xml b/spring-mvc-xml/pom.xml index 2131609ff6..049a3fec82 100644 --- a/spring-mvc-xml/pom.xml +++ b/spring-mvc-xml/pom.xml @@ -110,7 +110,7 @@ - 4.3.4.RELEASE + 5.0.2.RELEASE 4.2.0.RELEASE @@ -142,4 +142,4 @@ - \ No newline at end of file + From a4c02d6275f238d4dc948702ebbbfaa8149a08cc Mon Sep 17 00:00:00 2001 From: Eugen Paraschiv Date: Sun, 14 Jan 2018 00:59:55 +0200 Subject: [PATCH 14/22] minor formatting cleanup --- .../java/com/baeldung/jsp/ExampleOne.java | 30 +++++++------------ .../java/com/baeldung/jsp/ExampleThree.java | 22 ++++++-------- .../spring/controller/ErrorController.java | 9 +++--- .../controller/GeoIPTestController.java | 12 ++++---- .../spring/controller/ImageController.java | 1 - .../java/com/baeldung/spring/form/GeoIP.java | 10 +++---- .../RawDBDemoGeoIPLocationService.java | 8 ++--- .../baeldung/geoip/GeoIpIntegrationTest.java | 11 ++++--- 8 files changed, 43 insertions(+), 60 deletions(-) diff --git a/spring-mvc-xml/src/main/java/com/baeldung/jsp/ExampleOne.java b/spring-mvc-xml/src/main/java/com/baeldung/jsp/ExampleOne.java index 0b153bf8ec..6744570639 100644 --- a/spring-mvc-xml/src/main/java/com/baeldung/jsp/ExampleOne.java +++ b/spring-mvc-xml/src/main/java/com/baeldung/jsp/ExampleOne.java @@ -8,24 +8,14 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class ExampleOne extends HttpServlet { - - private static final long serialVersionUID = 1L; - - @Override - protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - response.setContentType("text/html"); - PrintWriter out = response.getWriter(); - out.println( - "" + - "" + - "" + - "HTML Rendered by Servlet" + - "" + - "" + - "

HTML Rendered by Servlet


" + - "

This page was rendered by the ExampleOne Servlet!

" + - "" + - "" - ); - } + + private static final long serialVersionUID = 1L; + + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + response.setContentType("text/html"); + PrintWriter out = response.getWriter(); + out.println("" + "" + "" + "HTML Rendered by Servlet" + "" + "" + "

HTML Rendered by Servlet


" + "

This page was rendered by the ExampleOne Servlet!

" + + "" + ""); + } } \ No newline at end of file diff --git a/spring-mvc-xml/src/main/java/com/baeldung/jsp/ExampleThree.java b/spring-mvc-xml/src/main/java/com/baeldung/jsp/ExampleThree.java index 49fefcffde..7269f917b4 100644 --- a/spring-mvc-xml/src/main/java/com/baeldung/jsp/ExampleThree.java +++ b/spring-mvc-xml/src/main/java/com/baeldung/jsp/ExampleThree.java @@ -7,18 +7,14 @@ import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -@WebServlet( - name = "ExampleThree", - description = "JSP Servlet With Annotations", - urlPatterns = {"/ExampleThree"} -) +@WebServlet(name = "ExampleThree", description = "JSP Servlet With Annotations", urlPatterns = { "/ExampleThree" }) public class ExampleThree extends HttpServlet { - private static final long serialVersionUID = 1L; - - @Override - protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - String message = request.getParameter("message"); - request.setAttribute("text", message); - request.getRequestDispatcher("/jsp/ExampleThree.jsp").forward(request, response); - } + private static final long serialVersionUID = 1L; + + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + String message = request.getParameter("message"); + request.setAttribute("text", message); + request.getRequestDispatcher("/jsp/ExampleThree.jsp").forward(request, response); + } } diff --git a/spring-mvc-xml/src/main/java/com/baeldung/spring/controller/ErrorController.java b/spring-mvc-xml/src/main/java/com/baeldung/spring/controller/ErrorController.java index 96556bd5b1..6ae1023374 100644 --- a/spring-mvc-xml/src/main/java/com/baeldung/spring/controller/ErrorController.java +++ b/spring-mvc-xml/src/main/java/com/baeldung/spring/controller/ErrorController.java @@ -9,7 +9,7 @@ import org.springframework.web.servlet.ModelAndView; @Controller public class ErrorController { - + @RequestMapping(value = "500Error", method = RequestMethod.GET) public void throwRuntimeException() { throw new NullPointerException("Throwing a null pointer exception"); @@ -34,19 +34,18 @@ public class ErrorController { errorMsg = "Http Error Code : 404. Resource not found"; break; } - // Handle other 4xx error codes. + // Handle other 4xx error codes. case 500: { errorMsg = "Http Error Code : 500. Internal Server Error"; break; } - // Handle other 5xx error codes. + // Handle other 5xx error codes. } errorPage.addObject("errorMsg", errorMsg); return errorPage; } private int getErrorCode(HttpServletRequest httpRequest) { - return (Integer) httpRequest - .getAttribute("javax.servlet.error.status_code"); + return (Integer) httpRequest.getAttribute("javax.servlet.error.status_code"); } } diff --git a/spring-mvc-xml/src/main/java/com/baeldung/spring/controller/GeoIPTestController.java b/spring-mvc-xml/src/main/java/com/baeldung/spring/controller/GeoIPTestController.java index 16de4e56f5..eeaddcf8e0 100644 --- a/spring-mvc-xml/src/main/java/com/baeldung/spring/controller/GeoIPTestController.java +++ b/spring-mvc-xml/src/main/java/com/baeldung/spring/controller/GeoIPTestController.java @@ -14,15 +14,15 @@ import com.baeldung.spring.service.RawDBDemoGeoIPLocationService; @Controller public class GeoIPTestController { private RawDBDemoGeoIPLocationService locationService; + public GeoIPTestController() throws IOException { - locationService - = new RawDBDemoGeoIPLocationService(); + locationService = new RawDBDemoGeoIPLocationService(); } - @RequestMapping(value="/GeoIPTest", method = RequestMethod.POST) + + @RequestMapping(value = "/GeoIPTest", method = RequestMethod.POST) @ResponseBody - public GeoIP getLocation( - @RequestParam(value="ipAddress", required=true) String ipAddress) throws Exception { - + public GeoIP getLocation(@RequestParam(value = "ipAddress", required = true) String ipAddress) throws Exception { + return locationService.getLocation(ipAddress); } } diff --git a/spring-mvc-xml/src/main/java/com/baeldung/spring/controller/ImageController.java b/spring-mvc-xml/src/main/java/com/baeldung/spring/controller/ImageController.java index ef8d1214df..fc46c07e06 100644 --- a/spring-mvc-xml/src/main/java/com/baeldung/spring/controller/ImageController.java +++ b/spring-mvc-xml/src/main/java/com/baeldung/spring/controller/ImageController.java @@ -12,7 +12,6 @@ import org.springframework.web.context.support.ServletContextResource; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletResponse; -import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; diff --git a/spring-mvc-xml/src/main/java/com/baeldung/spring/form/GeoIP.java b/spring-mvc-xml/src/main/java/com/baeldung/spring/form/GeoIP.java index 19f56867a1..4373303107 100644 --- a/spring-mvc-xml/src/main/java/com/baeldung/spring/form/GeoIP.java +++ b/spring-mvc-xml/src/main/java/com/baeldung/spring/form/GeoIP.java @@ -5,15 +5,15 @@ public class GeoIP { private String city; private String latitude; private String longitude; - + public GeoIP() { - + } - + public GeoIP(String ipAddress) { this.ipAddress = ipAddress; } - + public GeoIP(String ipAddress, String city, String latitude, String longitude) { this.ipAddress = ipAddress; this.city = city; @@ -52,5 +52,5 @@ public class GeoIP { public void setLongitude(String longitude) { this.longitude = longitude; } - + } diff --git a/spring-mvc-xml/src/main/java/com/baeldung/spring/service/RawDBDemoGeoIPLocationService.java b/spring-mvc-xml/src/main/java/com/baeldung/spring/service/RawDBDemoGeoIPLocationService.java index af3ce8cfb3..04443466c9 100644 --- a/spring-mvc-xml/src/main/java/com/baeldung/spring/service/RawDBDemoGeoIPLocationService.java +++ b/spring-mvc-xml/src/main/java/com/baeldung/spring/service/RawDBDemoGeoIPLocationService.java @@ -9,18 +9,18 @@ import com.maxmind.geoip2.DatabaseReader; import com.maxmind.geoip2.exception.GeoIp2Exception; import com.maxmind.geoip2.model.CityResponse; -public class RawDBDemoGeoIPLocationService{ +public class RawDBDemoGeoIPLocationService { private DatabaseReader dbReader; - + public RawDBDemoGeoIPLocationService() throws IOException { File database = new File("your-path-to-db-file"); dbReader = new DatabaseReader.Builder(database).build(); } - + public GeoIP getLocation(String ip) throws IOException, GeoIp2Exception { InetAddress ipAddress = InetAddress.getByName(ip); CityResponse response = dbReader.city(ipAddress); - + String cityName = response.getCity().getName(); String latitude = response.getLocation().getLatitude().toString(); String longitude = response.getLocation().getLongitude().toString(); diff --git a/spring-mvc-xml/src/test/java/com/baeldung/geoip/GeoIpIntegrationTest.java b/spring-mvc-xml/src/test/java/com/baeldung/geoip/GeoIpIntegrationTest.java index 2edaa125b7..0e957f3400 100644 --- a/spring-mvc-xml/src/test/java/com/baeldung/geoip/GeoIpIntegrationTest.java +++ b/spring-mvc-xml/src/test/java/com/baeldung/geoip/GeoIpIntegrationTest.java @@ -10,22 +10,21 @@ import com.maxmind.geoip2.DatabaseReader; import com.maxmind.geoip2.exception.GeoIp2Exception; import com.maxmind.geoip2.model.CityResponse; - public class GeoIpIntegrationTest { - + @Test public void givenIP_whenFetchingCity_thenReturnsCityData() throws IOException, GeoIp2Exception { File database = new File("your-path-to-db-file"); DatabaseReader dbReader = new DatabaseReader.Builder(database).build(); - + InetAddress ipAddress = InetAddress.getByName("your-public-ip"); CityResponse response = dbReader.city(ipAddress); - + String countryName = response.getCountry().getName(); String cityName = response.getCity().getName(); String postal = response.getPostal().getCode(); String state = response.getLeastSpecificSubdivision().getName(); - + } - + } From 5b8e84b26ccb063e67e9f21be53d0740820ed808 Mon Sep 17 00:00:00 2001 From: Nikhil Khatwani Date: Sun, 14 Jan 2018 20:30:32 +0530 Subject: [PATCH 15/22] Refaactoring-Thread Interrupt (#3412) --- .../com/baeldung/concurrent/stopping/ControlSubThread.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core-java-concurrency/src/main/java/com/baeldung/concurrent/stopping/ControlSubThread.java b/core-java-concurrency/src/main/java/com/baeldung/concurrent/stopping/ControlSubThread.java index 0e72821a88..e6522168bb 100644 --- a/core-java-concurrency/src/main/java/com/baeldung/concurrent/stopping/ControlSubThread.java +++ b/core-java-concurrency/src/main/java/com/baeldung/concurrent/stopping/ControlSubThread.java @@ -43,8 +43,9 @@ public class ControlSubThread implements Runnable { try { Thread.sleep(interval); } catch (InterruptedException e) { - // no-op, just loop again - } + Thread.currentThread().interrupt(); + System.out.println("Thread was interrupted, Failed to complete operation"); + } // do something } stopped.set(true); From fb141b9b3fac840db0292199c95a269745e02a3f Mon Sep 17 00:00:00 2001 From: Nikhil Khatwani Date: Mon, 15 Jan 2018 00:33:18 +0530 Subject: [PATCH 16/22] Bael 1469 (#3414) * Changes for BAEL-1469 Upgrade the Spring MVC article * web.xml changes for servlet 3.1 --- spring-mvc-xml/src/main/webapp/WEB-INF/web.xml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/spring-mvc-xml/src/main/webapp/WEB-INF/web.xml b/spring-mvc-xml/src/main/webapp/WEB-INF/web.xml index 1ea3051426..6ff435b84b 100644 --- a/spring-mvc-xml/src/main/webapp/WEB-INF/web.xml +++ b/spring-mvc-xml/src/main/webapp/WEB-INF/web.xml @@ -1,10 +1,9 @@ - - Spring MVC XML Application @@ -65,4 +64,4 @@ /errors - \ No newline at end of file + From 0d4a49f4f1ea17d6acbb42fd0935193df374fb9f Mon Sep 17 00:00:00 2001 From: Loredana Crusoveanu Date: Sun, 14 Jan 2018 22:27:54 +0200 Subject: [PATCH 17/22] Create README.md --- spring-5-security/README.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 spring-5-security/README.md diff --git a/spring-5-security/README.md b/spring-5-security/README.md new file mode 100644 index 0000000000..1c9fad65e4 --- /dev/null +++ b/spring-5-security/README.md @@ -0,0 +1,3 @@ +## Relevant articles: + +- [Spring Security 5 -OAuth2 Login](http://www.baeldung.com/spring-security-5-oauth2-login) From 22dbf2c8cd363c25220e03a194e31f8c3502f536 Mon Sep 17 00:00:00 2001 From: Eric Goebelbecker Date: Sun, 14 Jan 2018 16:48:42 -0500 Subject: [PATCH 18/22] Bael-1364 - Introduction to Lettuce (#3415) --- .../influxdb/InfluxDBConnectionLiveTest.java | 2 +- persistence-modules/redis/README.md | 2 + persistence-modules/redis/pom.xml | 7 + .../baeldung/LettuceIntegrationLiveTest.java | 312 ++++++++++++++++++ 4 files changed, 322 insertions(+), 1 deletion(-) create mode 100644 persistence-modules/redis/src/test/java/com/baeldung/LettuceIntegrationLiveTest.java diff --git a/influxdb/src/test/java/com/baeldung/influxdb/InfluxDBConnectionLiveTest.java b/influxdb/src/test/java/com/baeldung/influxdb/InfluxDBConnectionLiveTest.java index 50d35b9b1c..6c7a03cb70 100644 --- a/influxdb/src/test/java/com/baeldung/influxdb/InfluxDBConnectionLiveTest.java +++ b/influxdb/src/test/java/com/baeldung/influxdb/InfluxDBConnectionLiveTest.java @@ -55,7 +55,7 @@ public class InfluxDBConnectionLiveTest { InfluxDB connection = connectDatabase(); - // Create "baeldung and check for it + // Create "baeldung" and check for it connection.createDatabase("baeldung"); assertTrue(connection.databaseExists("baeldung")); diff --git a/persistence-modules/redis/README.md b/persistence-modules/redis/README.md index d179b80c33..dd655ca7aa 100644 --- a/persistence-modules/redis/README.md +++ b/persistence-modules/redis/README.md @@ -1,3 +1,5 @@ ### Relevant Articles: - [Intro to Jedis – the Java Redis Client Library](http://www.baeldung.com/jedis-java-redis-client-library) - [A Guide to Redis with Redisson](http://www.baeldung.com/redis-redisson) +- [Intro to Lettuce – the Java Redis Client Library](http://www.baeldung.com/lettuce-java-redis-client-library) + diff --git a/persistence-modules/redis/pom.xml b/persistence-modules/redis/pom.xml index 4321b491eb..1f27faa09a 100644 --- a/persistence-modules/redis/pom.xml +++ b/persistence-modules/redis/pom.xml @@ -36,6 +36,13 @@ redisson 3.3.0
+ + + io.lettuce + lettuce-core + 5.0.1.RELEASE + + diff --git a/persistence-modules/redis/src/test/java/com/baeldung/LettuceIntegrationLiveTest.java b/persistence-modules/redis/src/test/java/com/baeldung/LettuceIntegrationLiveTest.java new file mode 100644 index 0000000000..eb879d1d21 --- /dev/null +++ b/persistence-modules/redis/src/test/java/com/baeldung/LettuceIntegrationLiveTest.java @@ -0,0 +1,312 @@ +package com.baeldung; + +import io.lettuce.core.LettuceFutures; +import io.lettuce.core.RedisClient; +import io.lettuce.core.RedisFuture; +import io.lettuce.core.TransactionResult; +import io.lettuce.core.api.StatefulRedisConnection; +import io.lettuce.core.api.async.RedisAsyncCommands; +import io.lettuce.core.api.sync.RedisCommands; +import io.lettuce.core.pubsub.RedisPubSubListener; +import io.lettuce.core.pubsub.StatefulRedisPubSubConnection; +import io.lettuce.core.pubsub.api.async.RedisPubSubAsyncCommands; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +import static org.junit.Assert.assertTrue; + +public class LettuceIntegrationLiveTest { + + private static Logger log = LoggerFactory.getLogger(LettuceIntegrationLiveTest.class); + + private static StatefulRedisConnection redisConnection; + + private static RedisClient redisClient; + + @BeforeClass + public static void setUp() { + // Docker defaults to mapping redis port to 32768 + redisClient = RedisClient.create("redis://localhost:32768/"); + redisConnection = redisClient.connect(); + } + + @AfterClass + public static void destroy() { + redisConnection.close(); + } + + @Test + public void givenAString_thenSaveItAsRedisStringsSync() { + + RedisCommands syncCommands = redisConnection.sync(); + + String key = "key"; + String value = "value"; + + syncCommands.set(key, value); + String response = syncCommands.get(key); + + Assert.assertEquals(value, response); + } + + @Test + public void givenValues_thenSaveAsRedisHashSync() { + + RedisCommands syncCommands = redisConnection.sync(); + + String recordName = "record1"; + String name = "FirstName"; + String value = "John"; + String surname = "LastName"; + String value1 = "Smith"; + + syncCommands.hset(recordName, name, value); + syncCommands.hset(recordName, surname, value1); + Map record = syncCommands.hgetall(recordName); + + Assert.assertEquals(record.get(name), value); + Assert.assertEquals(record.get(surname), value1); + } + + @Test + public void givenAString_thenSaveItAsRedisStringsAsync() throws Exception { + + RedisAsyncCommands asyncCommands = redisConnection.async(); + + String key = "key"; + String value = "value"; + + asyncCommands.set(key, value); + RedisFuture redisFuture = asyncCommands.get(key); + + String response = redisFuture.get(); + + Assert.assertEquals(value, response); + } + + @Test + public void givenValues_thenSaveAsRedisHashAsync() throws Exception { + + RedisAsyncCommands asyncCommands = redisConnection.async(); + + String recordName = "record1"; + String name = "FirstName"; + String value = "John"; + String surname = "LastName"; + String value1 = "Smith"; + + asyncCommands.hset(recordName, name, value); + asyncCommands.hset(recordName, surname, value1); + RedisFuture> redisFuture = asyncCommands.hgetall(recordName); + + Map record = redisFuture.get(); + + Assert.assertEquals(record.get(name), value); + Assert.assertEquals(record.get(surname), value1); + } + + @Test + public void givenValues_thenSaveAsRedisListAsync() throws Exception { + + RedisAsyncCommands asyncCommands = redisConnection.async(); + + String listName = "tasks"; + String firstTask = "firstTask"; + String secondTask = "secondTask"; + + asyncCommands.del(listName); + + asyncCommands.lpush(listName, firstTask); + asyncCommands.lpush(listName, secondTask); + RedisFuture redisFuture = asyncCommands.rpop(listName); + + String nextTask = redisFuture.get(); + + Assert.assertEquals(firstTask, nextTask); + + asyncCommands.del(listName); + + asyncCommands.lpush(listName, firstTask); + asyncCommands.lpush(listName, secondTask); + + redisFuture = asyncCommands.lpop(listName); + + nextTask = redisFuture.get(); + + Assert.assertEquals(secondTask, nextTask); + + } + + @Test + public void givenSetElements_thenSaveThemInRedisSetAsync() throws Exception { + + RedisAsyncCommands asyncCommands = redisConnection.async(); + + String countries = "countries"; + + String countryOne = "Spain"; + String countryTwo = "Ireland"; + String countryThree = "Ireland"; + + asyncCommands.sadd(countries, countryOne); + + RedisFuture> countriesSetFuture = asyncCommands.smembers(countries); + Assert.assertEquals(2, countriesSetFuture.get().size()); + + asyncCommands.sadd(countries, countryTwo); + countriesSetFuture = asyncCommands.smembers(countries); + Assert.assertEquals(2, countriesSetFuture.get().size()); + + asyncCommands.sadd(countries, countryThree); + countriesSetFuture = asyncCommands.smembers(countries); + Assert.assertEquals(2, countriesSetFuture.get().size()); + + RedisFuture exists = asyncCommands.sismember(countries, countryThree); + assertTrue(exists.get()); + } + + @Test + public void givenARanking_thenSaveItInRedisSortedSetAsync() throws Exception { + + RedisAsyncCommands asyncCommands = redisConnection.async(); + + String key = "sortedset"; + + asyncCommands.zadd(key, 1, "one"); + asyncCommands.zadd(key, 4, "zero"); + asyncCommands.zadd(key, 2, "two"); + + RedisFuture> values = asyncCommands.zrevrange(key, 0, 3); + Assert.assertEquals("zero", values.get().get(0)); + + values = asyncCommands.zrange(key, 0, 3); + Assert.assertEquals("one", values.get().get(0)); + } + + @Test + public void givenMultipleOperationsThatNeedToBeExecutedAtomically_thenWrapThemInATransaction() throws Exception { + + RedisAsyncCommands asyncCommands = redisConnection.async(); + + // Start a transaction + asyncCommands.multi(); + + // Add three sets to it, and save the future responses + RedisFuture result1 = asyncCommands.set("key1", "value1"); + RedisFuture result2 = asyncCommands.set("key2", "value2"); + RedisFuture result3 = asyncCommands.set("key3", "value3"); + + // Execute it + RedisFuture execResult = asyncCommands.exec(); + + TransactionResult transactionResult = execResult.get(); + + // Get the three results in the transaction return + String firstResult = transactionResult.get(0); + String secondResult = transactionResult.get(0); + String thirdResult = transactionResult.get(0); + + // Our results are in both! + assertTrue(firstResult.equals("OK")); + assertTrue(secondResult.equals("OK")); + assertTrue(thirdResult.equals("OK")); + + assertTrue(result1.get().equals("OK")); + assertTrue(result2.get().equals("OK")); + assertTrue(result3.get().equals("OK")); + } + + @Test + public void givenMultipleIndependentOperations_whenNetworkOptimizationIsImportant_thenFlushManually() throws Exception { + + int iterations = 50; + + RedisAsyncCommands asyncCommands = redisConnection.async(); + + asyncCommands.setAutoFlushCommands(false); + + List> futures = new ArrayList<>(); + for (int i = 0; i < iterations; i++) { + futures.add(asyncCommands.set("key" + i, "value" + i)); + } + + asyncCommands.flushCommands(); + + // Wait until all futures complete + boolean result = LettuceFutures.awaitAll(5, TimeUnit.SECONDS, futures.toArray(new RedisFuture[futures.size()])); + + asyncCommands.setAutoFlushCommands(true); + + } + + @Test + public void givenPubSubChannel_whenMessage_thenMessageReceived() throws Exception { + + Listener listener = new Listener(); + StatefulRedisPubSubConnection connection = redisClient.connectPubSub(); + StatefulRedisPubSubConnection pubconnection = redisClient.connectPubSub(); + connection.addListener(listener); + + RedisPubSubAsyncCommands async = connection.async(); + async.subscribe("channel"); + + RedisPubSubAsyncCommands pubasync = pubconnection.async(); + RedisFuture result = pubasync.publish("channel", "hithere"); + + // Need a long wait for publish to complete, depending on system. + result.get(15, TimeUnit.SECONDS); + assertTrue(listener.getMessage().equals("hithere")); + + } + + private static class Listener implements RedisPubSubListener { + + private String message; + + String getMessage() { + return message; + } + + @Override + public void message(String channel, String message) { + log.debug("Got {} on {}", message, channel); + this.message = message; + } + + @Override + public void message(String pattern, String channel, String message) { + + } + + @Override + public void subscribed(String channel, long count) { + log.debug("Subscribed to {}", channel); + } + + @Override + public void psubscribed(String pattern, long count) { + + } + + @Override + public void unsubscribed(String channel, long count) { + + } + + @Override + public void punsubscribed(String pattern, long count) { + + } + } + +} From ee3c3a6237dfdc488dbca0d0e33fc6749d9530fc Mon Sep 17 00:00:00 2001 From: Alessio Stalla Date: Mon, 15 Jan 2018 01:32:47 +0100 Subject: [PATCH 19/22] BAEL-1402 code for the article: Try-with-resources in Kotlin (#3418) --- core-kotlin/pom.xml | 5 ++ .../kotlin/com/baeldung/kotlin/UseTest.kt | 67 +++++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 core-kotlin/src/test/kotlin/com/baeldung/kotlin/UseTest.kt diff --git a/core-kotlin/pom.xml b/core-kotlin/pom.xml index b511f0dd7b..2cd5275eeb 100644 --- a/core-kotlin/pom.xml +++ b/core-kotlin/pom.xml @@ -44,6 +44,11 @@ kotlin-stdlib ${kotlin-stdlib.version} + + org.jetbrains.kotlin + kotlin-stdlib-jre8 + ${kotlin-stdlib.version} + org.jetbrains.kotlin kotlin-test-junit diff --git a/core-kotlin/src/test/kotlin/com/baeldung/kotlin/UseTest.kt b/core-kotlin/src/test/kotlin/com/baeldung/kotlin/UseTest.kt new file mode 100644 index 0000000000..15bdfcafd8 --- /dev/null +++ b/core-kotlin/src/test/kotlin/com/baeldung/kotlin/UseTest.kt @@ -0,0 +1,67 @@ +package com.baeldung.kotlin + +import org.junit.Test +import java.beans.ExceptionListener +import java.beans.XMLEncoder +import java.io.* +import java.lang.Exception +import kotlin.test.assertEquals +import kotlin.test.assertTrue +import kotlin.test.fail + +class UseTest { + + @Test + fun givenCloseable_whenUseIsCalled_thenItIsClosed() { + val stringWriter = StringWriter() + val writer = BufferedWriter(stringWriter) //Using a BufferedWriter because after close() it throws. + writer.use { + assertEquals(writer, it) + + it.write("something") + } + try { + writer.write("something else") + + fail("write() should have thrown an exception because the writer is closed.") + } catch (e: IOException) { + //Ok + } + + assertEquals("something", stringWriter.toString()) + } + + @Test + fun givenAutoCloseable_whenUseIsCalled_thenItIsClosed() { + val baos = ByteArrayOutputStream() + val encoder = XMLEncoder(PrintStream(baos)) //XMLEncoder is AutoCloseable but not Closeable. + //Here, we use a PrintStream because after close() it throws. + encoder.exceptionListener = ThrowingExceptionListener() + encoder.use { + assertEquals(encoder, it) + + it.writeObject("something") + } + try { + encoder.writeObject("something else") + encoder.flush() + + fail("write() should have thrown an exception because the encoder is closed.") + } catch (e: IOException) { + //Ok + } + } + + @Test + fun whenSimpleFormIsUsed_thenItWorks() { + StringWriter().use { it.write("something") } + } +} + +class ThrowingExceptionListener : ExceptionListener { + override fun exceptionThrown(e: Exception?) { + if(e != null) { + throw e + } + } +} \ No newline at end of file From 7e5940b578b9d516dcb8d4fb714c72c4fb1ebf44 Mon Sep 17 00:00:00 2001 From: Tarang Bhalodia Date: Tue, 16 Jan 2018 01:11:38 +0530 Subject: [PATCH 20/22] tarangbhalodia@gmail.com [BAEL-1282: geospatial support elasticsearch] (#3421) * BAEL-1422: measure performance of Random and ThreadLocalRandom using JMH * BAEL-1422: updated benchmarking examples of Random and ThreadLocalRandom to use newWorkStealingPool that leverages ForkJoinPool * BAEL-1422: refactored benchmarking examples for comparing performance of ThreadLocalRandom and Random - initialised the collection of Callable before running benchmarking - removed for loop for submitting task and instead used executor.invokeAll(collection_of_callable) * BAEL-1282: added TDD type junit tests for geospatial queries elasticsearch --- spring-data-elasticsearch/pom.xml | 18 ++ .../elasticsearch/GeoQueriesTest.java | 179 ++++++++++++++++++ 2 files changed, 197 insertions(+) create mode 100644 spring-data-elasticsearch/src/test/java/com/baeldung/elasticsearch/GeoQueriesTest.java diff --git a/spring-data-elasticsearch/pom.xml b/spring-data-elasticsearch/pom.xml index 520707a432..688506450f 100644 --- a/spring-data-elasticsearch/pom.xml +++ b/spring-data-elasticsearch/pom.xml @@ -45,6 +45,24 @@ ${spring-data-elasticsearch.version} + + com.spatial4j + spatial4j + 0.4.1 + + + + com.vividsolutions + jts + 1.13 + + + xerces + xercesImpl + + + + org.springframework spring-test diff --git a/spring-data-elasticsearch/src/test/java/com/baeldung/elasticsearch/GeoQueriesTest.java b/spring-data-elasticsearch/src/test/java/com/baeldung/elasticsearch/GeoQueriesTest.java new file mode 100644 index 0000000000..19514ce4c2 --- /dev/null +++ b/spring-data-elasticsearch/src/test/java/com/baeldung/elasticsearch/GeoQueriesTest.java @@ -0,0 +1,179 @@ +package com.baeldung.elasticsearch; + +import static org.junit.Assert.assertTrue; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import org.elasticsearch.action.admin.indices.create.CreateIndexRequest; +import org.elasticsearch.action.index.IndexResponse; +import org.elasticsearch.action.search.SearchResponse; +import org.elasticsearch.client.Client; +import org.elasticsearch.common.geo.ShapeRelation; +import org.elasticsearch.common.geo.builders.ShapeBuilder; +import org.elasticsearch.common.unit.DistanceUnit; +import org.elasticsearch.index.query.QueryBuilder; +import org.elasticsearch.index.query.QueryBuilders; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.elasticsearch.core.ElasticsearchTemplate; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import com.baeldung.spring.data.es.config.Config; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = Config.class) +public class GeoQueriesTest { + + public static final String WONDERS_OF_WORLD = "wonders-of-world"; + public static final String WONDERS = "Wonders"; + @Autowired + private ElasticsearchTemplate elasticsearchTemplate; + + @Autowired + private Client client; + + @Before + public void setUp() { + String jsonObject = "{\"Wonders\":{\"properties\":{\"name\":{\"type\":\"string\",\"index\":\"not_analyzed\"},\"region\":{\"type\":\"geo_shape\",\"tree\":\"quadtree\",\"precision\":\"1m\"},\"location\":{\"type\":\"geo_point\"}}}}"; + CreateIndexRequest req = new CreateIndexRequest(WONDERS_OF_WORLD); + req.mapping(WONDERS, jsonObject); + client.admin() + .indices() + .create(req) + .actionGet(); + } + + @Test + public void givenGeoShapeData_whenExecutedGeoShapeQuery_thenResultNonEmpty() { + String jsonObject = "{\"name\":\"Agra\",\"region\":{\"type\":\"envelope\",\"coordinates\":[[75,25],[80.1,30.2]]}}"; + IndexResponse response = client.prepareIndex(WONDERS_OF_WORLD, WONDERS) + .setSource(jsonObject) + .get(); + String tajMahalId = response.getId(); + client.admin() + .indices() + .prepareRefresh(WONDERS_OF_WORLD) + .get(); + + QueryBuilder qb = QueryBuilders.geoShapeQuery("region", ShapeBuilder.newEnvelope() + .topLeft(74.00, 24.0) + .bottomRight(81.1, 31.2)) + .relation(ShapeRelation.WITHIN); + + SearchResponse searchResponse = client.prepareSearch(WONDERS_OF_WORLD) + .setTypes(WONDERS) + .setQuery(qb) + .execute() + .actionGet(); + List ids = Arrays.stream(searchResponse.getHits() + .getHits()) + .map(hit -> { + return hit.getId(); + }) + .collect(Collectors.toList()); + assertTrue(ids.contains(tajMahalId)); + } + + @Test + public void givenGeoPointData_whenExecutedGeoBoundingBoxQuery_thenResultNonEmpty() { + String jsonObject = "{\"name\":\"Pyramids of Giza\",\"location\":[31.131302,29.976480]}"; + IndexResponse response = client.prepareIndex(WONDERS_OF_WORLD, WONDERS) + .setSource(jsonObject) + .get(); + String pyramidsOfGizaId = response.getId(); + client.admin() + .indices() + .prepareRefresh(WONDERS_OF_WORLD) + .get(); + + QueryBuilder qb = QueryBuilders.geoBoundingBoxQuery("location") + .bottomLeft(28, 30) + .topRight(31, 32); + + SearchResponse searchResponse = client.prepareSearch(WONDERS_OF_WORLD) + .setTypes(WONDERS) + .setQuery(qb) + .execute() + .actionGet(); + List ids = Arrays.stream(searchResponse.getHits() + .getHits()) + .map(hit -> { + return hit.getId(); + }) + .collect(Collectors.toList()); + assertTrue(ids.contains(pyramidsOfGizaId)); + } + + @Test + public void givenGeoPointData_whenExecutedGeoDistanceQuery_thenResultNonEmpty() { + String jsonObject = "{\"name\":\"Lighthouse of alexandria\",\"location\":[31.131302,29.976480]}"; + IndexResponse response = client.prepareIndex(WONDERS_OF_WORLD, WONDERS) + .setSource(jsonObject) + .get(); + String lighthouseOfAlexandriaId = response.getId(); + client.admin() + .indices() + .prepareRefresh(WONDERS_OF_WORLD) + .get(); + + QueryBuilder qb = QueryBuilders.geoDistanceQuery("location") + .point(29.976, 31.131) + .distance(10, DistanceUnit.MILES); + + SearchResponse searchResponse = client.prepareSearch(WONDERS_OF_WORLD) + .setTypes(WONDERS) + .setQuery(qb) + .execute() + .actionGet(); + List ids = Arrays.stream(searchResponse.getHits() + .getHits()) + .map(hit -> { + return hit.getId(); + }) + .collect(Collectors.toList()); + assertTrue(ids.contains(lighthouseOfAlexandriaId)); + } + + @Test + public void givenGeoPointData_whenExecutedGeoPolygonQuery_thenResultNonEmpty() { + String jsonObject = "{\"name\":\"The Great Rann of Kutch\",\"location\":[69.859741,23.733732]}"; + IndexResponse response = client.prepareIndex(WONDERS_OF_WORLD, WONDERS) + .setSource(jsonObject) + .get(); + String greatRannOfKutchid = response.getId(); + client.admin() + .indices() + .prepareRefresh(WONDERS_OF_WORLD) + .get(); + + QueryBuilder qb = QueryBuilders.geoPolygonQuery("location") + .addPoint(22.733, 68.859) + .addPoint(24.733, 68.859) + .addPoint(23, 70.859); + + SearchResponse searchResponse = client.prepareSearch(WONDERS_OF_WORLD) + .setTypes(WONDERS) + .setQuery(qb) + .execute() + .actionGet(); + List ids = Arrays.stream(searchResponse.getHits() + .getHits()) + .map(hit -> { + return hit.getId(); + }) + .collect(Collectors.toList()); + assertTrue(ids.contains(greatRannOfKutchid)); + } + + @After + public void destroy() { + elasticsearchTemplate.deleteIndex(WONDERS_OF_WORLD); + } + +} From 293968321eb980e6599dd4f81b002b85e64308ad Mon Sep 17 00:00:00 2001 From: Antonio David Perez Morales Date: Mon, 15 Jan 2018 21:00:21 +0100 Subject: [PATCH 21/22] BAEL-1428: Adding example for manually set authenticated user (#3423) --- .../web/controller/LoginController.java | 43 ++++++++++++++ .../web/controller/PrintUserController.java | 27 +++++++++ .../security/spring/ManualSecurityConfig.java | 58 +++++++++++++++++++ .../spring/ManualSecurityIntegrationTest.java | 58 +++++++++++++++++++ 4 files changed, 186 insertions(+) create mode 100644 spring-security-mvc-custom/src/main/java/org/baeldung/web/controller/LoginController.java create mode 100644 spring-security-mvc-custom/src/main/java/org/baeldung/web/controller/PrintUserController.java create mode 100644 spring-security-mvc-custom/src/test/java/org/baeldung/security/spring/ManualSecurityConfig.java create mode 100644 spring-security-mvc-custom/src/test/java/org/baeldung/security/spring/ManualSecurityIntegrationTest.java diff --git a/spring-security-mvc-custom/src/main/java/org/baeldung/web/controller/LoginController.java b/spring-security-mvc-custom/src/main/java/org/baeldung/web/controller/LoginController.java new file mode 100644 index 0000000000..c67a6f667e --- /dev/null +++ b/spring-security-mvc-custom/src/main/java/org/baeldung/web/controller/LoginController.java @@ -0,0 +1,43 @@ +package org.baeldung.web.controller; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpSession; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; + +@Controller +@RequestMapping(value = "/custom") +public class LoginController { + + @Autowired + private AuthenticationManager authManager; + + public LoginController() { + super(); + } + + // API + + // custom login + + @RequestMapping(value = "/login", method = RequestMethod.POST) + public void login(@RequestParam("username") final String username, @RequestParam("password") final String password, final HttpServletRequest request) { + UsernamePasswordAuthenticationToken authReq = + new UsernamePasswordAuthenticationToken(username, password); + Authentication auth = authManager.authenticate(authReq); + SecurityContext sc = SecurityContextHolder.getContext(); + sc.setAuthentication(auth); + HttpSession session = request.getSession(true); + session.setAttribute("SPRING_SECURITY_CONTEXT", sc); + } + +} \ No newline at end of file diff --git a/spring-security-mvc-custom/src/main/java/org/baeldung/web/controller/PrintUserController.java b/spring-security-mvc-custom/src/main/java/org/baeldung/web/controller/PrintUserController.java new file mode 100644 index 0000000000..78f164c7f1 --- /dev/null +++ b/spring-security-mvc-custom/src/main/java/org/baeldung/web/controller/PrintUserController.java @@ -0,0 +1,27 @@ +package org.baeldung.web.controller; + +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; + +@Controller +@RequestMapping(value = "/custom") +public class PrintUserController { + + public PrintUserController() { + super(); + } + + // API + + // print user + + @RequestMapping(value = "/print", method = RequestMethod.GET) + public void printUser() { + SecurityContext sc = SecurityContextHolder.getContext(); + System.out.println("Logged User: "+sc.getAuthentication().getName()); + } + +} \ No newline at end of file diff --git a/spring-security-mvc-custom/src/test/java/org/baeldung/security/spring/ManualSecurityConfig.java b/spring-security-mvc-custom/src/test/java/org/baeldung/security/spring/ManualSecurityConfig.java new file mode 100644 index 0000000000..874856095c --- /dev/null +++ b/spring-security-mvc-custom/src/test/java/org/baeldung/security/spring/ManualSecurityConfig.java @@ -0,0 +1,58 @@ +package org.baeldung.security.spring; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.config.BeanIds; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.builders.WebSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; + +@Configuration +@EnableWebSecurity +@EnableGlobalMethodSecurity(prePostEnabled = true) +public class ManualSecurityConfig extends WebSecurityConfigurerAdapter { + + public ManualSecurityConfig() { + super(); + } + + // java config + + @Override + protected void configure(final AuthenticationManagerBuilder auth) throws Exception { + auth.inMemoryAuthentication().withUser("user1").password("user1Pass").authorities("ROLE_USER").and().withUser("admin").password("adminPass").authorities("ROLE_ADMIN"); + } + + @Override + public void configure(final WebSecurity web) throws Exception { + web.ignoring().antMatchers("/resources/**"); + } + + @Bean(name = BeanIds.AUTHENTICATION_MANAGER) + @Override + public AuthenticationManager authenticationManagerBean() throws Exception { + return super.authenticationManagerBean(); + } + + @Override + protected void configure(final HttpSecurity http) throws Exception { + // @formatter:off + http + .authorizeRequests() + .mvcMatchers("/custom/login").permitAll() + .anyRequest().authenticated() + .and() + .httpBasic() + .and() + .headers().cacheControl().disable() + .and() + .csrf().disable() + ; + // @formatter:on + } + +} diff --git a/spring-security-mvc-custom/src/test/java/org/baeldung/security/spring/ManualSecurityIntegrationTest.java b/spring-security-mvc-custom/src/test/java/org/baeldung/security/spring/ManualSecurityIntegrationTest.java new file mode 100644 index 0000000000..afc86bd74c --- /dev/null +++ b/spring-security-mvc-custom/src/test/java/org/baeldung/security/spring/ManualSecurityIntegrationTest.java @@ -0,0 +1,58 @@ +package org.baeldung.security.spring; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import javax.servlet.http.HttpSession; + +import org.baeldung.spring.MvcConfig; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.MockitoAnnotations; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.mock.web.MockHttpSession; +import org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; + +@RunWith(SpringJUnit4ClassRunner.class) +@WebAppConfiguration +@ContextConfiguration(classes = { MvcConfig.class, ManualSecurityConfig.class }) +public class ManualSecurityIntegrationTest { + + @Autowired + WebApplicationContext wac; + + private MockMvc mockMvc; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + mockMvc = MockMvcBuilders.webAppContextSetup(wac).apply(SecurityMockMvcConfigurers.springSecurity()).build(); + } + + /** + * Execute custom login and access the endpoint + */ + @Test + public void whenLoginIsSuccessFulThenEndpointCanBeAccessedAndCurrentUserPrinted() throws Exception { + + mockMvc.perform(get("/custom/print")) + .andExpect(status().isUnauthorized()); + + HttpSession session = mockMvc.perform(post("/custom/login").param("username", "user1").param("password", "user1Pass")) + .andExpect(status().isOk()) + .andReturn() + .getRequest() + .getSession(); + + mockMvc.perform(get("/custom/print").session((MockHttpSession) session)) + .andExpect(status().is2xxSuccessful()); + } + +} From f993bc0435953a345abd9047a64bd8a047f0c23b Mon Sep 17 00:00:00 2001 From: Bogdan Stoean <4540392+BogdanStoean@users.noreply.github.com> Date: Mon, 15 Jan 2018 23:05:19 +0200 Subject: [PATCH 22/22] [BAEL-1410] Spring Boot OAuth2 Support (#3409) * initial setup with spring boot/ spring data jpa/ flyway * BAEL-1315 - added flyway test extensions for spring * BAEL-1315 - added flyway test extensions for spring * BAEL-1315 - created multiple migration scripts and locations * BAEL-1315 - test insert after schema creation * cleanup * BAEL-1315 - test data changes by a migration * [BAEL-1410] Spring Boot Security Auto-Configuration * [BAEL-1410] Added some tests for incorrect credentials use case * [BAEL-1410] Added readme and some code improvements * [BAEL-1410] removed form based auth config because is redundant added oauth2 server auto-configuration sample with test * [BAEL-1410] added custom Authorization Server Config * [BAEL-1410] update README * [BAEL-1410]refactor tests * [BAEL-1410]oauth2 resource server * [BAEL-1410]oauth2 sso sample with facebook * [BAEL-1410]remove spring-flyway --- spring-boot-security/README.md | 8 +- spring-boot-security/pom.xml | 4 + .../SpringBootSecurityApplication.java | 4 +- .../config/BasicAuthConfiguration.java} | 6 +- .../config/FormLoginConfiguration.java | 39 ------- .../SpringBootOAuth2ResourceApplication.java | 30 +++++ ...ingBootAuthorizationServerApplication.java | 30 +++++ .../config/AuthorizationServerConfig.java | 39 +++++++ .../SpringBootOAuth2SsoApplication.java | 18 +++ .../resources/application-authz.properties | 3 + .../resources/application-resource.properties | 2 + .../main/resources/application-sso.properties | 9 ++ .../src/main/resources/application.properties | 6 +- .../FormConfigurationIntegrationTest.java | 106 ------------------ ...asicAuthConfigurationIntegrationTest.java} | 9 +- ...figAuthorizationServerIntegrationTest.java | 75 +++++++++++++ ...figAuthorizationServerIntegrationTest.java | 44 ++++++++ 17 files changed, 270 insertions(+), 162 deletions(-) rename spring-boot-security/src/main/java/com/baeldung/springbootsecurity/{ => basic_auth}/SpringBootSecurityApplication.java (80%) rename spring-boot-security/src/main/java/com/baeldung/springbootsecurity/{config/BasicConfiguration.java => basic_auth/config/BasicAuthConfiguration.java} (84%) delete mode 100644 spring-boot-security/src/main/java/com/baeldung/springbootsecurity/config/FormLoginConfiguration.java create mode 100644 spring-boot-security/src/main/java/com/baeldung/springbootsecurity/oauth2resource/SpringBootOAuth2ResourceApplication.java create mode 100644 spring-boot-security/src/main/java/com/baeldung/springbootsecurity/oauth2server/SpringBootAuthorizationServerApplication.java create mode 100644 spring-boot-security/src/main/java/com/baeldung/springbootsecurity/oauth2server/config/AuthorizationServerConfig.java create mode 100644 spring-boot-security/src/main/java/com/baeldung/springbootsecurity/oauth2sso/SpringBootOAuth2SsoApplication.java create mode 100644 spring-boot-security/src/main/resources/application-authz.properties create mode 100644 spring-boot-security/src/main/resources/application-resource.properties create mode 100644 spring-boot-security/src/main/resources/application-sso.properties delete mode 100644 spring-boot-security/src/test/java/com/baeldung/springbootsecurity/FormConfigurationIntegrationTest.java rename spring-boot-security/src/test/java/com/baeldung/springbootsecurity/{BasicConfigurationIntegrationTest.java => basic_auth/BasicAuthConfigurationIntegrationTest.java} (86%) create mode 100644 spring-boot-security/src/test/java/com/baeldung/springbootsecurity/oauth2server/CustomConfigAuthorizationServerIntegrationTest.java create mode 100644 spring-boot-security/src/test/java/com/baeldung/springbootsecurity/oauth2server/DefaultConfigAuthorizationServerIntegrationTest.java diff --git a/spring-boot-security/README.md b/spring-boot-security/README.md index 26ab8b2337..a0ddb8de7b 100644 --- a/spring-boot-security/README.md +++ b/spring-boot-security/README.md @@ -1,6 +1,8 @@ ### Spring Boot Security Auto-Configuration - mvn clean install -- uncomment in application.properties spring.profiles.active=basic # for basic auth config -- uncomment in application.properties spring.profiles.active=form # for form based auth config -- uncomment actuator dependency simultaneously with the line from main class +- uncomment actuator dependency simultaneously with the line from basic auth main class +- uncomment security properties for easy testing. If not random will be generated. + +### CURL commands +- curl -X POST -u baeldung-admin:baeldung -d grant_type=client_credentials -d username=baeldung-admin -d password=baeldung http://localhost:8080/oauth/token diff --git a/spring-boot-security/pom.xml b/spring-boot-security/pom.xml index c35191a7fc..c1ec14ff64 100644 --- a/spring-boot-security/pom.xml +++ b/spring-boot-security/pom.xml @@ -43,6 +43,10 @@ org.springframework.boot spring-boot-starter-security + + org.springframework.security.oauth + spring-security-oauth2 + org.springframework.boot spring-boot-starter-web diff --git a/spring-boot-security/src/main/java/com/baeldung/springbootsecurity/SpringBootSecurityApplication.java b/spring-boot-security/src/main/java/com/baeldung/springbootsecurity/basic_auth/SpringBootSecurityApplication.java similarity index 80% rename from spring-boot-security/src/main/java/com/baeldung/springbootsecurity/SpringBootSecurityApplication.java rename to spring-boot-security/src/main/java/com/baeldung/springbootsecurity/basic_auth/SpringBootSecurityApplication.java index 3a85da72e5..2ecad4ae35 100644 --- a/spring-boot-security/src/main/java/com/baeldung/springbootsecurity/SpringBootSecurityApplication.java +++ b/spring-boot-security/src/main/java/com/baeldung/springbootsecurity/basic_auth/SpringBootSecurityApplication.java @@ -1,4 +1,4 @@ -package com.baeldung.springbootsecurity; +package com.baeldung.springbootsecurity.basic_auth; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @@ -7,7 +7,7 @@ import org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration @SpringBootApplication(exclude = { SecurityAutoConfiguration.class // ,ManagementWebSecurityAutoConfiguration.class -}) +}, scanBasePackages = "com.baeldung.springbootsecurity.basic_auth") public class SpringBootSecurityApplication { public static void main(String[] args) { diff --git a/spring-boot-security/src/main/java/com/baeldung/springbootsecurity/config/BasicConfiguration.java b/spring-boot-security/src/main/java/com/baeldung/springbootsecurity/basic_auth/config/BasicAuthConfiguration.java similarity index 84% rename from spring-boot-security/src/main/java/com/baeldung/springbootsecurity/config/BasicConfiguration.java rename to spring-boot-security/src/main/java/com/baeldung/springbootsecurity/basic_auth/config/BasicAuthConfiguration.java index 1b08e5ee22..993c573fb0 100644 --- a/spring-boot-security/src/main/java/com/baeldung/springbootsecurity/config/BasicConfiguration.java +++ b/spring-boot-security/src/main/java/com/baeldung/springbootsecurity/basic_auth/config/BasicAuthConfiguration.java @@ -1,7 +1,6 @@ -package com.baeldung.springbootsecurity.config; +package com.baeldung.springbootsecurity.basic_auth.config; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Profile; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; @@ -9,8 +8,7 @@ import org.springframework.security.config.annotation.web.configuration.WebSecur @Configuration @EnableWebSecurity -@Profile("basic") -public class BasicConfiguration extends WebSecurityConfigurerAdapter { +public class BasicAuthConfiguration extends WebSecurityConfigurerAdapter { @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { diff --git a/spring-boot-security/src/main/java/com/baeldung/springbootsecurity/config/FormLoginConfiguration.java b/spring-boot-security/src/main/java/com/baeldung/springbootsecurity/config/FormLoginConfiguration.java deleted file mode 100644 index b4902a9ffc..0000000000 --- a/spring-boot-security/src/main/java/com/baeldung/springbootsecurity/config/FormLoginConfiguration.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.baeldung.springbootsecurity.config; - -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Profile; -import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; - -@Configuration -@EnableWebSecurity -@Profile("form") -public class FormLoginConfiguration extends WebSecurityConfigurerAdapter { - - @Override - protected void configure(AuthenticationManagerBuilder auth) throws Exception { - auth - .inMemoryAuthentication() - .withUser("user") - .password("password") - .roles("USER") - .and() - .withUser("admin") - .password("password") - .roles("USER", "ADMIN"); - } - - @Override - protected void configure(HttpSecurity http) throws Exception { - http - .authorizeRequests() - .anyRequest() - .authenticated() - .and() - .formLogin() - .and() - .httpBasic(); - } -} diff --git a/spring-boot-security/src/main/java/com/baeldung/springbootsecurity/oauth2resource/SpringBootOAuth2ResourceApplication.java b/spring-boot-security/src/main/java/com/baeldung/springbootsecurity/oauth2resource/SpringBootOAuth2ResourceApplication.java new file mode 100644 index 0000000000..56231a28bd --- /dev/null +++ b/spring-boot-security/src/main/java/com/baeldung/springbootsecurity/oauth2resource/SpringBootOAuth2ResourceApplication.java @@ -0,0 +1,30 @@ +package com.baeldung.springbootsecurity.oauth2resource; + +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@EnableResourceServer +@SpringBootApplication(scanBasePackages = "com.baeldung.springbootsecurity.oauth2resource") +public class SpringBootOAuth2ResourceApplication { + + public static void main(String[] args) { + new SpringApplicationBuilder() + .profiles("resource") + .sources(SpringBootOAuth2ResourceApplication.class) + .build() + .run(args); + } + + @RestController + class SecuredResourceController { + + @GetMapping("/securedResource") + public String securedResource() { + return "Baeldung Secured Resource OK"; + } + + } +} diff --git a/spring-boot-security/src/main/java/com/baeldung/springbootsecurity/oauth2server/SpringBootAuthorizationServerApplication.java b/spring-boot-security/src/main/java/com/baeldung/springbootsecurity/oauth2server/SpringBootAuthorizationServerApplication.java new file mode 100644 index 0000000000..44dabefbb8 --- /dev/null +++ b/spring-boot-security/src/main/java/com/baeldung/springbootsecurity/oauth2server/SpringBootAuthorizationServerApplication.java @@ -0,0 +1,30 @@ +package com.baeldung.springbootsecurity.oauth2server; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; +import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.security.Principal; + +@EnableResourceServer +@EnableAuthorizationServer +@SpringBootApplication(scanBasePackages = "com.baeldung.springbootsecurity.oauth2server") +public class SpringBootAuthorizationServerApplication { + + public static void main(String[] args) { + SpringApplication.run(SpringBootAuthorizationServerApplication.class, args); + } + + @RestController + class UserController { + + @GetMapping("/user") + public Principal user(Principal user) { + return user; + } + + } +} diff --git a/spring-boot-security/src/main/java/com/baeldung/springbootsecurity/oauth2server/config/AuthorizationServerConfig.java b/spring-boot-security/src/main/java/com/baeldung/springbootsecurity/oauth2server/config/AuthorizationServerConfig.java new file mode 100644 index 0000000000..b403feb5c1 --- /dev/null +++ b/spring-boot-security/src/main/java/com/baeldung/springbootsecurity/oauth2server/config/AuthorizationServerConfig.java @@ -0,0 +1,39 @@ +package com.baeldung.springbootsecurity.oauth2server.config; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; +import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; +import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer; + +@Configuration +@Profile("authz") +public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { + + @Autowired + private AuthenticationManager authenticationManager; + + @Override + public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { + endpoints.authenticationManager(authenticationManager); + } + + @Override + public void configure(ClientDetailsServiceConfigurer clients) throws Exception { + clients + .inMemory() + .withClient("baeldung") + .secret("baeldung") + .authorizedGrantTypes("client_credentials", "password", "authorization_code") + .scopes("openid", "read") + .autoApprove(true) + .and() + .withClient("baeldung-admin") + .secret("baeldung") + .authorizedGrantTypes("authorization_code", "client_credentials", "refresh_token") + .scopes("read", "write") + .autoApprove(true); + } +} diff --git a/spring-boot-security/src/main/java/com/baeldung/springbootsecurity/oauth2sso/SpringBootOAuth2SsoApplication.java b/spring-boot-security/src/main/java/com/baeldung/springbootsecurity/oauth2sso/SpringBootOAuth2SsoApplication.java new file mode 100644 index 0000000000..b1cd580f08 --- /dev/null +++ b/spring-boot-security/src/main/java/com/baeldung/springbootsecurity/oauth2sso/SpringBootOAuth2SsoApplication.java @@ -0,0 +1,18 @@ +package com.baeldung.springbootsecurity.oauth2sso; + +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso; +import org.springframework.boot.builder.SpringApplicationBuilder; + +@EnableOAuth2Sso +@SpringBootApplication(scanBasePackages = "com.baeldung.springbootsecurity.oauth2sso") +public class SpringBootOAuth2SsoApplication { + + public static void main(String[] args) { + new SpringApplicationBuilder() + .profiles("sso") + .sources(SpringBootOAuth2SsoApplication.class) + .build() + .run(args); + } +} diff --git a/spring-boot-security/src/main/resources/application-authz.properties b/spring-boot-security/src/main/resources/application-authz.properties new file mode 100644 index 0000000000..d29b0cdd3c --- /dev/null +++ b/spring-boot-security/src/main/resources/application-authz.properties @@ -0,0 +1,3 @@ +security.user.password=password +security.oauth2.client.client-id=client +security.oauth2.client.client-secret=secret diff --git a/spring-boot-security/src/main/resources/application-resource.properties b/spring-boot-security/src/main/resources/application-resource.properties new file mode 100644 index 0000000000..b157b01d51 --- /dev/null +++ b/spring-boot-security/src/main/resources/application-resource.properties @@ -0,0 +1,2 @@ +server.port=8081 +security.oauth2.resource.userInfoUri=http://localhost:8080/user \ No newline at end of file diff --git a/spring-boot-security/src/main/resources/application-sso.properties b/spring-boot-security/src/main/resources/application-sso.properties new file mode 100644 index 0000000000..ac6ae0cc93 --- /dev/null +++ b/spring-boot-security/src/main/resources/application-sso.properties @@ -0,0 +1,9 @@ +server.port=8082 +security.oauth2.client.clientId= +security.oauth2.client.clientSecret= +security.oauth2.client.accessTokenUri=https://graph.facebook.com/oauth/access_token +security.oauth2.client.userAuthorizationUri=https://www.facebook.com/dialog/oauth +security.oauth2.client.tokenName=oauth_token +security.oauth2.client.authenticationScheme=query +security.oauth2.client.clientAuthenticationScheme=form +security.oauth2.resource.userInfoUri=https://graph.facebook.com/me \ No newline at end of file diff --git a/spring-boot-security/src/main/resources/application.properties b/spring-boot-security/src/main/resources/application.properties index 6ca2edb175..c2b8d70dc6 100644 --- a/spring-boot-security/src/main/resources/application.properties +++ b/spring-boot-security/src/main/resources/application.properties @@ -1,4 +1,4 @@ #spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration -#spring.profiles.active=form -#spring.profiles.active=basic -#security.user.password=password \ No newline at end of file +#security.user.password=password +#security.oauth2.client.client-id=client +#security.oauth2.client.client-secret=secret diff --git a/spring-boot-security/src/test/java/com/baeldung/springbootsecurity/FormConfigurationIntegrationTest.java b/spring-boot-security/src/test/java/com/baeldung/springbootsecurity/FormConfigurationIntegrationTest.java deleted file mode 100644 index 697a4f2868..0000000000 --- a/spring-boot-security/src/test/java/com/baeldung/springbootsecurity/FormConfigurationIntegrationTest.java +++ /dev/null @@ -1,106 +0,0 @@ -package com.baeldung.springbootsecurity; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.context.embedded.LocalServerPort; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.http.*; -import org.springframework.test.context.ActiveProfiles; -import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; - -import java.util.Collections; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import static org.junit.Assert.*; -import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; - -@RunWith(SpringRunner.class) -@SpringBootTest(webEnvironment = RANDOM_PORT) -@ActiveProfiles("form") -public class FormConfigurationIntegrationTest { - - @Autowired TestRestTemplate restTemplate; - @LocalServerPort int port; - - @Test - public void whenLoginPageIsRequested_ThenSuccess() { - HttpHeaders httpHeaders = new HttpHeaders(); - httpHeaders.setAccept(Collections.singletonList(MediaType.TEXT_HTML)); - ResponseEntity responseEntity = restTemplate.exchange("/login", HttpMethod.GET, new HttpEntity(httpHeaders), String.class); - assertEquals(HttpStatus.OK, responseEntity.getStatusCode()); - assertTrue(responseEntity - .getBody() - .contains("_csrf")); - } - - @Test - public void whenTryingToLoginWithCorrectCredentials_ThenAuthenticateWithSuccess() { - HttpHeaders httpHeaders = getHeaders(); - httpHeaders.setAccept(Collections.singletonList(MediaType.TEXT_HTML)); - httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED); - MultiValueMap form = getFormSubmitCorrectCredentials(); - ResponseEntity responseEntity = this.restTemplate.exchange("/login", HttpMethod.POST, new HttpEntity<>(form, httpHeaders), String.class); - assertEquals(responseEntity.getStatusCode(), HttpStatus.FOUND); - assertTrue(responseEntity - .getHeaders() - .getLocation() - .toString() - .endsWith(this.port + "/")); - assertNotNull(responseEntity - .getHeaders() - .get("Set-Cookie")); - } - - @Test - public void whenTryingToLoginWithInorrectCredentials_ThenAuthenticationFailed() { - HttpHeaders httpHeaders = getHeaders(); - httpHeaders.setAccept(Collections.singletonList(MediaType.TEXT_HTML)); - httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED); - MultiValueMap form = getFormSubmitIncorrectCredentials(); - ResponseEntity responseEntity = this.restTemplate.exchange("/login", HttpMethod.POST, new HttpEntity<>(form, httpHeaders), String.class); - assertEquals(responseEntity.getStatusCode(), HttpStatus.FOUND); - assertTrue(responseEntity - .getHeaders() - .getLocation() - .toString() - .endsWith(this.port + "/login?error")); - assertNull(responseEntity - .getHeaders() - .get("Set-Cookie")); - } - - private MultiValueMap getFormSubmitCorrectCredentials() { - MultiValueMap form = new LinkedMultiValueMap<>(); - form.set("username", "user"); - form.set("password", "password"); - return form; - } - - private MultiValueMap getFormSubmitIncorrectCredentials() { - MultiValueMap form = new LinkedMultiValueMap<>(); - form.set("username", "user"); - form.set("password", "wrongpassword"); - return form; - } - - private HttpHeaders getHeaders() { - HttpHeaders headers = new HttpHeaders(); - ResponseEntity page = this.restTemplate.getForEntity("/login", String.class); - assertEquals(page.getStatusCode(), HttpStatus.OK); - String cookie = page - .getHeaders() - .getFirst("Set-Cookie"); - headers.set("Cookie", cookie); - Pattern pattern = Pattern.compile("(?s).*name=\"_csrf\".*?value=\"([^\"]+).*"); - Matcher matcher = pattern.matcher(page.getBody()); - assertTrue(matcher.matches()); - headers.set("X-CSRF-TOKEN", matcher.group(1)); - return headers; - } - -} diff --git a/spring-boot-security/src/test/java/com/baeldung/springbootsecurity/BasicConfigurationIntegrationTest.java b/spring-boot-security/src/test/java/com/baeldung/springbootsecurity/basic_auth/BasicAuthConfigurationIntegrationTest.java similarity index 86% rename from spring-boot-security/src/test/java/com/baeldung/springbootsecurity/BasicConfigurationIntegrationTest.java rename to spring-boot-security/src/test/java/com/baeldung/springbootsecurity/basic_auth/BasicAuthConfigurationIntegrationTest.java index 63e1c2ac73..4e4244abb7 100644 --- a/spring-boot-security/src/test/java/com/baeldung/springbootsecurity/BasicConfigurationIntegrationTest.java +++ b/spring-boot-security/src/test/java/com/baeldung/springbootsecurity/basic_auth/BasicAuthConfigurationIntegrationTest.java @@ -1,5 +1,6 @@ -package com.baeldung.springbootsecurity; +package com.baeldung.springbootsecurity.basic_auth; +import com.baeldung.springbootsecurity.basic_auth.SpringBootSecurityApplication; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -8,7 +9,6 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.junit4.SpringRunner; import java.io.IOException; @@ -20,9 +20,8 @@ import static org.junit.Assert.assertTrue; import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; @RunWith(SpringRunner.class) -@SpringBootTest(webEnvironment = RANDOM_PORT) -@ActiveProfiles("basic") -public class BasicConfigurationIntegrationTest { +@SpringBootTest(webEnvironment = RANDOM_PORT, classes = SpringBootSecurityApplication.class) +public class BasicAuthConfigurationIntegrationTest { TestRestTemplate restTemplate; URL base; diff --git a/spring-boot-security/src/test/java/com/baeldung/springbootsecurity/oauth2server/CustomConfigAuthorizationServerIntegrationTest.java b/spring-boot-security/src/test/java/com/baeldung/springbootsecurity/oauth2server/CustomConfigAuthorizationServerIntegrationTest.java new file mode 100644 index 0000000000..09df9ce645 --- /dev/null +++ b/spring-boot-security/src/test/java/com/baeldung/springbootsecurity/oauth2server/CustomConfigAuthorizationServerIntegrationTest.java @@ -0,0 +1,75 @@ +package com.baeldung.springbootsecurity.oauth2server; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.security.oauth2.client.DefaultOAuth2ClientContext; +import org.springframework.security.oauth2.client.OAuth2RestTemplate; +import org.springframework.security.oauth2.client.resource.OAuth2AccessDeniedException; +import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsResourceDetails; +import org.springframework.security.oauth2.common.OAuth2AccessToken; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.junit4.SpringRunner; + +import static java.lang.String.format; +import static java.util.Collections.singletonList; +import static org.junit.Assert.assertNotNull; +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; + +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = RANDOM_PORT, classes = SpringBootAuthorizationServerApplication.class) +@ActiveProfiles("authz") +public class CustomConfigAuthorizationServerIntegrationTest { + + @Value("${local.server.port}") protected int port; + + @Test + public void whenAccessTokenIsRequested_ThenAccessTokenValueIsNotNull() { + ClientCredentialsResourceDetails resourceDetails = getClientCredentialsResourceDetails(); + resourceDetails.setClientId("baeldung"); + resourceDetails.setClientSecret("baeldung"); + resourceDetails.setScope(singletonList("read")); + DefaultOAuth2ClientContext clientContext = new DefaultOAuth2ClientContext(); + OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(resourceDetails, clientContext); + restTemplate.setMessageConverters(singletonList(new MappingJackson2HttpMessageConverter())); + OAuth2AccessToken accessToken = restTemplate.getAccessToken(); + assertNotNull(accessToken); + + } + + @Test(expected = OAuth2AccessDeniedException.class) + public void whenAccessTokenIsRequestedWithInvalidException_ThenExceptionIsThrown() { + ClientCredentialsResourceDetails resourceDetails = getClientCredentialsResourceDetails(); + resourceDetails.setClientId("baeldung"); + resourceDetails.setClientSecret("baeldung"); + resourceDetails.setScope(singletonList("write")); + DefaultOAuth2ClientContext clientContext = new DefaultOAuth2ClientContext(); + OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(resourceDetails, clientContext); + restTemplate.setMessageConverters(singletonList(new MappingJackson2HttpMessageConverter())); + restTemplate.getAccessToken(); + } + + @Test + public void whenAccessTokenIsRequestedByClientWithWriteScope_ThenAccessTokenIsNotNull() { + ClientCredentialsResourceDetails resourceDetails = getClientCredentialsResourceDetails(); + resourceDetails.setClientId("baeldung-admin"); + resourceDetails.setClientSecret("baeldung"); + resourceDetails.setScope(singletonList("write")); + DefaultOAuth2ClientContext clientContext = new DefaultOAuth2ClientContext(); + OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(resourceDetails, clientContext); + restTemplate.setMessageConverters(singletonList(new MappingJackson2HttpMessageConverter())); + OAuth2AccessToken accessToken = restTemplate.getAccessToken(); + assertNotNull(accessToken); + } + + private ClientCredentialsResourceDetails getClientCredentialsResourceDetails() { + ClientCredentialsResourceDetails resourceDetails = new ClientCredentialsResourceDetails(); + resourceDetails.setAccessTokenUri(format("http://localhost:%d/oauth/token", port)); + resourceDetails.setGrantType("client_credentials"); + return resourceDetails; + } + +} + diff --git a/spring-boot-security/src/test/java/com/baeldung/springbootsecurity/oauth2server/DefaultConfigAuthorizationServerIntegrationTest.java b/spring-boot-security/src/test/java/com/baeldung/springbootsecurity/oauth2server/DefaultConfigAuthorizationServerIntegrationTest.java new file mode 100644 index 0000000000..c7b1b4ef6c --- /dev/null +++ b/spring-boot-security/src/test/java/com/baeldung/springbootsecurity/oauth2server/DefaultConfigAuthorizationServerIntegrationTest.java @@ -0,0 +1,44 @@ +package com.baeldung.springbootsecurity.oauth2server; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.security.oauth2.client.DefaultOAuth2ClientContext; +import org.springframework.security.oauth2.client.OAuth2RestTemplate; +import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsResourceDetails; +import org.springframework.security.oauth2.common.OAuth2AccessToken; +import org.springframework.test.context.junit4.SpringRunner; + +import static java.lang.String.format; +import static java.util.Arrays.asList; +import static java.util.Collections.singletonList; +import static org.junit.Assert.assertNotNull; +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; + +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = RANDOM_PORT, classes = SpringBootAuthorizationServerApplication.class, + properties = { "security.oauth2.client.client-id=client", "security.oauth2.client.client-secret=secret" }) +public class DefaultConfigAuthorizationServerIntegrationTest { + + @Value("${local.server.port}") protected int port; + + @Test + public void whenAccessTokenIsRequested_ThenAccessTokenValueIsNotNull() { + ClientCredentialsResourceDetails resourceDetails = new ClientCredentialsResourceDetails(); + resourceDetails.setAccessTokenUri(format("http://localhost:%d/oauth/token", port)); + resourceDetails.setClientId("client"); + resourceDetails.setClientSecret("secret"); + resourceDetails.setGrantType("client_credentials"); + resourceDetails.setScope(asList("read", "write")); + DefaultOAuth2ClientContext clientContext = new DefaultOAuth2ClientContext(); + OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(resourceDetails, clientContext); + restTemplate.setMessageConverters(singletonList(new MappingJackson2HttpMessageConverter())); + OAuth2AccessToken accessToken = restTemplate.getAccessToken(); + assertNotNull(accessToken); + + } + +} +