[Spring Security] SecurityConfig, UserDetailsService, BCrypt, CSRF
1. Spring Security
: Application 의 보안( Authentication, Authroization )을 담당하는 스프링 하위 프레임워크
- Authentication ( 인증 ) : 해당 사용자의 로그인 정보가 일치하는 지 확인 ( ID / PW )
- Authorization ( 권한 ) : '인증' 된 사용자가 요청한 페이지에 접근 가능한지 권한을 확인하고 부여
[ SecurityConfig ]
: 시큐리티의 Authentication / Authorization 기능을 설정
(1) old 버전
@Configuration
@EnableWebSecurity
public class SecurityConfig (extends WebSecurityConfigurerAdapter) {
@Override
protected void configure(HttpSecurity http) throws Exception{
http.csrf().disable(); // Spring Security - Post - 403 forbidden 방지
http
.authorizeRequests()
// admin 페이지는 ROLE_ADMIN 일때만 match
.antMatchers("/admin**/**").hasAuthority("ROLE_ADMIN")
// 나머지 request는 허용 // .authenticated() 로 주면 로그인 했을시 허용 // authorization : 권한처리
.anyRequest().permitAll()
.and()
// 로그인페이지 설정
.formLogin()
.loginPage("/login")
.permitAll() // 로그인 페이지 접근 허용
//.loginProcessingUrl("/loginProcess")
.usernameParameter("email")
.passwordParameter("pw")
.loginProcessingUrl("/loginProcess")
.defaultSuccessUrl("/allboard")
.failureHandler(authenticationFailureHandler)
// .failureUrl("/loginFail")
.and()
// 로그아웃
.logout()
.logoutSuccessUrl("/login")
- 관리자 페이지와 같은 권한에 따른 페이지 접근 권한을 부여하고자 하면
.authorizeRequests().anyMatchers("url꼴").hasAuthority("ROLE_권한").anyRequest().permitAll()
=> 특정 url 에 대한 접근을 특정 권한이 있을 시에만 permitAll 시켜준다.
=> 권한은 Enum 을 활용해서 USER / ADMIN 등의 역할을 부여하는 과정을 거친 뒤 Set, DB 에 insert 해준다.
(2) New 버전
: SecurityFilterChain을 리턴하는 메소드를 빈에 등록하는 방식 (컴포넌트 방식으로 컨테이너가 관리)
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.csrf().disable();
http.authorizeRequests()
.antMatchers("/user/**").authenticated()
.antMatchers("/manager/**").access("hasAnyRole('ROLE_MANAGER','ROLE_ADMIN')")
.antMatchers("/admin/**").access("hasRole('ROLE_ADMIN')")
.anyRequest().permitAll();
return http.build();
}
}
=> SecurityConfig 에서는 Authentication 에 성공했을 시, 실패했을 시 특정 URL로 보내는 설정을 해주고,
Authentication, 즉 로그인 정보가 일치하는지에 대해
UserDetailService 인터페이스를 상속해서 사용하는 UserDetails 메소드를 이용합니다.
package ask;
import java.util.Optional;
import javax.servlet.http.HttpSession;
import ask.Member.MemberDTO;
import ask.Member.MemberRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
@Service
public class CustomUserDetailService implements UserDetailsService {
@Autowired
private MemberRepository memberRepository;
@Autowired
HttpSession session;
@Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
Optional<MemberDTO> user = memberRepository.findByEmail(email);
if (user == null) {
return null;
}
// session 에 set만 해두면 자동으로 successURL 로 넘어감
session.setAttribute("sessiondto", user.get());
return User.builder()
.username(email)
.password(user.get().getPw())
.roles(user.get().getRole())
.build();
}
}
=> UserDetails 에서는 사용자가 로그인 시도 시 입력한 id (email) 값을 받아와서
DB에서 해당 id에 해당하는 사용자 객체를 가져온다.
=> 사용자 객체를 User.builder() 를 사용해 빌드하고 리턴한다.
=> 리턴값을 가지고 자동으로 로그인 정보가 일치하는 지 검증 후 success / fail 을 판단,
=> SecurityConfig 에서 성공 실패에 대한 URL, 혹은 Handle 처리를 받게 된다.
2. Bcrypt 암호화 처리
SecurityConfig 에서 @Bean 등록해두고,
회원가입 process를 처리할 때
@Autowired Bcrypt 객체를 가져와서
.encode()
암호화 후 객체에 set, DB에 저장한다.
- Security Config
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
- 회원가입 Process
@Autowired
BCryptPasswordEncoder bCryptPasswordEncoder;
@PostMapping("/joinProcess")
public String joinProcess(MemberDTO memberDTO) throws Exception {
// PW 암호화
memberDTO.setPw(bCryptPasswordEncoder.encode(memberDTO.getPw()));
memberRepository.registerMember(memberDTO);
return "redirect:/login";
}
- Spring Security 를 사용해 관리자, 일반 사용자 등에 따른 권한을 부여하고,
권한을 가진 사용자만 접근할 수 있는 페이지를 만들 수 있고 ( Authorization )
- UserDetailService 인터페이스를 활용해 로그인 정보 검증을 진행 ( Authentication ),
검증 통과 / 실패에 따라 Security Config 파일에서 처리하는 방법을 정해둘 수 있습니다.
3. Security - CSRF
: Cross-site request forgery 403 forbidden
(1) Spring Security 적용 시, 요청에서 csrf => forbidden 403
=> Spring Config 파일 생성, 설정이 선행되어야 함
(2) Spring Boot 에서 Spring Security 를 사용하고, Post 방식 요청을 하고자 할 때
CSRF 로 인해 403 발생, Spring config 설정 - configure 세팅에서
http.csrf().disable(); // Spring Security - Post - 403 forbidden 방지
설정 추가 필요
(3) AJAX 통신을 사용하고자할 때, CSRF 로 인한 403 에러 발생
<head> 안에 토큰, 헤더 name 으로 지정해두고
<meta name="_csrf_parameter" content="${_csrf.parameterName}" />
<meta name="_csrf_header" content="${_csrf.headerName}" />
<meta name="_csrf" content="${_csrf.token}" />
- ajax 에서 데이터를 보내기 전에, 헤더에 토큰으로 담아서 보내는 설정을 해준다.
- if 문으로 토큰과 헤더가 존재할시만 실행하게 해서 null 값일 때 발생하는 에러를 잡아줘야한다.
beforeSend: function(xhr){
if(token && header) {
xhr.setRequestHeader(header, token);}
},
data : ...