Spring Security (세션기반)
과정
로그인 요청(/login 폼 제출)
<!-- login.html -->
<form method = "POST" action="/login">
<input type = "text" name = "username" placeholder = "아이디">
<input type="password" name="password" placeholder="비밀번호" />
<button type="submit">로그인</button>
</form>
인증 위임(AuthenticationManager)
예시
@Service
publci class CustomUserDetailsService implements UserDetailsService{
@Override
public UserDetails loadUserByUsername(String username){
//DB에서 사용자 조회 예시
if("minsang".equals(username)){
return User.builder()
.username("minsang")
.password(passwordEncoder().encoder("1234"))
.roles("USER")
.build();
}
throw new UsernameNotFoundException("사용자 없음");
}
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
}
비밀번호 검증(PasswordEncoder 매칭)
boolean matches = passwordEncoder.matches(rawPassword, encodedPasswordFromDB);
if(!matches) throw new BadCredentailsException("비밀번호 불일치")
SecurityContext 채우기 + 세션 생성
Authentication authResult = authenticationManger.authenticate(authRequest);
//인증 성공하면 SecurityContext에 저장
SecurityContext context = SecurityContextHolder.createEmptyContext();
context.setAuthentication(authResult);
인가(Authorization)
정책이 퍼지면 추적이 어려움
@RestController
public class BoardController{
@PreAUthorize(hasRole('ADMIN'))
@GetMapping("/admin")
public String adminPage(){...}
@PreAUthroize("hasRole('USER')")
@GetMapping("/user")
public String userPage(){...}
}
@Configuration
@EnableWebSecurity
public class SecurityConfig{
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception{
http
.csrf(csrf -> csrf.disable()) //REST면 disable, 폼이면 enable 고려
.authorizeHttpRequests(auth -> auth
.requestMatchers("/login", "/public/**").permitAll()
.anyRequest().authenticated()) // 나머지 모든 요청은 인증된 사용자만 접근 가능
.formLogin(form -> form
.loginPage("/login")
.defaultSuccessUrl("/me",true)
.permitAll()).logout(logout -> logout.logoutUrl("/logout"));
return http.build();
}
@Bean
public UserDetailsService userDetailsService(PasswordEncoder encoder){
UserDetails user = User.withUsername("test")
.password(encoder.encode("1234"))
.roles("USER")
.build();
return new InMemoryUserDetailsManager(user)
}
@Bean
PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
}
회원가입(signUp)
@Entity
public class User {
@Id
@GeneratedValue
private Long id;
private String username;
private String password;
private String role;
public User(String username, String password, String role){
this.username = username;
this.password = password;
this.role = role;
}
}
과정 특징
장점
단점
AuthController - 회원가입
@PostMapping("/auth/signUp")
public String singUp(@RequestBody SignUpRequest req){
User user= new User(req.username(), passwordEncoder.encode(req.password()),
userRepository.save(user);
return "회원가입 완료";
}
@PreAuthorize(”hasRole(’ROLE_USER’)”)
CSRF(Cross-Site Request Forgery, 사이트 간 요청 위조)
Collection<? extends GrantedAuthority> GrantedAuthority의 자식 타입을 담을 수 있다는 의미 (제네릭 와일드 카드)