본문 바로가기

FastCampus - 한번에 끝내는 Java|Spring 웹 개발/04-2 시큐리티

Security 02-05: Basic Auth (2) - 패스트캠퍼스 챌린지 49일차

이번 강의에서는 Basic Auth 외에도 일반적인 CSRF 가 필요한 웹사이트와 함께 운용하기 위해서 별도의 Configuration 을 추가합니다. 프로젝트가 갑자기 이전 강의 거로 돌아가는게 제일 당황스럽던;

@Configuration 을 별도로 추가하자

강의에서는 사전에 준비해두신 프로젝트로 진행해서 BasicAuth configuration 을 한 번 더 추가했는데요, 저는 이전 강의에서 직접 만들었던 프로젝트에서 이어서 작업하므로 거꾸로 일반 웹페이지 형식으로 받는 Configuration 을 추가해야 합니다.

기존 Basic Auth 설정을 /api/v1 로 이동

일단 기존 핸들러들을 /api 아래로 다 옮겨보겠습니다.

@RestController
@RequestMapping("/api/v1")
class GreetingApiController {
    @GetMapping("/greeting")
    fun greeting() = "Hello from API!"

    @PostMapping("/greeting")
    fun greeting(@RequestBody name: String) = "Hello from API, ${name}!"
}

설정도 옮기고요.

  • Enable* 두 개는 웹 서비스를 해줄 곳에서 추가할 거고 두 개 있으면 오류가 나므로 주석처리합니다.
    • configure 도 마찬가지로 주석처리하긴 했는데... 음, 잘 되네요.
  • @Order(1)  을 추가해서 이 설정이 먼저 처리되도록 했습니다.
@Order(1)
@Configuration
//@EnableWebSecurity          // << comment out
//@EnableGlobalMethodSecurity // << comment out
open class ApiSecurityConf: WebSecurityConfigurerAdapter() {
    override fun configure(http: HttpSecurity) {
        http
            .antMatcher("/api/**") // << new
            .csrf().disable()
            .authorizeRequests().anyRequest().authenticated()
            .and()
            .httpBasic()
    }

//    override fun configure(auth: AuthenticationManagerBuilder) {
//        auth.inMemoryAuthentication()
//            .withUser(
//                // WARNING: This method is considered unsafe for production and is only intended for sample applications.
//                User.withDefaultPasswordEncoder()
//                    // 이 밑은 deprecated 나 경고가 없습니다.
//                    .username("user1")
//                    .password("1111")
//                    .roles("USER")
//                    .build()
//            )
//    }
}

/api/v1/greeting 에 get 을 요청 (basic auth)

웹서비스 설정 및 핸들러 추가

다른 건 몰라도 thymeleaf 하나만큼은 쓸 줄 몰라서 실습 소스 저장소를 봐야만 했습니다.

https://github.com/jongwon/sp-fastcampus-spring-sec/tree/5-2-BasicAuthenticationFilter-Test/server/login-custom-filter/src/main/resources/templates

먼저 Order 는 2구요, 원본 강의에서는 이전에 했던 걸 가져와서 하면서 커스텀 필터도 적용되는 걸 보여줬는데, 강의에서 확인했으니 굳이 그걸 제가 하진 않겠습니다.

@Order(2)
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
open class SecurityConf: WebSecurityConfigurerAdapter() {
    override fun configure(http: HttpSecurity) {
        http
            .formLogin().and()
            .authorizeRequests().antMatchers("/").permitAll()
            .and()
            .authorizeRequests().anyRequest().authenticated()
            .and()
            .logout { it.logoutSuccessUrl("/") }
    }

    override fun configure(auth: AuthenticationManagerBuilder) {
//        super.configure(auth)
        auth.inMemoryAuthentication()
            .withUser(
                // WARNING: This method is considered unsafe for production and is only intended for sample applications.
                User.withDefaultPasswordEncoder()
                    // 이 밑은 deprecated 나 경고가 없습니다.
                    .username("user1")
                    .password("1111")
                    .roles("USER")
                    .build()
            )
    }

    override fun configure(web: WebSecurity) {
        web.ignoring().requestMatchers(PathRequest.toStaticResources().atCommonLocations())
    }
}

컨트롤러는 이렇게. ThymeLeaf 를 경유해서 화면에 템플릿으로 값을 뿌려주려면 model 에 값을 넣어야 합니다. 일단 잘은 모르겠는데 이름을 넣으면 나오기는 합니다.

@Controller // RestController 이면 뷰 렌더링이 안 됨
class GreetingController {
    @GetMapping("/greeting")
    fun greeting(): String {
        return "greeting"
    }

    @PostMapping("/greeting")
    fun greeting(@RequestParam(name = "username") username: String, model: Model): String {
        model.addAttribute("username", username)
        return "greeting_with_result"
    }
}

html 은 resources/templates 경로에 적당히 넣습니다.

index.html

<!DOCTYPE html>

<html lang="ko" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head>
    <meta charset="UTF-8">
    <title>Title</title>

    <link rel="stylesheet" th:href="@{/css/bootstrap.css}">
    <link rel="stylesheet" th:href="@{/css/index.css}">
</head>
<body>
index
<ul>
    <li><a href="/greeting">greeting</a></li>
    <li><form th:action="@{/greeting}" th:object="${String}" method="post">
        <input name="name"/>
        <input type="submit">greeting (post)</input>
    </form></li>
    <li><form th:action="@{/logout}" method="post"><input type="submit">logout</button>
    </form></li>
</ul>
</body>
</html>

greeting.html

<!DOCTYPE html>

<html lang="ko" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
hello!
</body>
</html>

greeting_with_result.html

여기서 골머리를 썩인게, th:text="${패러미터}" 식이어야지 템플릿 채우기가 됩니다. 대체 뭔; 저는 일반 템플릿 엔진처럼 그냥 본문에 떡하니 ${패러미터} 하고 쓰면 당연히 될 줄 알았죠. 그런 거 아니었습니다.

https://spring.io/guides/gs/handling-form-submission/
https://www.thymeleaf.org/doc/articles/springmvcaccessdata.html
https://www.baeldung.com/spring-thymeleaf-request-parameters

<!DOCTYPE html>

<html lang="ko" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
hello! <span th:text="${username}">몰?루</span>!
</body>
</html>

이렇게 해놓으면 post 및 login 이 막히는 것도 확인해볼 수 있습니다. CSRF야 뭐 개발자 도구 열어보면 되겠죠?

본 포스팅은 패스트캠퍼스 환급 챌린지 참여를 위해 작성되었습니다.
패스트캠퍼스: https://bit.ly/37BpXiC

#패스트캠퍼스 #패캠챌린지 #직장인인강 #직장인자기계발 #패스트캠퍼스후기 #한번에끝내는JavaSpring웹개발마스터초격차패키지Online

내일이 마지막입니다. 시큐리티 공부는 어디로 가는가...