본문 바로가기

개발/프로젝트

[스프링부트 게시판] 17. Spring Security

이번 게시글에서는 로그인 기능을 스프링 시큐리티 라이브러리를 사용하여 수정하도록 하겠습니다.

 

상세한 내용은 공식 홈페이지 Docs와 아래 블로그 링크를 참고하시면 됩니다.

 

Spring Security :: Spring Security

If you are ready to start securing an application see the Getting Started sections for servlet and reactive. These sections will walk you through creating your first Spring Security applications. If you want to understand how Spring Security works, you can

docs.spring.io

 

 

스프링 부트 Spring Security

📝 스프링 시큐리티란? 스프링 시큐리티는 스프링 기반 애플리케이션의 보안(인증과 권한, 인가 등)을 담당하는 스프링 하위 프레임워크입니다. 주로 서블릿 필터와 이들로 구성된 필터체인으

daegwonkim.tistory.com

 

먼저 pom.xml 파일에서 이전에 주석처리를 했던 시큐리티 라이브러리와 관련된 부분에 대해서 전부 주석 해제를 진행합니다.

pom.xml

 

이후 UserApiController에 구현한 로그인 함수를 주석 처리를 하고 프로젝트를 실행하여 결과를 확인해봅니다.

 

Result

보이는 화면으로 출력이 된 것을 확인 하실 수 있습니다. 스프링 시큐리티 라이브러리가 작동이 되어있으면 해당 홈페이지의 어떤 경로를 통하든 스프링 시큐리티가 가로채서 위 화면으로 이동하게 됩니다.

 

위 화면에서 로그인하기 위해 Username을 'user'로 Password는 서버를 실행할 때 콘솔창에 출력된 다음 구문을 그대로 복사하면 됩니다.

 

Console

로그인에 성공하면 자동으로 세션이 생기게 됩니다.

 

이제 header.html 파일을 수정해주겠습니다.

우선 아래 태그 라이브러리를 구문을 header.html 파일의 위쪽에 추가해줍니다.

 

그리고 전에 th:block으로 if문 처리를 했던 곳에서 if를 지우고 이와 같은 코드를 작성합니다.

 

<header th:fragment="header">
    <nav class="navbar navbar-expand-sm bg-dark navbar-dark">
      <a class="navbar-brand" href="#">메인페이지</a>
      <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#collapsibleNavbar">
        <span class="navbar-toggler-icon"></span>
      </button>
      <th:block sec:authorize="isAnonymous()">
      <div class="collapse navbar-collapse" id="collapsibleNavbar">
        <ul class="navbar-nav">
          <li class="nav-item">
            <a class="nav-link" th:href="@{/user/loginForm}">로그인</a>
          </li>
          <li class="nav-item">
            <a class="nav-link" th:href="@{/user/joinForm}">회원가입</a>
          </li>
        </ul>
      </div>  
      </th:block>
      <th:block sec:authorize="isAuthenticated()">
      <div class="collapse navbar-collapse" id="collapsibleNavbar">
        <ul class="navbar-nav">
          <li class="nav-item">
            <a class="nav-link" href="#">로그아웃</a>
          </li>
          <li class="nav-item">
            <a class="nav-link" href="#">마이페이지</a>
          </li>
        </ul>
      </div>  
      </th:block>
    </nav>
</header>

 

아까 태그로 xmlns:secsec 속성의 코드를 작성할 수 있는데 이 때 authorize는 스프링 시큐리티로 로그인의 여부를 확인할 수 있습니다.

 

isAnnoymous() - 로그인 세션이 없는 경우

isAuthenticated() - 로그인 세션이 있는 경우

 

Result

 

처음엔 /toy로 전달하여서 /toy/main으로 링크 변경 후 헤더의 로그아웃 마이페이지가 정상적으로 나오는 것을 확인할 수 있습니다.


다음으로 스프링 시큐리티 로그인 페이지를 간단하게 커스터마이징을 해보도록 하겠습니다.

 

먼저 주소 정리를 위해 UserControllerUserApiController 클래스를 열고 다음과 같이 경로를 변경하였습니다.

