Skip to content

Commit

Permalink
Apply #18 - Spring Security
Browse files Browse the repository at this point in the history
시큐리티 기능 적용
디펜던시에는 타임리프를 사용하므로
`thymeleaf-extras-springsecurity5` 추가 잊으면 안 됨

시큐리티 설정은 DB를 사용하는 방식으로,
테스트 계정의 패스워드는 `noop` 을 써서 암호화하지 않음
추후 암호화 가능
로그인 인증 예외 범위는 루트 및 일반 조회 페이지로 지정

기존 테스트는 시큐리티와 별개로 동작시키고 싶으므로
시큐리티 자동 설정과 필터를 제외하는 규칙을 추가
스프링 시큐리티의 인증 테스트는
필요에 따라 별도로 진행할 계획

**PS
Spring Security 5.7.0-M2 부터 
` WebSecurityConfigurerAdapter ` 지원을 하지 않는다.
` SecurityFilterChain` 을 직접 Bean으로 등혹하는 방식을 권장.
  • Loading branch information
bmcho committed Aug 23, 2022
1 parent db098b8 commit 681f7b3
Show file tree
Hide file tree
Showing 11 changed files with 145 additions and 10 deletions.
3 changes: 3 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,15 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5'
runtimeOnly 'com.h2database:h2'
runtimeOnly 'mysql:mysql-connector-java'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.security:spring-security-test'

// queryDSL 설정
implementation "com.querydsl:querydsl-jpa"
Expand Down
47 changes: 47 additions & 0 deletions src/main/java/com/bm/getin/config/SecurityCustomConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.bm.getin.config;

import com.bm.getin.service.AdminService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;

public class SecurityCustomConfig {

@Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}

@Autowired
public void configureGlobal(
AuthenticationManagerBuilder auth,
PasswordEncoder passwordEncoder,
AdminService adminService
) throws Exception {
auth.userDetailsService(adminService).passwordEncoder(passwordEncoder);
}


