[Spring Boot Tutorial] 4. Spring security 맛보기

2019. 10. 4. 10:43Spring/Spring Boot Tutorial

반응형

이전 게시글
[Spring Boot Tutorial] 1. spring boot 시작하기 + 초기세팅
[Spring Boot Tutorial] 2. MySQL + JPA 설정
[Spring Boot Tutorial] 3. JPA CRUD


Spring security ?

Spring 기반 웹 애플리케이션의 보안기능 구현에 이용되는 프레임워크.
다음과 같은 보안 기능을 제공해줍니다.

  • 로그인 인증(Authentication)
  • 권한에 따른 접근 인가(Authorization)
  • 세션 관리
  • 암호화 (encryption)
  • CSRF(cross site request forgery) 공격 방어
  • 브라우저 기능을 이용한 공격으로부터 방어

본격적으로 spring security를 사용하기 이전에 이번 포스팅에서는 spring security 스타터폼 의존성 라이브러리를 추가하는 것만으로 자동 설정되는 시큐리티 설정을 활용한 맛보기 과정을 진행합니다.
맛보기 과정은 다음과 같습니다.


Spring security 맛보기 과정

  1. 의존성 라이브러리 추가
  2. 프로퍼티에 security user 정보 추가
  3. 컨트롤러 작성
  4. 뷰 화면 만들기
  5. 결과
  6. 추후 개선해야할 사항
    1. 로그인과 상관없이 api는 사용할 수 있도록 변경하기
    2. url 별 접근 권한 설정하기


1. pom.xml 에 의존성 라이브러리 추가

spring-security와 thymeleaf 템플릿을 이용하기 위해 pom.xml에 maven repository 의존성 라이브러리를 추가합니다.

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
	<groupId>org.thymeleaf.extras</groupId>
	<artifactId>thymeleaf-extras-springsecurity5</artifactId>
</dependency>

view 화면을 만드는 방법(jsp, thymeleaf 등..)은 다양합니다.
그 중 저는 타임리프 템플릿 엔진을 이용하여 표현할 것입니다.
타임리프를 이용하기 위해 thymeleaf 스타터 폼을 추가하고

타임리프 템플릿 엔진에서 spring security 인증, 인가 정보를 활용하기 위해 thymeleaf-extras-springsecurity5 를 추가합니다.


2. 프로퍼티에 security user 정보 추가

spring boot에서 spring-security 스타터폼을 추가하면 security관련 설정이 아래와같이 자동 설정됩니다.

  • 로그인 url (GET) : /login
  • 로그인 성공 후 실행될 url (GET) : /
  • GUID(Globally Unique Identifier) username : user
  • GUID password : 랜덤값 (서버 시작시, 로그에 출력됩니다.)

URLscheme://호스트명:포트명/login 으로 접속하면 기본적으로 제공해주는 로그인 폼 웹화면이 나오고,
username: user, password: 로그에 출력된 랜덤 생성 비밀번호를 기입 후 Sign in 버튼을 누르면
URLscheme://호스트명:포트명/ 으로 최종적으로 이동합니다.


29

자동으로 생성되는 GUID password는 Using generated security password라는 메시지와 함께 출력된 값입니다.
이 비밀번호를 로그인할 때 사용하면 됩니다.

30

지금 상황에서 Sign in 버튼을 누를경우 404 에러 페이지가 출력됩니다.
왜냐하면 로그인 성공후 "/" url로 GET method가 실행될 텐데, 이 메서드에 대한 기능 정의를 하지 않았기 때문입니다.


로그인 과정을 본격적으로 진행하는 것에 앞서, security user에 대한 기본 설정을 프로퍼티에 추가합니다.

spring:
  security:
    user:
      name: admin
      password: admin
      roles:
      - ADMIN
      - VIEW

기본적으로 생성되던 username: user, password: 서버시작시 랜덤 생성 은 비밀번호가 서버 재시작 시 매번 바뀌는 번거로움이 있기 때문에 위와 같이 프로퍼티에 정의했습니다.

