똑같은 삽질은 2번 하지 말자

Spring Boot 개념다지기 No.18(Starter-Security) 본문

Spring/Spring Boot

Spring Boot 개념다지기 No.18(Starter-Security)

곽빵 2020. 5. 5. 15:58

페이지가 두개있다. hello.html , my.html

my페이지는 로그인 한 사용자에게만 접근 가능하게 하고 싶다.

 

Spring Security를 이용해서 해보자.

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

 

추가해서 테스트를 돌려보면 

@RunWith(SpringRunner.class)
@WebMvcTest(HomeController.class)
public class HomeControllerTest {
	@Autowired
	MockMvc mockMvc;
	
	@Test
	public void hello() throws Exception{
		mockMvc.perform(get("/hello"))
				.andDo(print())
				.andExpect(status().isOk())
				.andExpect(view().name("hello"));
	}
	@Test
	public void my() throws Exception{
		mockMvc.perform(get("/my"))
				.andDo(print())
				.andExpect(status().isOk())
				.andExpect(view().name("my"));
	}

}

이러한 에러가 나온다.

시큐리티를 추가하면 모든요청에 대한 스프링 시큐리티의 인증이 필요

 

*Accept Header에 따라 Basic Authenticate 가 달라진다.

위 처럼 그리고 앱을 작동시켜보면 아무런 인증정보가 없으므로 Form 인증(Spring Security에서 기본 제공)

하는 페이지로 이동하게 된다.

스프링 부트 시큐리티 자동 설정(거의 쓸일 없음ㅎ)

  • SecurityAutoConfiguration 
  • UserDetailsServiceAutoConfiguration
  • spring-boot-starter-security
    • 스프링 시큐리티 5.* 의존성 추가
  • 모든 요청에 인증이 필요함.
  • 기본 사용자 생성
    • Username: user
    • Password: 애플리케이션을 실행할 때 마다 랜덤 값 생성 (콘솔에 출력 됨.)
    • spring.security.user.name
    • spring.security.user.password
  • 인증 관련 각종 이벤트 발생
    • DefaultAuthenticationEventPublisher 빈 등록
    • 다양한 인증 에러 핸들러 등록 가능

SecurityAutoConfiguration 에서 요청을 전부 인증요구하는 코드를 찾아보았다.

이렇게 자동설정이 되어있는걸 커스터마이징 해보자.

 

위의 사진의 자동설정을 커스터마이징 하면

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter{ // 자동설정은 자동 무시
	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http.authorizeRequests()
				.antMatchers("/","/hello").permitAll() // / 와 /hello 는 그냥 허용
				.anyRequest().authenticated() // 나머지 모든요청은 인증필요
				.and()
			.formLogin() // 그리고 폼로그인 사용(Accept헤더에 html가 있음)
				.and()
			.httpBasic(); // Basic Authenticated 사용하겠다.
	}
    @Bean
	public PasswordEncoder passwordEncoder() {
		// 스프링부트가 권장하는 인코더
		// 인코드를 안하면 인증을 인정해주지 않는다.
		return PasswordEncoderFactories.createDelegatingPasswordEncoder();
	}
}
@Service // UserDetailsService 타입의 빈이 등록되어있어야 자동등록이 무시된다.
	     // 보통 로그인 처리 Service단에 UserDetailsService가 구현되도록 해야한다.
public class AccountService implements UserDetailsService{
	@Autowired
	private PasswordEncoder passwordEncoder;
	
	@Override
	// UserDetails 그 어플의 유저정보들을 담고있는 인터페이스라고 생각하면된다.
	// UserDetails에 담겨있는 username , password가 폼에서 입력한 친구와 같으면 로그인된다.
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		Account user = new Account();
		String password = "1234";
		user.setUsername("Gwak");
		user.setPassword(passwordEncoder.encode(password));// 인코드해서 패스워드 저장
		if(!username.equals("Gwak")) // 여기는 해당하는 ID가 없으면 예외날리는것
			new UsernameNotFoundException(username);
		// User는 스프링에서 기본 제공
		return new User(user.getUsername(),user.getPassword(),authorities());
	}

	private Collection<? extends GrantedAuthority> authorities() {
		return Arrays.asList(new SimpleGrantedAuthority("ROLE_USER")); // 권한부여
	}
}

반환되는 UserDetails와 입력한 정보를 비교한다.(내가 등록한 PasswordEncoder를 참고해서)

 

정보가 틀리면

이런식으로 화면이 찍힌다.

 

이건 정말 기본적인 스프링시큐리티이며 정말 시작에 불과하다.

configure 오기전 처리 configure에서의 CSRF처리 인증방식을 oauth사용  Form변경등등

 

참고 문헌

UserDetailsServie 구현

PasswordEncoder 설정 및 사용

Comments