- 웹개발의 막보스 스프링 시큐리티는 매우 지능적이며 간단한 선언으로 작동하므로
대량의 코드를 손쉽게 절약 - 몇십 줄만으로 대형 서비스사와 비슷한 수준의 보안을 유지
- ACEGI라는 이름으로 시작하고 10년째 서비스
보안
- Authentication 후 Authorization
- 인증 후 권한 부여, 비슷한 단어가 아니니 구분 확실히
- 종류
- Credential(자격)기반 인증
- 웹의 대부분의 인증방식
- 대개 사용자명과 비밀번호를 입력받고 저장된 정보와 일치하는지 확인
- 스프링 시큐리티가 구현할 인증
- Two facotor Authentication
- 한번에 2가지 방식의 인증
- 별거 아니지만 하나의 인증이 추가 되면 프로그램 변화할 부분이 많음
- physical Authentication
- 지문인식, 키삽입
- 웹을 벗어낫지만 가장 효과적인 보안 수단
- Credential(자격)기반 인증
스프링 시큐리티
- 지금꺄지 웹 개발자가 직접 구현했던 아이디/비번 입력,로그인, 사욘ㅇ자 인증후
- 각각의 기능에 대한 Authorization에 대한 작업을 구현한 보안 프레임워크
- 프로그램 외 리소스 접근 제어 가능
스프링 부트의 스프링 시큐리티
- spring-boot-starter-security만 추가해주면 자동설정 적용
- 모든 request에 대해 인증이 걸리게 된다
- 또한 Basic Authentication, form Authentication 둘다 적용된다
- 401 응답 헤더를 보면 WWW-Authenticate:”Basic realm=”Realm””를 확인 할 수있다
- 브라우저가 이 응답을 받으면 Basic Authentication form을 띄우게 된다
- Basic Authentication의 응답은 요청의 Accept 헤더에 따라 달라진다
- 피들러나 Junit Test시 Accept헤더에 아무것도 없으므로 401 응답
헤더를 보면 WWW-Authenticate:”Basic realm=”Realm””를 확인 할 수있다 - 브라우저의 경우 accept가 html이 있으므로 302응답과 login에 대한url을 제공함
으로써 리다이렉션해서 login창이 뜨도록 한다 - login으로 갈때 콘솔에
Using generated security password: 0abed2f9-e76d-4282-9645-e3af074a84dd
를 user의 패스워드로 사용할 수 있다 - 이것을 입력해서 인증받으면 예제들의 요청에 접근이 가능하다
- 피들러나 Junit Test시 Accept헤더에 아무것도 없으므로 401 응답
스프링 부트 자동설정-SecurityAutoConfiguration
- SecurityAutoConfiguration
- 가장 처음 보이는 것은 @Bean밑의 DefaultAuthenticationEventPublisher 등록
- 비번틀림,계정없음등등의 대한 많은 이벤트를 발생하고 있음
- 개발자는 그 이벤트에 대한 핸들러를 등록해서 여러가지 일을 할 수 있음
- 스프링 부트가 아닌 일반 스프링 사용할때는 직접 이것을 등록해서 동일 효과
- 가장 처음 보이는 것은 @Bean밑의 DefaultAuthenticationEventPublisher 등록
- SpringBootWebSecurityConfiguration
- WebSecurityConfigurerAdapter 상속받은 빈(empty) 클래스 설정
- WebSecurityConfigurerAdapter
- 스프링 부트가 아닌 스프링 시큐리티에서 제공하는 기본 설정 클래스
- 스프링 사용자의 경우 이 설정 클래스를 기본적으로 상속받아서 만듬
- 스프링 부트는 이 기본설정을 그대로 사용하겠다는 뜻이기도 하다
- WebSecurityConfigurerAdapter에서 시큐리티를 반환하는 getHttp()를 보면
기본 설정을 볼 수 있다
- WebSecurityConfigurerAdapter
- WebSecurityConfigurerAdapter 상속받은 빈(empty) 클래스 설정
1 | http = new HttpSecurity(objectPostProcessor, authenticationBuilder, |
- 설정이 없을떄 기본적으로 어떤 설정이 되어있는지 확인 가능하다
1 | protected void configure(HttpSecurity http) throws Exception { |
- 핵심: 스프링 시큐리티가 제공하며 스프링 부트가 아무 일 하지 않아서 그대로 등록됨
- UserDetailsServiceAutoConfiguration
- 미설정시 login시에 자동으로 패스워드 지원했던 그 부분
- 스프링 시큐리티의 기본적인 랜덤 인메모리 유저를 만들어서 제공
- 다른 설정클래스가 없을때만 자동설정이 지원됨
- 즉 개발하면서 UserDetailsService를 구현하면 이 자동설정은 반영되지 않음
테스트 통과하기위해서는?
- 의존성 추가
1
2
3
4
5
6<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<version>${spring-security.version}</version>
<scope>test</scope>
</dependency> - Test에 @WithMockUser 추가
- 테스트 클래스 적용시 해당 클래스의 모든 테스트에 적용
- 테스트 메소드 적용시 해당 테스트에만 적용
- 가짜 유저 인증 정보를 추가해서 테스트를 돌려주게 되서 테스트가 성공하게 됨
커스터마이징
다음 3가지를 커스터마이징 할 것이다
- WebSecurityConfigurerAdapter
- UserDetailsService
- PasswordEncoder
WebSecurityConfigurerAdapter
- 직접 구현하면 SpringBootWebSecurityConfiguration 자동설정이 작동안된다-> anyRequest앞에 /, /hello에 대해서 제한을 풀었음
WebSecurityConfigurerAdapter 구현 직접 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
//원래 값
/*
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin().and()
.httpBasic();
*/
http.authorizeRequests()
.antMatchers("/","/hello").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.and()
.httpBasic();
}
}
- 직접 구현하면 SpringBootWebSecurityConfiguration 자동설정이 작동안된다
UserDetailsService
- Account를 관리하는 Service 계층에서 이 인터페이스를 구현하도록 하면 된다
- 서비스와 별개로 다른 클래스에서 이 인터페이스를 구현하게 하여도 상관은 없다
- 즁요한것은 UserDetailsService 타입의 빈이 등록이 되어있어야 한다는 것이다
- 그렇지 않으면 위에서 보았던 UserDetailsServiceAutoConfiguration 자동설정이
적용되어버림
1 |
|
- 설명
- Account를 관리하는 서비스 계층
- AccountRepository는 JpaRepository<Account,Long>를 상속받은 인터페이스
- UserDetailsService 인터페이스 구현을 위해 loadUserByUsername 구현
- loadUserByUsername()
- 로그인할때 입력한 이름이 username 아규먼트로 들어온다
- findByUsername로 유저 정보를 찾아온다
- 리턴값인 UserDetails
- 서비스마다 제각각으로 구현되어있는 유저 정보를 공통적으로 처리하기 위한
인터페이스 - 스프링이 구현체로 User 제공
- 서비스마다 제각각으로 구현되어있는 유저 정보를 공통적으로 처리하기 위한
- 기본 계정은 ApplicationRunner 혹은 PostConstructure를 이용해서 생성
- 테스트시 실패 -> 패스워드 인코더가 없습니다
PasswordEncoder
패스워드 인코딩은 반드시 필요
다양한 패스워드 인코딩 방식이 존재
시큐리티 5.0 이전에는 기본 PasswordEncoder는 NoOpPasswordEncoder로 인코딩 없는
패스워드가 사용되었다그렇다고 바로 BCryptPasswordEncoder같은 것을 default로 하면 문제가 생긴다
- 쉽게 마이그레이션이 힘든 많은 구 프로그램들이 예전 패스워드 방식 사용
- 암호저장에 대한 모범사례가 계속 변경됨
- 스프링 시큐리티가 자주 변경될 수 없는 커스텀 프레임워크를 사용
DelegatingPasswordEncoder
- 현재 암호저장 권장사항을 이용해서 암호가 인코딩 가능한지 확인
- 기본방식과 현대 방식의 암호들의 검증 가능
- 향후 인코딩 업그레이드가 가능
- 생성방법 : 쉬운 방법과 커스텀하게 만드는 방법이 있음
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16//쉬운 방법
PasswordEncoder passwordEncoder =
PasswordEncoderFactories.createDelegatingPasswordEncoder();
//커스텀 방법
String idForEncode = "bcrypt";
Map encoders = new HashMap<>();
encoders.put(idForEncode, new BCryptPasswordEncoder());
encoders.put("noop", NoOpPasswordEncoder.getInstance());
encoders.put("pbkdf2", new Pbkdf2PasswordEncoder());
encoders.put("scrypt", new SCryptPasswordEncoder());
encoders.put("sha256", new StandardPasswordEncoder());
PasswordEncoder passwordEncoder =
new DelegatingPasswordEncoder(idForEncode, encoders);패스워드 포맷 형태 1
2
3
4
5{bcrypt}$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG
{noop}password
{pbkdf2}5d923b44a6d129f3ddf3e3c8d29412723dcbde72445e8ef6bf3b508fbf17fa4ed4d6b99ca763d8dc
{scrypt}$e0801$8bWJaSu2IKSn9Z9kM+TPXfOc/9bdYSrN1oD9qfVThWEwdRTnO7re7Ei+fUZRJ68k9lTyuTeUp4of4g24hHnazw==$OAOec05+bXxvuu/1qZ6NUR+xQYvYv7BeL1QxwRpY5Pc=
{sha256}97cde38028ad898ebc02e690819fa220e88c62e0699403e94fff291cfffaf8410849f27605abcbc0
절대 하면 안되는 방법
- WebSecurityConfig에 @Bean을 붙여 PasswordEncoder를 리턴값을 받는 메소드를
생성후 NoOpPasswordEncoder.getInstance()설정 - 현재 등록된 패스워드 인코더를 NoOpPasswordEncoder로 교체해버리는 방법
- 쓰지말자
- WebSecurityConfig에 @Bean을 붙여 PasswordEncoder를 리턴값을 받는 메소드를
이제 PasswordEncoder가 빈으로 등록이 되었으니 AccountService에서 @Autowired로
받아서 사용가능하다
내부적으로 처리할때는 PasswordEncoder의 encode(문자열)를 사용하면 된다
정말 빙산의 일각이고 앞으로 더 공부해보자
Related POST
- [Spring Boot] 13. Spring REST Client
- [Spring Boot] 12. Spring Security
- [Spring Boot] 11. Spring Data
- [Spring Boot] 10. 스프링 웹 MVC-2: Spring HATOAS, CORS
- [Spring Boot] 9. 스프링 웹 MVC-1:
- [Spring Boot] 8. Spring-Boot-Devtools
- [Spring Boot] 7. 테스트(Testing)
- [Spring Boot] 6. 로깅(Logging)
- [Spring Boot] 5. Profiles
- [Spring Boot] 4. 스프링부트 외부설정
- [Spring Boot] 3. 스프링부트 핵심기능
- [Spring Boot] 2. 스프링부트 이해
- [Spring Boot] 1. 스프링부트 시작