그리고, 이 유저는 ROLE_ADMIN, ROLE_VIEW 롤을 가집니다. (prefix로 ROLE_이 자동으로 붙습니다.)
우리는 이 role을 이용해서 인가처리를 할 예정입니다.

이번 포스팅은 스프링 시큐리티에 대한 대략적인 기능을 맛보기 위한 목적이기 때문에 단일 사용자로 과정을 진행하지만, 다음 포스팅에서는 DB를 통한 여러 사용자의 인증, 인가 처리를 진행할 겁니다.


3. 컨트롤러 작성

2번 과정에서, 로그인이 되지 않았던 것이, 로그인 성공 후 이동될 "/" GET method 정의를 하지 않았기 때문이라 했었죠?
컨트롤러 클래스를 하나 생성하여 메서드를 하나 추가합시다.

컨트롤러 클래스를 하나 추가하여 로그인 한 유저의 'ROLE_VIEW' 권한 보유 유무에 따라 이동할 페이지를 분기처리 해봅시다.


57

spring security 에서는 org.springframework.security.core.userdetails.UserDetails 의 구현 클래스를 이용하여 로그인 사용자 정보를 가져옵니다.

그 중, db를 통한 설정없이 프로퍼티나 인메모리를 이용한 기본적인 spring security를 이용할 때에는 별도의 구현 클래스를 생성할 필요없이 spring-boot-starter-security 에 내장된 UserDetails 인터페이스의 구현클래스org.springframework.security.core.userdetails.User 클래스를 이용하여 로그인 사용자 정보를 가져올 수 있습니다.

User클래스에는 로그인사용자의 username, password 그리고 UserRole 목록(Set<GrantedAuthority> authorities)를 가지고 있습니다.

import org.springframework.security.core.userdetails.User;

@Controller
public class LoginController {

    @GetMapping(value = "")
    public String login(@AuthenticationPrincipal User user){
        if(user != null) {
                if(user.getAuthorities().contains(new SimpleGrantedAuthority("ROLE_VIEW"))) {
                return "redirect:/v";
            }
            }
        return "redirect:/login";
    }

}

로그인한 사용자에 관한 정보를 가져오기 위해 @AuthenticationPrincipal 애터네이션을 붙인 user 인자를 이용합니다.
user는 ln 7을 보면 알 수 있듯, 우리가 이전 포스팅에서 생성한 User 엔티티가 아닌 다른 클래스의 인스턴스입니다.

UserDetails를 구현한 클래스인 User를 이용하여 로그인 객체를 받았고,
사용자가 ROLE_VIEW 롤을 가지고 있을 경우 /v url로 redirect 하도록 정의했습니다.


@RequestMapping("/v")
@Controller
public class VController {

	@GetMapping("")
	public String main() {
		return "content/main";
	}
}

4. 뷰 화면 만들기

위의 컨트롤러에서 로그인 성공 후, 그 유저가 ROLE_VIEW 롤을 가지고 있을 경우 "/v" 로 이동하며, 그 url은 내부적으로 "content/main" 이라는 뷰로 이동하도록 설정했습니다.

spring boot 에서 자동으로 설정되어 있는 뷰 페이지 prefix는 classpath:/templates/, suffix는 .html입니다.
즉, content/mainclasspath:/templates/content/main.html
아래의 폴더 구조를 참고하세요.

31

※ classpath의 위치는 src/main/resources 입니다.

JSP 에서 spring-security 기능을 사용하려면 아래와 같이 taglib를 추가해야했었죠?

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5">
<head>
<meta charset="UTF-8">
<title>demo main</title>
</head>
<body>
<h1>demo main 페이지</h1>
<th:block sec:authorize="isAuthenticated()">
	<span sec:authentication="principal.username"></span>님 반가워요!
</th:block>
<a th:href="@{/logout}">로그아웃</a>
</body>
</html>