UserController
UserApiController

 

여기서 /auth/... 경로는 인증이 되지 않은 사용자도 출입할 수 있는 경로를 의미합니다. 수정할 로그인폼 코드에서 user.js를 사용하지 않기 때문에 UserController에서 addScript를 주석 처리 하였습니다.

 

이 후 header.html 파일에 설정되어 있는 경로 역시 다음과 같이 수정해줍니다.

 

header.html

 

해당 코드는 비 로그인 사용자가 접근해야 하기 때문에 이에 해당하는 것만 수정하고 나머지는 건들지 않습니다.

 

[loginForm.html]

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
		xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
		layout:decorate="~{layouts/layout}">
<th:block layout:fragment="content">
<div class="container" style="margin-top:30px">
  	<form action="#" method="post">
	  <div class="form-group">
	    <label for="username">Username:</label>
	    <input type="text" class="form-control" placeholder="Enter email" id="username">
	  </div>
	  <div class="form-group">
	    <label for="password">Password:</label>
	    <input type="password" class="form-control" placeholder="Enter password" id="password">
	  </div>
	  <div class="form-group form-check">
	    <label class="form-check-label">
	      <input name="remember" class="form-check-input" type="checkbox"> Remember me
	    </label>
	  </div>
	  <button id="loginBtn" type="button" class="btn btn-primary">Login</button>
	</form>
</div>
</th:block>
</html>

 

loginForm.html 에서 <form> 태그에서 actionmethod 속성을 추가해주었습니다.


그리고 스프링 시큐리티를 설정을 하기 위해 config 패키지에 새로운 클래스 파일을 생성합니다.

 

 

전에 참고하던 스프링 시큐리티에서는 WebSecurityConfigurerAdapter를 사용하였으나 현재 스프링 공식에서 보안상에 문제로 SecurityFilterChain Bean을 권장하고 있어서 이를 활용하여 설명드리겠습니다.

 

먼저 URL의 불편함을 해결하기 위해 application.properties에서 server의 context-path를 지우겠습니다.

 

application.properties

 

 

그리고 SecurityConfig 클래스 파일로 들아가 이와 같이 코드를 작성합니다.

 

[SecurityConfig.java]

@Configuration
public class SecurityConfig {
	
	@Bean
	public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
		http
			.formLogin()
			.loginPage("/auth/loginForm")
			.defaultSuccessUrl("/main")
			.usernameParameter("username")
			.passwordParameter("password")
			.and()
			.authorizeHttpRequests()
			.requestMatchers("/auth/**").permitAll()
			.anyRequest().authenticated();
			
		return http.build();
	}
	
}

 

먼저 formLogin() 관련으로 설명 드리겠습니다.

 

formLogin()은 기존에 스프링 시큐리티 라이브러리에서 바로 전환하던 로그인 페이지를 설정해주는 것입니다. 

 

loginPage() - 스프링 시큐리티의 로그인 페이지 URL 설정

defaultSuccessUrl() - 로그인 성공 시 기본적으로 보내야할 URL 설정

usernameParameter() / passwordParameter() - 로그인 할 때 사용하는 변수 설정(User의 username과 password 사용)

 

그리고 중요한 authorizeHttpRequests() 에 대해 설명 드리겠습니다.

 

authorizeHttpRequests()은 URL에 따른 권한 설정입니다. 여기서 권한(Authorize)란 User 클래스에서 사용하던 RoleType에서 사용하던 ADMIN / USER를 이용하여 특정 URL에만 접근 할 수 있는 기능입니다. 이 후에 사용할 예정입니다.

 

requestMatchers() - 해당 URL에 대한 권한에 대해 설정하는 값입니다. 여기서 /auth 로 시작하는 URL은 permitAll(), 인증 에 대한 조건 없이 아무나 접속해도 된다 라는 뜻입니다.

anyRequest() - 위의 requestMatchers() 가 if이면 anyRequest()는 else와 동일합니다. 다른 URL은 인증받은 사용자만 접근이 가능하게 설정했습니다.

 

Result

 

정상적으로 화면이 출력 되는 것을 확인 하실 수 있습니다.