Spring Security
Spring Security는 Java 기반의 애플리케이션에서 보안과 인증을 처리하기 위한 프레임워크로 REST API 및 서비스를 보호하기 위한 다양한 보안 기능을 제공하며 주요 기능으로 인증(Authentication)과 인가(Authorization)가 있다. Spring 프레임워크와 통합되어 사용하기 쉽고 유연한 설정 방식을 제공한다. XML 또는 Java 구성을 통해 보안 설정을 정의할 수 있으며, 어노테이션과 Spring Boot의 자동 설정 기능을 활용하여 간편하게 보안을 구성할 수 있다.
* 인증(Authentication)
인증은 사용자의 신원을 확인하는 과정으로, 사용자가 제공한 자격 증명(예: 아이디와 비밀번호)을 검증하여 사용자를 식별한다. Spring Security는 다양한 인증 방식을 지원하며, 사용자 정보를 데이터베이스, LDAP 서버, 메모리 등 다양한 소스에서 가져올 수 있다.
* 인가(Authorization)
인가는 인증된 사용자에게 특정 리소스 또는 기능에 대한 액세스 권한을 부여하는 과정이다. Spring Security는 세분화된 권한 관리를 지원하여, 역할 기반의 접근 제어를 구현할 수 있다. 이를 통해 애플리케이션의 리소스에 대한 접근 제한을 설정할 수 있다.
* 이 외 기능
Spring Security는 또한 다양한 보안 기능을 제공한다. 세션 관리, 크로스 사이트 스크립팅(XSS) 및 크로스 사이트 요청 위조(CSRF) 공격 방어, 보안 헤더 설정 등을 처리할 수 있다.
* 전체 소스 코드
https://github.com/kangilbin/Spring-Boot/tree/master/Spring%20Security
1. Security 설정 파일 생성
WebSecurityConfigurerAdapter 클래스는 보안 구성을 설정하는 클래스로 configure 메서드를 재정의 하여 보안 요구 사항에 맞게 설정한다.
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
/**
* 1-1) 보안 구성을 설정
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/", "/hello").permitAll() // "/", "/hello"는 모두 볼 수 있음
.anyRequest().authenticated() // 나머지 모든 요청은 인증이 필요
.and()
.formLogin() // form 로그인 사용
.and()
.httpBasic(); // http basic 사용
}
/**
* 1-2) 패스워드 인코딩
* Spring Security에서 권장하는 패스워드 인코딩 방법
* 인코딩을 하지 않으면 오류 발생
*/
@Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
}
1-1) configure() 메서드 주요 기능
설정 | 설명 |
http.authorizeRequests() | URL 경로에 대한 인가 규칙을 설정합니다. |
.antMatchers().permitAll() | 특정 URL 경로를 인증 없이 허용합니다. |
.anyRequest().authenticated() | 모든 요청에 대해 인증을 요구합니다. |
.hasRole() | 특정 역할을 가진 사용자만 접근을 허용합니다. |
.formLogin() | 폼 기반 로그인을 활성화합니다. |
.loginPage() | 로그인 페이지의 경로를 지정합니다. |
.defaultSuccessUrl() | 로그인 성공 후 이동할 기본 URL을 설정합니다. |
.logout() | 로그아웃을 처리하는 설정을 추가합니다. |
.logoutUrl() | 로그아웃 URL을 지정합니다. |
.logoutSuccessUrl() | 로그아웃 성공 후 이동할 URL을 설정합니다. |
.csrf() | CSRF(Cross-Site Request Forgery) 공격 방어 설정을 활성화합니다. |
.sessionManagement() | 세션 관리를 설정합니다. |
.sessionCreationPolicy() | 세션 생성 정책을 설정합니다. |
1-2) 패스워드 인코딩
Spring Security에서는 보안을 위해 패스워드를 인코딩하여 저장해야 한다. 패스워드 인코딩은 일반적으로 단방향 해시 함수를 사용하며 원래 값으로 복원할 수 없다. Security 권장하는 암호화 알고리즘 방법은 BCrypt로 암호화 메서드를 Bean에 등록하여 사용한다.
2. 사용자 Service 클래스 생성
사용자 정보를 관리하는 Service에 UserDetailsService를 구현하거나 새로운 클래스를 만들어 UserDetailsService구현해야 한다. 중요한 건 UserDetailsService을 Bean에 등록 한다는 것이다.
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
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.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import java.util.Arrays;
import java.util.Collection;
import java.util.Optional;
@Service
public class AccountService implements UserDetailsService {
@Autowired
private AccountRepository accountRepository;
@Autowired
private PasswordEncoder passwordEncoder;
// 새로운 acount 저장
public AccountEntity createAccountEntity(String username, String password) {
AccountEntity accountEntity = new AccountEntity();
accountEntity.setUsername(username);
accountEntity.setPassword(passwordEncoder.encode(password)); // 패스워드 인코딩
return accountRepository.save(accountEntity);
}
// 2-1) 로그인 처리 시에 자동으로 호출된다.
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
/*
로그인할 때 username, password를 입력하는데 입력받은 username을 가지고와 해당 username에
해당하는 user 정보에서 password를 가져와 입력받은 password와 비교해 같으면 로그인 처리한다.
다를 경우 예외를 던진다.
*/
Optional<AccountEntity> byUsername = accountRepository.findByUsername(username);
AccountEntity accountEntity = byUsername.orElseThrow(() -> new UsernameNotFoundException(username)); // 해당하는 데이터가 없으면 예외 처리
/*
* UserDetails을 return 해줘야 하지만 UserDetails은 Servcie의 User 정보
* 즉, 현재 프로젝트에서는 AccountEntity의 인터페이스이다. 이 인터페이스의
* 기본 정보를 Spring Security에서 User라는 클래스로 지원해준다.
* new User(id, pw, authorities)
*/
return new User(accountEntity.getUsername(), accountEntity.getPassword(), authorities());
}
// 2-2)
private Collection<? extends GrantedAuthority> authorities() {
return Arrays.asList(new SimpleGrantedAuthority("ROLE_USER")); // 권한 세팅
}
2-1) loadUserByUsername 메서드
인증 과정 중에 자동으로 호출되는 메서드로 사용자가 로그인할 때, 입력한 사용자명(username)을 기반으로 Spring Security는 loadUserByUsername() 메서드를 호출하여 사용자 정보를 조회한다. 이 메서드는 사용자명을 입력받아 해당 사용자에 대한 정보를 반환하는 역할을 수행한다.
2-2) 사용자 권한 설정
사용자의 권한 정보를 나타내는 GrantedAuthority 객체들의 컬렉션으로, 사용자가 가지고 있는 권한들을 전달한다.
- GrantedAuthority : 인증된 사용자의 권한을 나타내는 인터페이스로 일반적으로는 SimpleGrantedAuthority 클래스를 사용하여 권한을 생성하고 컬렉션에 추가한다.
- ROLE_USER : Spring Security에서 사전 정의된 기본적인 권한 이름이다. 기본적으로 이러한 권한 이름을 사용하여 권한 기반의 접근 제어를 수행할 수 있도록 지원한다.
* Controller에서 권한 체크 방법 두 가지
@Controller
public class MyController {
@GetMapping("/admin")
public String adminPage() {
// ROLE_ADMIN 권한을 가진 사용자만 해당 페이지에 접근할 수 있도록 체크
SecurityContextHolder.getContext().getAuthentication().getAuthorities().stream()
.filter(a -> a.getAuthority().equals("ROLE_ADMIN"))
.findFirst()
.orElseThrow(() -> new AccessDeniedException("Access denied"));
// 권한 체크를 통과한 경우에만 해당 페이지로 이동
return "admin";
}
}
@Controller
public class MyController {
/**
* @PreAuthorize을 사용해서 권한을 요구하는 설정을 추가
*/
@GetMapping("/admin")
@PreAuthorize("hasRole('ADMIN')")
public String adminPage() {
// ...
}
}
* Security 설정 파일에서 체크
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/admin").hasRole("ADMIN") // "/admin"는 ROLE_ADMIN 권한만 가능
.antMatchers("/user").hasRole("USER") // "/user"는 ROLE_USER 권한만 가능
.antMatchers("/", "/hello").permitAll() // "/", "/hello"는 모드 볼 수 있음
.anyRequest().authenticated()
.and()
.formLogin()
.and()
.httpBasic();
}
@Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
}
반응형
'Java' 카테고리의 다른 글
[Spring Boot/Spring Cloud] MSA - API Gateway를 사용한 로드밸런싱 방법 (0) | 2023.08.04 |
---|---|
[Spring Boot/Spring Cloud] MSA - Netflix Eureka 서버/클라이언트 구축 (0) | 2023.07.23 |
[Spring Boot/Spring Data JPA] Flyway 데이터베이스 마이그레이션 (0) | 2023.07.04 |
[Spring Boot] Spring Data JPA 데이터 초기화 (0) | 2023.07.03 |
[Spring Boot] Spring Data JPA 연동 방법 (0) | 2023.07.02 |