JSP 뷰에 spring-security를 이용해 본적 있는 사람이라면 익숙한 문법들이 보일거에요.

타임리프 템플릿을 이용한 html 에서는 태그라이브러리를 사용하는 것 대신 xml 네임스페이스에 sec, th 등의 이용을 추가합니다.
( 만일 pom.xml 에 thymeleaf 사용에 필요한 의존성 라이브러리를 추가하지 않을 경우에는 권한에 관한 기능이 제대로 작동하지 않을 것입니다. 1번에 알려준 라이브러리들을 모두 추가했는지 확인해주세요. )

우선, ln 10~12 에 작성된 <th:block> 태그는 로그인이 된 사용자만 보여주도록 하기 위해
sec:authorize="isAuthenticated()" 속성을 추가 했습니다. 이 속성을 추가할 경우에는 로그인된 사용자만 접근할 수 있습니다.

그리고, sec:authentication="principal.username" 로 로그인한 사용자의 username도 출력할 수 있습니다.

타임리프에서는 @ 를 이용하여 서버내의 리소스를 호출할 수 있습니다.
타임리프가 제공하는 context-relative url을 이용하고 싶다면 href 속성이 아닌 th:href 속성을 이용해야 합니다.
(비슷한 기능으로 th:href, th:action, th:onclick 를 제공합니다. 맨 앞에 prefix로 th:가 붙는다는 걸 잊지 마세요!)


5. 결과

여기까지의 과정을 모두 따라했다면, 서버를 시작해서 로그인 과정을 따라해봅시다.

32

username: admin, password: admin 으로 로그인 하면


33

username을 담고 있는 데모 메인 페이지가 보일거에요. 그리고 로그아웃 링크를 누르면


34

로그아웃 할 것인지 한번 더 묻고


35

로그아웃 성공후 다시 login페이지로 도착했습니다.


위의 그림들을 보면 기능상으로는 문제가 없어보인다.
그러나 이 설정들 만으로는 우리가 기대하는 기능을 충족하지 못합니다.
원치않는 2가지 현상을 소개시켜주면서 이번 페이지를 마치겠습니다.


6. 개선해야할 사항

6.1 로그인과 상관없이 api는 사용할 수 있도록 변경하기

지난 시간에 만들었던 user 를 조회하는 api를 접속하려고 시도할 경우 401 에러가 발생합니다.

36

spring security 기본 설정에서는 login페이지를 제외한 모든 페이지는 인증(Authentication)과정을 통과한 유저만 사용할 수 있도록 설정되어있기 때문입니다.
따라서 api 조회도 로그인 과정을 거치지 않고서는 401 에러를 발생시키는 겁니다.

6.2. url 별 접근 권한 설정하기

그리고, 2번에서 설정한 프로퍼티 roles 중 'VIEW'를 제거한 후, 로그인을 시도해보자.
'admin' 사용자가 'ROLE_VIEW' 롤을 가지고 있지 않아 다시 login 페이지로 이동될 것입니다.
그러나, url로 직접 http://localhost:8989/v 로 접속할 경우 접속이 되는 것을 확인할 수 있을 것입니다.

롤 권한과 상관없이 /v 페이지로 이동이 가능하다니! 이유가 왜일까요?

이유는 우리가 로그인 성공시, GET http://localhost:8989/ 로 이동하여 롤 보유여부를 체크하여 http://localhost:8989/v 페이지로 이동하는 로직만 짰을 뿐이지
/v url에 관한 개별적인 access 조건을 정의하지 않았기 때문입니다.

spring boot에서 기본적으로 제공해주는 spring security 설정만으로는 우리가 원하는 인증 인가 처리를 하기엔 부족합니다.

이어지는 포스팅에서는 Spring Security를 필요에 따라 직접 custom하는 방법을 알아볼 것입니다.


++ plus tip

cURL과 postman에서 host 앞에 username:password@ 를 붙이면 api 응답이 됩니다.

37


38

728x90
반응형