이번 포스팅에서 알아 볼 내용은 바로 Spring Security입니다. 기존의 Servlet 필터와 Interceptor로 구현했던 시큐리티 설정을 스프링 시큐리티를 통해서 구현할 수 있습니다. 실제 구현 과정은 많은 부분 생략하였고 설정 위주로 포스팅을 진행하였기 때문에 필요한 부분에 대해서 골라서 보시면 될꺼 같습니다.

1. 인증과 인가

스프링 시큐리티에서 자주 나오는 용어로 "인증"과 "인가"가 있습니다. 헷갈릴 수 있는 개념인데 인증은 현관문에서 비밀번호를 입력하고 집으로 들어가는 행위로 비유할 수 있습니다. 인가라는 단어는 "인가된 사용자"라는 용어로 많이 사용됩니다. 어떤 행위를 하는데 이 사용자가 권한이 있는 사용자라면 인가된 사용자라고합니다.

 

웹에서 인증이란 해당 URL은 보안 절차를 거친 사용자들만 접근할 수 있다는 것을 의미합니다. 게시글을 작성하기 위해서는 로그인을 해야하는데 이것은 인증을 했다는 것을 뜻합니다. 일반 유저는 관리자 페이지에 접근하지 못하는데 이는 관리자 계정만 관리자 페이지에 접근할 수 있도록 권한(인가)을 가지고 있다는 것으로 이해하시면 됩니다.

2. 스프링 시큐리티 동작방식

스프링 시큐리티는 세션/쿠키방식으로 인증합니다. 

1. 유저의 요청(http request)

2. AuthenticationManager는 UserDetailsService로부터 인증과 관련된 모든 정보를 UserDetails타입으로 만들어서 반환

3. spring security의 인메모리 세션저장소인 SecurityContextHolder에 저장

4. 유저에게 sesson ID와 함께 응답을 내려줌

5. 이후 요청에서는 요청쿠키에서 JESSIONID를 확인해서 유효하면 Authentication을 획득

3. 스프링 시큐리티 의존성 추가 및 application.properties 설정

스프링 시큐리티를 사용하기 위해서는 메이븐 기준으로 pom.xml에 2개의 의존성을 추가해야합니다. 아래 추가한 의존성은 타임리프에서 스프링 시큐리티를 사용하기 위해서 추가해줍니다.

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

<dependency>
    <groupId>org.thymeleaf.extras</groupId>
    <artifactId>thymeleaf-extras-springsecurity5</artifactId>
</dependency>

이제 다음으로 application.properties에 security 관련 로그를 출력하는 설정을 추가합니다. 로깅 레밸은 debug로 설정하겠습니다.

logging.level.org.springframework.security=debug

4. SecurityConfig 클래스 작성

이제 SecurityConfig 클래스를 작성해보겠습니다. 클래스 이름은 자신이 원하는걸로 만들면 되고, 여기서 중요한건 @EnableWebSecirity 어노테이션 추가와 WebSecurityConfigurerAdapter 클래스 상속입니다. configure() 메소드를 오버라이드해서 어플리케이션을 실행해보면 log.info에 작성한 내용이 출력되는 것을 확인할 수 있습니다. 

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        log.info("security config ......");
    }

}

또한 다른 로그로 스프링 시큐리티에서 자동으로 생성하는 패스워드와 로그가 출력되는 것을 확인할 수 있습니다. 아래는 로그 예시입니다. 스프링 시큐리티는 기본적으로 하나의 사용자 정보를 가지도록 세팅되어 있습니다. 사용자의 이름은 "user", 패스워드는 로그에 찍히는 비밀번호 입니다. 

Using default security password : 85259335-9ff9-4060-8e28-6760ac4cc6ff

이렇게 SecurityConfig를 생성하지 않고 컨트롤러와 타임리프를 이용해서 페이지를 제작하면 스프링 시큐리티는 모든 URI에 대한 인증을 요구하는데 이때 아이디와 기본적으로 생성된 비밀번호를 입력하면 인증을 받을 수 있습니다.

5. 회원 테이블 설계

회원 테이블을 먼저 설계하고 로그인,로그아웃, 페이지 접근 권한 등을 설정하는 방법에 대해서 알아 보겠습니다. 스프링 DATA JPA를 사용해서 테이블을 만들었으며, JPA 자체를 사용해서 데이터를 저장/수정/조회하는 방법은 생략하겠습니다.

@Getter
@Setter
@Entity
@Table(name = "member")
@ToString
@EqualsAndHashCode(of = "userId")
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class Member{

    @Id
    private String userId;

    private String userNm;

    private String userPwd;

    private String nickname;

    private String gender;

    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "Asia/Seoul")
    private LocalDate birthday;

    @OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    @JoinColumn(name = "userId")
    private List<MemberRole> roles;
}

페이지별로 접근 권한을 설정하기 위해서 MEMBER_ROLE 테이블을 추가로 만듭니다. 

@Entity
@Getter
@Setter
@Table(name = "member_role")
@EqualsAndHashCode(of = "")
@ToString
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class MemberRole extends BaseEntity{

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long rno;

    private String roleName;

}

 

회원가입을 구현하여 2개의 아이디를 만들고 MEMBER_ROLE 테이블에 한명은 MANAGER, 한명은 BASIC으로 roleName을 부여합니다. Spring Security 설정에 대해서 집중하기 위해 회원가입 과정은 생략하였습니다.

6. 로그인/로그아웃 및 페이지 권한 설정

이제 특정 권한을 가진 사람만이 특정 URI에 접근할 수 있도록 위에서 만들었던 SecurityConfig 클래스에 다음 코드를 추가합니다. "/guest"로 시작하는 url은 모든 사용자가 접근할 수 있고, "/manager"로 시작하는 url은 MANAGER ROLE을 가진 회원만 접근할 수 있습니다.

 

"http.formLogin.loginPage("/user/login")"을 통해 로그인 페이지와 연결을해주었고, 만약 로그인을 했지만 접근권한이 없을 때 처리하기 위해 컨트롤러에 "/accessDenied" 를 구현해 주었습니다. 마지막으로 로그아웃을 처리하기 위해 http.logout설정을 추가했습니다.

@Override
protected void configure(HttpSecurity http) throws Exception {
    log.info("security config ......");

    http.authorizeRequests().antMatchers("/guest/**").permitAll();
    
    http.authorizeRequests().antMatchers("/manager/**").hasRole("MANAGER");

    http.formLogin().loginPage("/user/login");
    
    http.exceptHandling().accessDeniedPage("/accessDenied");
    
    http.logout().logoutRequestMatcher(new AntPathRequestMatcher("/user/logout"))
                 .logoutSuccessUrl("/")
                 .invalidateHttpSession(true)
                 .permitAll();

}

추가로 궁금하신 점은 댓글로 남겨주시면 답변 드리겠습니다.

REFERENCE

스타트 스프링 부트 (구멍가게 코딩단) 도서 참고

https://sjh836.tistory.com/165

 

spring security 파헤치기 (구조, 인증과정, 설정, 핸들러 및 암호화 예제, @Secured, @AuthenticationPrincipal,

참조문서 https://docs.spring.io/spring-security/site/docs/4.2.7.RELEASE/reference/htmlsingle/#getting-started http://springsource.tistory.com/80 https://okky.kr/article/382738 1. 스프링 시큐리티란?..

sjh836.tistory.com

 

  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 카카오스토리 공유하기