시큐리티)
POM.XML 설정추가
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
시큐리티 속성 추가
spring.security.user.name= 기본 사용자 이름, 기본값 user
spring.security.user.password= 기본 사용자의 암호, 기본값 UUID
spring.security.user.roles= 기본 사용자의 권한, 기본값 없다
서버시작시 실행로그
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | . ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.2.4.RELEASE) 2020-02-10 04:03:45.159 INFO 1648 --- [ main] com.example.demo.DemoApplication : Starting DemoApplication on DESKTOP-6HPEM1U with PID 1648 (D:\example\springboot2_mvc_example1\target\classes started by k in D:\example\springboot2_mvc_example1) 2020-02-10 04:03:45.164 INFO 1648 --- [ main] com.example.demo.DemoApplication : No active profile set, falling back to default profiles: default 2020-02-10 04:03:46.114 INFO 1648 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8443 (https) 8080 (http) 2020-02-10 04:03:46.128 INFO 1648 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat] 2020-02-10 04:03:46.129 INFO 1648 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.30] 2020-02-10 04:03:46.191 INFO 1648 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext 2020-02-10 04:03:46.191 INFO 1648 --- [ main] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 984 ms 2020-02-10 04:03:46.413 INFO 1648 --- [ main] .s.s.UserDetailsServiceAutoConfiguration : Using generated security password: d688f762-97d5-4949-ab11-242830a58c97 2020-02-10 04:03:46.462 INFO 1648 --- [ main] o.s.s.web.DefaultSecurityFilterChain : Creating filter chain: any request, [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@594d9f07, org.springframework.security.web.context.SecurityContextPersistenceFilter@101a461c, org.springframework.security.web.header.HeaderWriterFilter@4232b34a, org.springframework.security.web.csrf.CsrfFilter@12ad1b2a, org.springframework.security.web.authentication.logout.LogoutFilter@6d2a2560, org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter@2e86807a, org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter@49fa1d74, org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter@5befbac1, org.springframework.security.web.authentication.www.BasicAuthenticationFilter@3a4ab7f7, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@5ebffb44, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@d08edc5, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@5e9f73b, org.springframework.security.web.session.SessionManagementFilter@f5ce0bb, org.springframework.security.web.access.ExceptionTranslationFilter@751ae8a4, org.springframework.security.web.access.intercept.FilterSecurityInterceptor@6bee793f] 2020-02-10 04:03:46.929 INFO 1648 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8443 (https) 8080 (http) with context path '' 2020-02-10 04:03:46.932 INFO 1648 --- [ main] com.example.demo.DemoApplication : Started DemoApplication in 2.062 seconds (JVM running for 2.873) | cs |
응답결과)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | GET https://localhost:8443/books/ HTTP/1.1 401 Cache-Control: private Expires: Thu, 01 Jan 1970 00:00:00 GMT Set-Cookie: JSESSIONID=FF67234CA9ACE835526DC0724111B40B; Path=/; Secure; HttpOnly WWW-Authenticate: Basic realm="Realm" X-Content-Type-Options: nosniff X-XSS-Protection: 1; mode=block Strict-Transport-Security: max-age=31536000 ; includeSubDomains X-Frame-Options: DENY Content-Type: application/json Transfer-Encoding: chunked Date: Sun, 09 Feb 2020 19:01:00 GMT Keep-Alive: timeout=60 Connection: keep-alive { "timestamp": 1581274860648, "status": 401, "error": "Unauthorized", "message": "Unauthorized", "path": "/books/" } Response code: 401; Time: 2170ms; Content length: 105 bytes Cookies are preserved between requests: > ..../springboot2_mvc_example1/.idea/httpRequests/http-client.cookies | cs |
@GetMapping("/books/")
@ResponseBody
public Iterable<Book> all(){
return bookService.findAll();
}
GET https://localhost:8443/books/
Authorization: Basic user fc2c0d5b-f2b4-498f-95e4-3cbfc51a104d
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | GET https://localhost:8443/books/ HTTP/1.1 200 Cache-Control: private Expires: Thu, 01 Jan 1970 00:00:00 GMT Set-Cookie: JSESSIONID=F93016E073AE5AF5EA824AA9A4CCDE9B; Path=/; Secure; HttpOnly X-Content-Type-Options: nosniff X-XSS-Protection: 1; mode=block Strict-Transport-Security: max-age=31536000 ; includeSubDomains X-Frame-Options: DENY Content-Type: application/json Transfer-Encoding: chunked Date: Sun, 09 Feb 2020 19:41:03 GMT Keep-Alive: timeout=60 Connection: keep-alive [ { "isbn": "9780618260300", "title": "The Hobbit", "authors": [ "J.R.R, Tolkien" ] }, { "isbn": "9780451524935", "title": "1984", "authors": [ "Georeg Orwell" ] }, { "isbn": "9780061120084", "title": "To Kill a Mockingbird", "authors": [ "Harper Lee" ] } ] Response code: 200; Time: 537ms; Content length: 226 bytes Cookies are preserved between requests: > .../springboot2_mvc_example1/.idea/httpRequests/http-client.cookies | cs |
스프링 보안 테스트 애노테이션
@WithMockUSer : 주어진 username, password와 roles/authorities를 사용자로 실행한다.
@WithAnonymouseUser : 익명 사용자로 실행한다.
@WithUserDetails : UserDetailService에서의 설정된 이름의 사용자로 실행한다.
테스트)
@WithMockUser 주석처리후 테스트
package com.example.demo;
import com.example.demo.library.Book;
import com.example.demo.library.BookController;
import com.example.demo.library.BookService;
import org.hamcrest.Matchers;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.when;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
@ExtendWith(SpringExtension.class)
@SpringBootTest
@AutoConfigureMockMvc
//@WithMockUser
public class BookControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private BookService bookService;
@Test
public void shouldReturnListOfBooks() throws Exception{
List<Book> books = Arrays.asList(
new Book("123", "Spring 5 Recipes", Arrays.asList("Marten Deinum", "Josh Long")),
new Book("321", "Pro Spring MVC", Arrays.asList("Marten Deinum", "Colin Yates")));
when(bookService.findAll()).thenReturn(books);
mockMvc.perform(get("/books/")
.with(csrf())
)
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE))
.andExpect(MockMvcResultMatchers.jsonPath("$", Matchers.hasSize(2)))
.andExpect(MockMvcResultMatchers.jsonPath("$[*].isbn", Matchers.containsInAnyOrder("123","321")))
.andExpect(MockMvcResultMatchers.jsonPath("$[*].title",Matchers.containsInAnyOrder("Spring 5 Recipes", "Pro Spring MVC")));
}
@Test
public void shouldReturn404WhenBookNotFound() throws Exception{
when(bookService.find(anyString())).thenReturn(Optional.empty());
mockMvc.perform(get("/books/123")).andExpect(status().isNotFound());
}
@Test
public void shouldAddBook() throws Exception{
when(bookService.create(any(Book.class))).thenReturn(
new Book("123456789", "Test Book Stored", Arrays.asList("T. Author")));
mockMvc.perform(post("/books")
.contentType(MediaType.APPLICATION_JSON)
.content("{\n" +
" \"isbn\":\"123456789\",\n" +
" \"title\":\"Test Book Stored\",\n" +
" \"authors\":[\"T. Author\"]\n" +
"}")
.with(csrf())
)
.andExpect(status().isCreated())
.andExpect(header().string("Location","http://localhost/books/123456789"));
}
}
3개 메소드중 shouldAddBook()만 적겠습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 | MockHttpServletRequest: HTTP Method = POST Request URI = /books Parameters = {_csrf=[78784fe4-0ee7-476b-8f27-164f31aa8883]} Headers = [Content-Type:"application/json;charset=UTF-8", Content-Length:"81"] Body = { "isbn":"123456789", "title":"Test Book Stored", "authors":["T. Author"] } Session Attrs = {} Handler: Type = null Async: Async started = false Async result = null Resolved Exception: Type = null ModelAndView: View name = null View = null Model = null FlashMap: Attributes = null MockHttpServletResponse: Status = 401 Error message = Unauthorized Headers = [WWW-Authenticate:"Basic realm="Realm"", X-Content-Type-Options:"nosniff", X-XSS-Protection:"1; mode=block", Cache-Control:"no-cache, no-store, max-age=0, must-revalidate", Pragma:"no-cache", Expires:"0", X-Frame-Options:"DENY"] Content type = null Body = Forwarded URL = null Redirected URL = null Cookies = [] java.lang.AssertionError: Status expected:<201> but was:<401> Expected :201 Actual :401 <Click to see difference> at org.springframework.test.util.AssertionErrors.fail(AssertionErrors.java:59) at org.springframework.test.util.AssertionErrors.assertEquals(AssertionErrors.java:122) at org.springframework.test.web.servlet.result.StatusResultMatchers.lambda$matcher$9(StatusResultMatchers.java:627) at org.springframework.test.web.servlet.MockMvc$1.andExpect(MockMvc.java:196) at com.example.demo.BookControllerTest.shouldAddBook(BookControllerTest.java:82) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:566) at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:675) at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60) at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:125) at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:132) at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:124) at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:74) at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115) at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105) at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:104) at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:62) at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:43) at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:35) at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104) at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98) at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$6(TestMethodTestDescriptor.java:202) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:198) at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:135) at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:69) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:135) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125) at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122) at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80) at java.base/java.util.ArrayList.forEach(ArrayList.java:1540) at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125) at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122) at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80) at java.base/java.util.ArrayList.forEach(ArrayList.java:1540) at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125) at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135) at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123) at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122) at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80) at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32) at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57) at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51) at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:229) at org.junit.platform.launcher.core.DefaultLauncher.lambda$execute$6(DefaultLauncher.java:197) at org.junit.platform.launcher.core.DefaultLauncher.withInterceptedStreams(DefaultLauncher.java:211) at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:191) at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:128) at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:69) at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33) at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:230) at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58) | cs |
반응형
'WEB > 스프링 부트 2' 카테고리의 다른 글
Spring Security + H2 (0) | 2020.02.12 |
---|---|
TestRestTemplate, WebTestClient (0) | 2020.02.11 |
WebFlux + Thymeleaf (0) | 2020.02.07 |
WebFlux (0) | 2020.02.07 |
비동기 Emitter (0) | 2020.02.06 |