HttpSecurity로 대부분 구현한다고 생각하면 된다. 참고로 현재는 WebSecurityConfigurerAdapter는 사용을 안한다.
먼저 config 패키지에 SecurityConfig라는 시큐리티 설정 파일을 만들어 주고 필요한 @bean들을 추가해 사용할 수 있다. --> 사진을 찾다보니 현재 WebSecurityConfigurerAdapter는 시큐리티3부터 사용을 안하지만 HttpSecurity에 대한 설명이 나와있어서 사용했다. 현재 WebSecurityConfigurerAdapter 를 상속하지 않는다!
이제는 @Bean 으로 SpringSecurityFilterChain 을 구현한다.
config 클래스에 @EnableWebSecurity 어노테이션을 달아서 시큐리티 설정을 해준다.
@Configuration
@EnableWebSecurity
public class SecurityConfig{
// 패스워드 암호화 관련 메소드
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
// 특정 HTTP 요청에 대한 웹 기반 보안 구성
// 시큐리티 대부분의 설정을 담당하는 메소드
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf(AbstractHttpConfigurer::disable)
.httpBasic(AbstractHttpConfigurer::disable)
.authorizeHttpRequests((authorize) -> authorize
.requestMatchers("/signup", "/", "/login").permitAll()
.anyRequest().authenticated()
)
// Form 로그인을 활용하는경우 (JWT에는 필요없음)
.formLogin(form -> form
.loginPage("/loginform")
.loginProcessingUrl("/login")
.defaultSuccessUrl("/")
.permitAll()
)
.logout((logout) -> logout
.logoutUrl("/logout")
.logoutSuccessUrl("/")
.invalidateHttpSession(true)
)
.sessionManagement(sessionManagement -> sessionManagement
.maximumSessions(1)
.maxSessionsPreventsLogin(true)
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
)
);
return http.build();
}
// 이외에도 등록해서 사용하면 된다..
}
코드설명
filterChain() : 특정 Http 요청에 대해 웹 기반 보안 구성. 인증/인가 및 로그아웃을 설정한다.
.csrf(Cross site Request forgery) : 공격자가 인증된 브라우저에 저장된 쿠키의 세션 정보를 활용하여 웹 서버에 사용자가 의도하지 않은 요청을 전달하는 것. 즉, 정상적인 사용자가 의도치 않은 위조요청을 보내는 것을 의미한다.
REST API를 이용한 개발을 진행 할 예정일 때, Rest Api 환경에서는 Session 기반 인증과 다르기 때문에 서버에 인증 정보를 보관하지 않고, 권한 요청시 필요한 인증정보(OAuth2, Jwt토큰 등)요청을 포함하기 때문에 굳이 불필요한 csrf 보안을 활성화할 필요가 없다.
따라서 csrf는 disable 처리
.HttpBasic()
HttpBasic() : Http basic Auth 기반으로 로그인 인증창이 뜬다.
.authorizeHttpRequests() : 인증, 인가가 필요한 URL 지정
anyRequest() : requestMatchers에서 지정된 URL 외의 요청에 대한 설정
authenticated() : 해당 URL에 진입하기 위해서는 인증이 필요함
requestMatchers("Url").permitAll() : requestMatchers에서 지정된 url은 인증, 인가 없이도 접근 허용
Url에 /**/ 와 같이 ** 사용 : ** 위치에 어떤 값이 들어와도 적용 (와일드 카드)
hasAuthority() : 해당 URL에 진입하기 위해서 Authorization(인가, 예를 들면 ADMIN만 진입 가능)이 필요함
.hasAuthority(UserRole.ADMIN.name()) 와 같이 사용 가능
formLogin() : Form Login 방식 적용
loginPage() : 로그인 페이지 URL
defaultSuccessURL() : 로그인 성공시 이동할 URL
failureURL() : 로그인 실패시 이동할 URL
logout() : 로그아웃에 대한 정보
invalidateHttpSession() : 로그아웃 이후 전체 세션 삭제 여부
sessionManagement() : 세션 생성 및 사용여부에 대한 정책 설정
SessionCreationPolicy() : 정책을 설정
SessionCreationPolicy.Stateless : 4가지 정책 중 하나로, 스프링 시큐리티가 생성하지 않고 존재해도 사용하지 않는다. (JWT와 같이 세션을 사용하지 않는 경우에 사용)
BCryptPasswordEncoder
BCrype 인코딩을 통하여 비밀번호에 대한 암호화를 수행한다.
password를 암호화해줌
Spring Security에서 비밀번호를 안전하게 저장할 수 있도록 비밀번호의 단방향 암호화를 지원한다. -> PasswordEncoder 인터페이스와 구현체들
encode() : 비밀번호를 암호화(단방향)
matches() : 암호화된 비밀번호와 암호화되지 않은 비밀번호가 일치하는지 비교
upgradeEncoding() : 인코딩된 암호화를 다시 한번 인코딩 할 때 사용 (true일 경우 다시 인코딩, default=false)
PasswordEncoder가 제공하는 구현 클래스
StandardPasswordEncoder : SHA-256을 이용해 암호를 해시한다. (강도가 약한 해싱 알고리즘이기 때문에 지금은 많이 사용되지 않는다.)
BCryptPasswordEncoder : bcrypt 강력 해싱 함수로 암호를 인코딩
NoOpPasswordEncoder : 암호를 인코딩하지 않고 일반 텍스트로 유지(테스트 용도로만 사용.)
SCryptPasswordEncoder : scrypt 해싱 함수로 암호를 인코딩한다.
@Bean // 패스워드 암호화 관련 메소드
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
현재 사용되는 알고리즘에서 취약성이 발견되어 다른 인코딩 알고리즘으로 변경하고자 할 때 대응하기 좋은 방법은 DelegatingPasswordEncoder을 사용하는 것
@Bean // DelegatingPasswordEncoder: 여러 인코딩 알고리즘을 사용할 수 있게 해주는 기능
public static PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
기타 참고용
Configure 작성 문법 바뀐 부분
스프링 3.0 이상의 버전부터는 스프링 시큐리티 버전도 바뀌어서 기존의 Configuration과는 다르게 작성해야 한다. WebSecurity, HttpSecurity 모두 큰 변화를 맞이 했는데, 그중 하나가 lambdas 형식의 작성법이다.
http
.authorizeHttpRequests(authorizeRequest -> authorizeRequest
// 해당 경로는 모든 권한을 허용한다.
.requestMatchers(HttpMethod.GET, "/login**", "/web-resources/**", "/actuator/**").permitAll()
// 해당 경로는 어드민 권한이 있어야한다.
.requestMatchers(HttpMethod.GET, "/admin/**").hasAnyRole("ADMIN")
// 해당 경로는 유저 권한이 있어야 한다.
.requestMatchers(HttpMethod.GET, "/order/**").hasAnyRole("USER")
// 나머지는 모두 권한이 필요하다.
.anyRequest().authenticated()
requestMatchers
특정 리소스에 대해서 권한을 설정한다.
permitAll
리소스의 접근을 인증절차 없이 허용한다.
authenticated
리소스의 접근을 인증절차를 통해 허용한다.
hasAnyRole
해당 권한을 가진 사용자만 접근을 허용한다.
anyRequest
모든 리소스를 의미하며, anyMatcher로 설정하지 않은 리소스를 말한다.
HttpSecurity - 로그인처리 설정
로그인 FORM 페이지를 이용하여 로그인하는 방식을 사용하려고 할때 여러가지 설정을 할 수 있다.
// Form 로그인을 활용하는경우 (JWT에는 필요없음)
// .formLogin(Customizer.withDefaults()); // Security가 제공하는 로그인 방식 사용
.formLogin(formLogin -> formLogin
.loginPage("/login")
.loginProcessingUrl("/loginProc")
.usernameParameter("userId")
.passwordParameter("userPw")
.permitAll())
JwtAuthenticationFilter 사용
HttpSecurity - 커스텀 필드 등록 ⭐
커스텀 필터를 생성해서 등록할 수 있다!
.addFilterBefore(jwtAuthenticationFilter,
UsernamePasswordAuthenticationFilter.class)
// UsernamePasswordAuthenticationFilter가 기존 시큐리티 세션 방식의 로그인 필터이기 때문에
// UsernamePasswordAuthenticationFilter 앞에 커스텀한 필터 체인을 넣어준다.