@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/", "/event/**", "/places/**")
.permitAll()
.anyRequest()
.authenticated()
.and()
.formLogin()
.permitAll()
.and()
.logout()
.permitAll()
.logoutSuccessUrl("/")
;

return http.build();
}
}
3 changes: 3 additions & 0 deletions src/main/java/com/bm/getin/repository/AdminRepository.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,8 @@
import com.bm.getin.domain.Admin;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.Optional;

public interface AdminRepository extends JpaRepository<Admin, Long> {
Optional<Admin> findByEmail(String email);
}
29 changes: 28 additions & 1 deletion src/main/java/com/bm/getin/service/AdminService.java
Original file line number Diff line number Diff line change
@@ -1,4 +1,31 @@
package com.bm.getin.service;

public class AdminService {
import com.bm.getin.domain.Admin;
import com.bm.getin.repository.AdminRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
@RequiredArgsConstructor
public class AdminService implements UserDetailsService {
private final AdminRepository adminRepository;

@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Admin admin = adminRepository.findByEmail(username)
.orElseThrow(() -> new UsernameNotFoundException("Admin Not Found."));

return User.builder()
.username(admin.getEmail())
.password(admin.getPassword())
.authorities(List.of())
.build()
;
}
}
2 changes: 1 addition & 1 deletion src/main/resources/data.sql
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ values

insert into `admin` (`email`, `nickname`, `password`, `phone_number`, `memo`)
values
('[email protected]', '테스트', '1234', '010-0101-0101', '안녕하세요')
('[email protected]', '테스트', '{noop}1234', '010-0101-0101', '안녕하세요')
;

insert into `admin_place_map` (`admin_id`, `place_id`)
Expand Down
15 changes: 12 additions & 3 deletions src/test/java/com/bm/getin/controller/AdminControllerTest.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.bm.getin.controller;

import com.bm.getin.config.SecurityCustomConfig;
import com.bm.getin.constant.AdminOperationStatus;
import com.bm.getin.constant.EventStatus;
import com.bm.getin.constant.PlaceType;
Expand All @@ -11,8 +12,11 @@
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.FilterType;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.http.MediaType;
Expand All @@ -37,7 +41,12 @@
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

@DisplayName("View 컨트롤러 - 어드민")
@WebMvcTest(AdminController.class)
@WebMvcTest(
controllers = AdminController.class,
excludeAutoConfiguration = SecurityAutoConfiguration.class,
excludeFilters = @ComponentScan.Filter(
type = FilterType.ASSIGNABLE_TYPE, classes = SecurityCustomConfig.class)
)
class AdminControllerTest {
private final MockMvc mvc;
@MockBean
Expand Down Expand Up @@ -254,7 +263,7 @@ void givenNothing_whenRequestingNewEventPage_thenReturnsNewEventPage() throws Ex
void givenNewEvent_whenSavingEvent_thenSavesEventAndReturnsToListPage() throws Exception {
// Given
long placeId = 1L;
EventRequest eventRequest = EventRequest.of(null,"test event", EventStatus.OPENED, LocalDateTime.now(), LocalDateTime.now(), 10, 10, null);
EventRequest eventRequest = EventRequest.of(null, "test event", EventStatus.OPENED, LocalDateTime.now(), LocalDateTime.now(), 10, 10, null);
given(eventService.upsertEvent(eventRequest.toDto(PlaceDto.idOnly(placeId)))).willReturn(true);

// When & Then
Expand Down Expand Up @@ -295,7 +304,7 @@ void given_whenAfterOperation_thenRedirectsToPage() throws Exception {
@Test
void givenPlaceObject_whenConverting_thenReturnsFormData() {
// Given
PlaceRequest placeRequest = PlaceRequest.of(null,PlaceType.SPORTS, "강남 배드민턴장", "서울시 강남구 강남동", "010-1231-2312", 10, null);
PlaceRequest placeRequest = PlaceRequest.of(null, PlaceType.SPORTS, "강남 배드민턴장", "서울시 강남구 강남동", "010-1231-2312", 10, null);

// When
String result = objectToFormData(placeRequest);
Expand Down
11 changes: 10 additions & 1 deletion src/test/java/com/bm/getin/controller/AuthControllerTest.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
package com.bm.getin.controller;

import com.bm.getin.config.SecurityCustomConfig;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.FilterType;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;

Expand All @@ -12,7 +16,12 @@
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

@DisplayName("View 컨트롤러 - 인증")
@WebMvcTest(AuthController.class)
@WebMvcTest(
controllers = AuthController.class,
excludeAutoConfiguration = SecurityAutoConfiguration.class,
excludeFilters = @ComponentScan.Filter(
type = FilterType.ASSIGNABLE_TYPE, classes = SecurityCustomConfig.class)
)
class AuthControllerTest {

private final MockMvc mvc;
Expand Down
11 changes: 10 additions & 1 deletion src/test/java/com/bm/getin/controller/BaseControllerTest.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
package com.bm.getin.controller;

import com.bm.getin.config.SecurityCustomConfig;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.FilterType;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;

Expand All @@ -13,7 +17,12 @@
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

@DisplayName("View 컨트롤러 - 기본 페이지")
@WebMvcTest(BaseController.class)
@WebMvcTest(
controllers = BaseController.class,
excludeAutoConfiguration = SecurityAutoConfiguration.class,
excludeFilters = @ComponentScan.Filter(
type = FilterType.ASSIGNABLE_TYPE, classes = SecurityCustomConfig.class)
)
class BaseControllerTest {

// @Autowired
Expand Down
11 changes: 10 additions & 1 deletion src/test/java/com/bm/getin/controller/EventControllerTest.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
package com.bm.getin.controller;

import com.bm.getin.config.SecurityCustomConfig;
import com.bm.getin.constant.EventStatus;
import com.bm.getin.dto.EventDto;
import com.bm.getin.service.EventService;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.FilterType;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.http.MediaType;
Expand All @@ -24,7 +28,12 @@
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

@DisplayName("View 컨트롤러 - 이벤트")
@WebMvcTest(EventController.class)
@WebMvcTest(
controllers = EventController.class,
excludeAutoConfiguration = SecurityAutoConfiguration.class,
excludeFilters = @ComponentScan.Filter(
type = FilterType.ASSIGNABLE_TYPE, classes = SecurityCustomConfig.class)
)
class EventControllerTest {

private final MockMvc mvc;
Expand Down
11 changes: 10 additions & 1 deletion src/test/java/com/bm/getin/controller/PlaceControllerTest.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
package com.bm.getin.controller;

import com.bm.getin.config.SecurityCustomConfig;
import com.bm.getin.dto.PlaceDto;
import com.bm.getin.service.PlaceService;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.FilterType;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
Expand All @@ -22,7 +26,12 @@
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

@DisplayName("View 컨트롤러 - 장소")
@WebMvcTest(PlaceController.class)
@WebMvcTest(
controllers = PlaceController.class,
excludeAutoConfiguration = SecurityAutoConfiguration.class,
excludeFilters = @ComponentScan.Filter(
type = FilterType.ASSIGNABLE_TYPE, classes = SecurityCustomConfig.class)
)
class PlaceControllerTest {

private final MockMvc mvc;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,27 @@
package com.bm.getin.controller.error;

import com.bm.getin.config.SecurityCustomConfig;
import com.bm.getin.controller.EventController;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.FilterType;
import org.springframework.test.web.servlet.MockMvc;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@DisplayName("View 컨트롤러 - 에러")
@WebMvcTest(BaseErrorController.class)
@WebMvcTest(
controllers = BaseErrorController.class,
excludeAutoConfiguration = SecurityAutoConfiguration.class,
excludeFilters = @ComponentScan.Filter(
type = FilterType.ASSIGNABLE_TYPE, classes = SecurityCustomConfig.class)
)
class BaseErrorControllerTest {
private final MockMvc mvc;

Expand Down

0 comments on commit 681f7b3

Please sign in to comment.