Spring Security 환경설정
Spring Security 의존성 추가
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6'
testImplementation 'org.springframework.security:spring-security-test'
• 스프링 시큐리티를 사용하기 위한 스타터
• 타임리프에서 스프링 시큐리티를 사용하기 위한 의존성
• 스프링 시큐리티를 테스트 하기 위한 의존성
spring:
security:
user:
name: user
password: 1111
SecurityConfig
Spring Security의 환경설정을 구성하기 위한 클래스다!
- 먼저 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와 같이 세션을 사용하지 않는 경우에 사용)
- .csrf(Cross site Request forgery) : 공격자가 인증된 브라우저에 저장된 쿠키의 세션 정보를 활용하여 웹 서버에 사용자가 의도하지 않은 요청을 전달하는 것. 즉, 정상적인 사용자가 의도치 않은 위조요청을 보내는 것을 의미한다.
BCryptPasswordEncoder
BCrype 인코딩을 통하여 비밀번호에 대한 암호화를 수행한다.
- 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 형식의 작성법이다.
- 람다식을 파라미터로 전달하여 아래와 같이 사용한다.
.formLogin(formLogin -> formLogin
.loginPage("/login")
.defaultSuccessUrl("/home"))
HttpSecurity
스프링시큐리티의 각종 설정은 HttpSecurity로 대부분 하게 된다!
Spring Boot 3.1(Spring 6.1) Security Config: 'csrf()' is deprecated and marked for removal
- 스프링 부트 버전이 올라가면서 작성방식에 차이가 생김
Spring boot 3.0.6, Spring security 6, jwt적용 및 인증, 예외 처리
- 버전이 올라가면서 동작방식이 달라짐
HttpSecurity - 리소스(URL) 접근 권한 설정
특정 리소스의 접근 허용 또는 특정 권한을 가진 사용자만 접근을 가능하게 할 수 있다.
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 앞에 커스텀한 필터 체인을 넣어준다.
- addFilterBefore
- 지정된 필터 앞에 커스텀 필터를 추가한다.
- addFilterAfter
- 지정된 필터 뒤에 커스텀 필터를 추가한다.
- addFilterAt
- 지정된 필터의 순서에 커스텀 필터가 추가된다.
JwtAuthenticationFilter
- JwtAuthenticationFilter.java
- jwt 방식으로 로그인을 진행할 것이기 때문에 커스텀한 필터이다.
@Order(0)
@RequiredArgsConstructor
@Component
@Slf4j
public class JwtAuthenticationFilter extends OncePerRequestFilter {
private final TokenProvider tokenProvider;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String token = parseBearerToken(request);
// 토큰값이 유요하다면 검증을 시작한다.
if (token != null && tokenProvider.validToken(token)) {
// 토큰 검증
Authentication authentication = tokenProvider.getAuthentication(token);
// SecurityContextHolder => 인증정보를 담는다.
SecurityContextHolder.getContext().setAuthentication(authentication);
log.info("Security Context에 {} 인증 정보를 저장했다", authentication.getPrincipal());
} else {
log.info("유효한 JWT 토큰이 없습니다, uri: {}", request.getRequestURI());
}
filterChain.doFilter(request, response);
}
/**
* Authorization Bearer 제거(공백포함 7글자)
* @param request 요청 request
* @return token (없는경우 null)
*/
private String parseBearerToken(HttpServletRequest request) {
return Optional.ofNullable(request.getHeader(HttpHeaders.AUTHORIZATION))
.filter(token -> token.length() >= 7 && token.substring(0, 7).equalsIgnoreCase("Bearer "))
.map(token -> token.substring(7))
.orElse(null);
}
}
JwtAuthenticationFilter에 대해서는 이전 블로그에 자세하게 적어놓았다.
Spring Security + JWT (RefreshToken, AccessToken)를 사용한 로그인, 로그아웃 구현 - 4편 :: 미정 (tistory.com)
Spring Security + JWT (RefreshToken, AccessToken)를 사용한 로그인, 로그아웃 구현 - 4편
개요* 1~3편 정리1편에서 기본적인 세팅은 끝이 났다.2편에서는 SecurityConfig에 대해서 보안 설정을 했다.3편에서는 RefreshToken과 BlackListToken에 대해서 엔티티와 레포지토리, 서비스를 작성했다. 4편
eesko.tistory.com
jwt에 대해서 알고싶다면
[멋쟁이사자처럼 백엔드 TIL] Spring Security : JWT(JSON Web Token) :: 미정 (tistory.com)
'Spring > Security' 카테고리의 다른 글
Spring : OAuth2 로그인 방식 (0) | 2024.07.26 |
---|---|
Spring Security + Session 를 사용한 회원가입, 로그인, 로그아웃 구현 (0) | 2024.07.26 |
Spring Security : JWT(JSON Web Token) (4) | 2024.07.23 |
Spring Security + JWT (RefreshToken, AccessToken)를 사용한 로그인, 로그아웃 구현 - 4편 (0) | 2024.07.23 |
Spring Security + JWT (RefreshToken, AccessToken)를 사용한 로그인, 로그아웃 구현 - 3편 (1) | 2024.07.22 |