이전 포스팅(https://velog.io/@max9106/Spring-Boot-%EC%8A%A4%ED%94%84%EB%A7%81-%EC%8B%9C%ED%81%90%EB%A6%AC%ED%8B%B0)에서 말한 것 처럼, 기본 스프링 시큐리티는 모든 요청에 대해 인증을 요구한다.
이번에는 index페이지와 hello페이지는 누구나 볼 수 있고, my page만 인증을 거치도록 커스터마이징 해보려고한다.
config라는 디렉토리를 추가하고, 시큐리티 설정을 하는 클래스를 만들어준다.
이렇게해주면, 스프링부트가 지원해주는 SecurityAutoConfiguration(임의의 user를 생성해주는 등의 역할을 하는 것)은 사용되지 않는다.(WebSecurityConfigurerAdapter를 구현했기 때문)
그 후, configure(HttpSecurity http) 메서드를 구현해주면 된다.
실행시켜주면, my page에만 로그인을 요구함을 볼 수 있다.
실제로 애플리케이션을 사용할 때, 스프링 부트가 만들어준(자동설정) 유저정보를 사용하는 경우는 거의 없다. 보통 각자 유저 정보를 관리하는데 그 부분을 커스터마이징 해보겠다.
account라는 패키지를 만들어주고, Account클래스를 만들어준다.(id, username, passoword 를 가지고 있다.)
데이터를 관리하는 AccountRepository를 만들어준다.
AccountRepository를 만들기 위해 jpa 의존성을 추가해준다.
또 DB설정을 간편하게 해보기 위해 h2 의존성도 추가해준다.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
AccountService도 만들어준다.
서비스에서는 Repository를 받아와서 새로운 Account를 저장하는 메서드와, 메서드를 만들어준다.
account를 만든 쪽에서 받아서 사용할 수 있도록 createAccount를 만들어준다.
유저 관리를 커스터마이징 하기 위해 UserDetailsService를 implements한다.(보통 유저 정보를 관리하는 서비스 계층에서 UserDetailsService를 구현한다.)
이렇게 UserDetailsService 타입의 Bean이 등록되어 있으면 이제 스프링 부트가 만들어 준 유저가 더 이상 생기지 않는다.
login 폼에서 입력한 username이 loadUserByUsername으로 들어온다. 그 username에 해당하는 실제 유저 정보(password포함)를 확인한다. password가 입력한 값과 일치하면 로그인시키고, 아니면 예외를 던진다.
loadUserByUsername을 구현해보겠다.
findByUsername이라는 것을 만들어서 username을 넘겨준다.
Repository에도 findByUsername을 만들어준다.
username을 통해 정보를 가져올 수 있으면 Account 객체에 담고, 없으면 UsernameNotFoundException 예외를 던진다.
리턴 값은 UserDetails라는 인터페이스 구현체이다.
UserDetails 인터페이스는 서비스마다 제각각 구현되어있는 유저정보(이 예제에서는 Account)들의 인터페이스이다.
스프링 시큐리티는 UserDetails의 구현체로 User라는 것을 지원해준다.
따라서 User(username, password, 권한)을 리턴해주면 된다.
이렇게 리턴해주면 UserDetails로 변환하여 리턴된다.
이제 ApplicationRunner를 통해 테스트할 유저를 만들어주겠다.
이렇게 하고 실행 후, mypage에서 유저정보를 입력하면 로그인이 되어야하는데, password encoder때문에 로그인이 되지 않는다. 왜냐하면 encoding을 하지 않았기 때문에 시큐리티 보안정책에 위배된다.
이를 해결하기 위해 여러가지 password encoder 방법들이 존재한다.
시큐리티 설정 파일에 password encoder를 noop으로 등록하는 방법이다. 이렇게 하면 noop 방식으로 decoding 하기 때문에 encoding이 필요없게 된다.
이 방법은 예시로 해보는 것이고, 절대 사용하면 안된다.
(SecurityConfig에 Bean으로 등록했다고 SecurityConfig에서만 사용가능한 것이 아니고, 애플리케이션 전반적으로 모두 사용가능하다)
이렇게 하고 실행 후, 만들어준 유저로 로그인 해보면 정상작동한다.
그러나 이 방법은 절대 사용하면 안된다.
스프링 시큐리티에서 권장하는 encoder는 PasswordEncoderFactories.createDelegatingPasswordEncoder()
이다.
이것을 password encoder로 사용하면,
유저 정보를 저장하기 전에, 서비스에서 PasswordEncoder를 주입받아, password를 encoding하여 저장해야한다.
애플리케이션을 실행하여, mypage에서 로그인 해보면 정상작동함을 볼 수 있다.
encoding 된 것도 확인가능하다.
(실제로는 password를 로그에 찍어보면 안된다)