✔ Spring Security의
기본 id는 user
비밀번호는 콘솔에 적혀있다.
✔ localhost:8080/login은 아무설정도 안한상태에서는 springSecurity에서 가로채서 spring security login으로 연결된다.
이때 user와 콘솔에 나오는 비밀번호를 입력하지 않으면 url로 접근 할 수 없다.
SecurityFilterChain을 넣게 되면 Spring security의 로그인은 더이상 나오지 않게 된다.
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.authorizeRequests()
.antMatchers("/user/**").authenticated()
.antMatchers("/manager/**").access("hasRole('ROLE_ADMIN')or hasRole('ROLE_MANAGER')")
.antMatchers("/admin/**").access("hasRole('ROLE_ADMIN')")
.anyRequest().permitAll();
.and()
.formLogin()
.loginPage("/login");
return http.build();
}
}
anyMatchers와 .anyRequest로 접근 조건을 걸고
.antMatchers("/user/**").authenticated()
.antMatchers("/manager/**").access("hasRole('ROLE_ADMIN')or hasRole('ROLE_MANAGER')")
.antMatchers("/admin/**").access("hasRole('ROLE_ADMIN')")
.anyRequest().permitAll();
로그인이 필요할경우 /login으로 이동하고 해당페이지가 로그인 기능을 수행한다.(.loginPage가 없다면 403에러가 난다.)
로그인이 필요한 경우란 unauthenticated request인 경우를 만한다 이때 FilterSecurityInterceptor가 unauthenticated request가 AccessDeniedException에 의해 거부당한것을 신호를 보내게된다.
이때 ExceptionTranslationFilter가 로그인을 하게 하기위해 로그인페이지로 redirect시킨다.
.and()
.formLogin()
.loginPage("/login");
public SecurityFilterChain filterChain(HttpSecurity http) {
http
.formLogin(form -> form
.loginPage("/login")
.permitAll()
);
// ...
}
이후 로그인 페이지로 이동시킨후 로그인을 submit하고 나서의 과정은 spring.io에서 자세하게 확인 할 수 있다.
https://docs.spring.io/spring-security/reference/servlet/authentication/passwords/form.html
✔@EnableWebSecurity 어노테이션을 등록하면 스프링 시큐리티 필터(내가 작성한 SecurityConfig)가 스프링 필터체인에 등록이 된다.
@Configuration
@EnableWebSecurity
public class SecurityConfig {
}
✔403 error란?
403 Forbidden
https://developer.mozilla.org/ko/docs/Web/HTTP/Status/403
✔ WebSecurityConfigurerAdapter 지원 종료되어 최신 스프링 부트에서는 사용 불가능하다.
@Configuration // IoC 빈(bean)을 등록
@EnableWebSecurity // 필터 체인 관리 시작 어노테이션
public class SecurityConfig extends WebSecurityConfigurerAdapter{
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.authorizeRequests()
.antMatchers("/user/**").authenticated()//어떠한 권한이던 권한
//.antMatchers("/admin/**").access("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER')")
//.antMatchers("/admin/**").access("hasRole('ROLE_ADMIN') and hasRole('ROLE_USER')")
.antMatchers("/admin/**").access("hasRole('ROLE_ADMIN')")
.anyRequest().permitAll()
.and()
.formLogin()
.loginPage("/login")
.loginProcessingUrl("/loginProc")
.defaultSuccessUrl("/")
.and()
.oauth2Login()
.loginPage("/login")
.userInfoEndpoint()
.userService(principalOauth2UserService);
}
}
아래와 같은 방식으로 변경해줘야한다.
그리 어렵지 않다. Bean으로 등록해주고 SecurityFilterChain을 return해주면 된다. 의존성 주입할때 SecurityFilter를 넣어준다는 개념으로 생각하면 될듯 싶다.
@Configuration
@EnableWebSecurity
public class AuthorizeUrlsSecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeRequests((authorizeRequests) ->
authorizeRequests
.antMatchers("/**").hasRole("USER")
)
.formLogin(withDefaults());
return http.build();
}
}
✔ http.authorizeRequest의 의미
Spring Security를 사용하면 모든 Request를 Spring Security가 가로채고 접근을 제한하게 된다.
따라서 접근을 할 수 있게 해줄 수 있는 설정이 있는데 그게 바로 http.authorizeRequest의 기능이다.
Allows restricting access based upon the HttpServletRequest using RequestMatcher implementations (i.e. via URL patterns).
Example Configurations
The most basic example is to configure all URLs to require the role "ROLE_USER". The configuration below requires authentication to every URL and will grant access to both the user "admin" and "user".
@Configuration
@EnableWebSecurity
public class AuthorizeUrlsSecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeRequests((authorizeRequests) ->
authorizeRequests
.antMatchers("/**").hasRole("USER")
)
.formLogin(withDefaults());
return http.build();
}
@Bean
public UserDetailsService userDetailsService() {
UserDetails user = User.withDefaultPasswordEncoder()
.username("user")
.password("password")
.roles("USER")
.build();
UserDetails admin = User.withDefaultPasswordEncoder()
.username("admin")
.password("password")
.roles("ADMIN", "USER")
.build();
return new InMemoryUserDetailsManager(user, admin);
}
}
authorizeRequest().anyMathcer("/user/**").authenticated()라면
"/user/"와 하위의 url에 대하여 어떠한 것이든 권한을 가진 경우에만 접근할 수 있게 하는것이다.
->어떠한 것이든 권한을 갖다 ->관리자, 일반사용자 어떤 사용자이던 특정한 권한을 갖게 되어있다.
->즉 로그인이 되어 특정한 권한을 확인이 되는 경우 -> 로그인이 된 경우를 의미한다.
.antMatchers("/admin/**").access("hasRole('ROLE_ADMIN')")
"/admin/"와 하위의 url에 대하여 ADMIN 권한을 가진 사용자만 접근할 수 있다.
✔ . anyRequest().permitAll(); 어떤 url이던 접근을 허용한다.
@Bean
public SecurityFilterChain configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.authorizeRequests()
.antMatchers("/user/**").authenticated()
.antMatchers("/manager/**").access("hasRole('ROLE_ADMIN')or hasRole('ROLE_MANAGER')")
.antMatchers("/admin/**").access("hasRole('ROLE_ADMIN')")
.anyRequest().permitAll();
return http.build();
}
✔회원가입을 할때 비밀번호를 암호화 해야한다. 왜냐하면 시큐리티로 로그인 할때 비밀번호가 암호화되어있지 않으면 로그인 할 수 없다.
@PostMapping("/join")
public @ResponseBody String join(User user) {
System.out.println(user);
user.setRole("ROLE_USER");
String rawPassword = user.getPassword();
String encPassword = bCryptPasswordEncoder.encode(rawPassword);
user.setPassword(encPassword);
userRepository.save(user);//회원가입 잘됨. 하지만 시큐리티로 로그인 할 수 없음 ->암호화가 되어있지않기때문에
return "redirect:/loginForm";
}
-> 그 이유는 스프링에서 로그인 하기위해서는 비밀번호를 인코딩하며 사용해야한다. 여기서 passwordEncoderInterface가 사용된다 하지만 password를 암호화 하지않은 채로 저장하여 사용하기 위해서는 NoOpPasswordEncoder를 사용하여 raw파일 그대로 사용할 수 있었는데 SprinSecurity5 부터는 이 NoOpsPasswordEncdoer를 지원하지않는다.
따라서 암호화 하는 인코더를 사용하여 저장해야한다.
https://www.baeldung.com/spring-security-5-default-password-encoder