티스토리 뷰
1. Spring Security란?
Spring Security는 스프링 기반 애플리케이션의 보안(인증, 인가 등)을 담당하는 스프링의 하위 프레임워크이다. 만약 이 프레임워크가 존재하지 않았다면 자체적으로 작성해야 할 로직이 많지만, Spring Security는 보안과 관련해서 체계적으로 많은 옵션을 제공해준다는 장점이 있다.
* 인증과 인가
인증(Authenticate) : 보호된 리소스에 접근한 대상의 신원 정보를 확인하는 과정(ex. 로그인 과정)
인가(Authorize) : 인증된 사용자가 어떤 리소스에 접근할 수 있고, 어떤 동작을 수행할 수 있는지 검증하는 과정
2. Spring Security의 특징
- Servlet API 통합
- 인증, 인가에 대해 포괄적이고 확장 가능한 지원
- 필터 기반으로 동작하여 MVC와 분리하여 관리 및 동작
- annotation을 통한 간단한 설정
- 세션과 쿠키 방식으로 인증
- Authentication Manager와 Access Decision Manager를 통해 사용자의 리소스 접근을 관리
- 인증 관리자는 UserNamePasswordAuthenticationFilter, 접근 관리자는 FilterSecurityInterceptor가 수행
3. Spring Security의 기본 구조
- SecurityContextPersistenceFilter : SecurityContextRepository에서 SecurityContext를 가져오고 저장하는 일을 담당
- LogoutFilter : 로그아웃 URL로 지정된 가상 URL에 대한 요청을 감시하고 일치하는 요청이 있으면 사용자를 로그아웃시킴
- UsernamePasswordAuthenticationFilter : 로그인 URL에 대한 요청을 감시하며, 사용자 인증 처리
- DefaultLoginPageGeneratingFilter : 로그인 form URL에 대한 요청을 감시하며, 로그인 form 기능을 수행하는데 필요한 HTML 생성
- BasicAuthenticationFilter : HTTP 기본 인증 헤더를 감시하고 헤더에 Basic 토큰 존재 시 인증 처리
- RequestCacheAwareFilter : 로그인 성공 후 기존 요청 정보를 재구성하기 위해 사용
- SecurityContextHolderAwareRequestFilter : HttpServletRequestWrapper를 상속한 SecurityContextHolderAwareRequestWrapper로 HttpServletRequest 정보를 감쌈
- AnonymoutAuthenticationFilter : 해당 필터가 호출되는 시점까지 사용자 정보가 인증되지 않았다면, 인증 토큰에 사용자가 익명 사용자로 나타남
- RequestCacheAwareFilter : 로그인 성공 이후, 기존 요청 정보를 재구성하기 위해 사용
- ExceptionTranslationFilter : 보호된 요청을 처리할 때 발생할 수 있는 예외를 위임하거나 전달
- FilterSecurityInterceptor : AccessDecisionManager로써 권한 부여처리를 위임하여 접근 제어를 쉽게 해줌
4. Spring Security 등록 및 기본 사용법
4.1 라이브러리 추가하기
Maven 사용 - pom.xml 수정
...
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
....
Gradle 사용 - build.gradle 수정
dependencies{
...
implementation 'org.springframework.boot:spring-boot-starter-security'
}
4.2 CustomUserDetail 만들기
@Entity
class Member{
@Id
@GenerateValue(stragy=StragyType.IDENTITY)
private long id;
@Column
private String username; // 로그인시 받는 id값
@Column
private String password; // 로그인시 받는 password값
@ElementCollection
@CollectionTalbe(name="roles",joinColumns=@JoinColumn(name="member_id"))
@Column(name="role")
private List<String> hasRole = new ArrayList<>(); // 가지고 있는 권한 정보
}
class MemberDetail implements UserDetails {
private Member member;
public MemberDetail(Member member) {
this.member = member;
}
// 권한정보 제공
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
// 특별한 권한 시스템을 사용하지 않을경우
// return Collections.EMPTY_LIST;
// 를 사용하면 된다.
ArrayList<GrantedAuthority> auths = new ArrayList<>();
for(String role : member.getHasRole()){
auths.add(new GrantedAuthority() {
@Override
public String getAuthority() {
return role;
}
});
}
return auths;
}
// 비밀번호 정보 제공
@Override
public String getPassword() {
return member.getLoginPw();
}
// ID 정보 제공
@Override
public String getUsername() {
return member.getLoginId();
}
// 계정 만료여부 제공
// 특별히 사용을 안할시 항상 true를 반환하도록 처리
@Override
public boolean isAccountNonExpired() {
return true;
}
// 계정 비활성화 여부 제공
// 특별히 사용 안할시 항상 true를 반환하도록 처리
@Override
public boolean isAccountNonLocked() {
return true;
}
// 계정 인증 정보를 항상 저장할지에 대한 여부
// true 처리할시 모든 인증정보를 만료시키지 않기에 주의해야한다.
@Override
public boolean isCredentialsNonExpired() {
return false;
}
// 계정의 활성화 여부
// 딱히 사용안할시 항상 true를 반환하도록 처리
@Override
public boolean isEnabled() {
return true;
}
}
4.3 CustomUserDetailService 만들기
@Service
@RequriedArgsConstructor
public class MemberDetailService implements UserDetailService{
private final MemberRepo repo;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// username으로 Repository에서 Member을 찾는 method
Member member = repo.findByUsername(username);
if(member == null)
throw new UsernameNotFoundException("계정을 찾을 수 없습니다.");
return new MemberDetail(member);
}
}
4.4 Configuration 만들기
@Configuration
@RequiredArgsConstructor
@EnableWebSecurity
class SecurityConfig extends WebSecurityConfigurerAdapter{
private final MemberDetailService detailService;
/**
* Spring Security의 앞단 설정을 할수 있다.
* debug, firewall, ignore등의 설정이 가능
* 단 여기서 resource에 대한 모든 접근을 허용하는 설정할수도 있는데
* 그럴경우 SpringSecuity에서 접근을 통제하는 설정은 무시해버린다.
*/
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/h2-console/**");
}
/**
* Spring Security 기능 설정을 할수 있다.
* 특정 리소스에 접근하지 못하게 하거나 반대로 로그인, 회원가입 페이지외에 인증정보가 있어야
* 접근할 수 있도록 설정할 수 있다.
* 특정 리소스의 접근허용 또는 특정 권한 요구,로그인, 로그아웃, 로그인,로그아웃 성공시 Event
* 등의 설정이 가능하다.
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
// 요청에 대한 설정
// permitAll시 해당 url에 대한 인증정보를 요구하지 않는다.
// authenticated시 해당 url에는 인증 정보를 요구한다.(로그인 필요)
// hasAnyRole시 해당 url에는 특정 권한 정보를 요구한다.
// resources에 대해 접근혀용을 해야지 브라우저에서 로그인없이 js파일이나 css파일에 접근할 수 있다.
http
.authorizeRequests() // 요청에 대한 설정
.antMatchers("/notice/**").permitAll()
.antMatchers("/main").permitAll()
//.antMatchers("/js/**").permitAll()
//.antMatchers("/css/**").permitAll()
.antMatchers("/resources/**").permitAll()
.antMatchers("/admin/**").hasAnyRole("ADMIN")
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.loginProcessingUrl("/member/login")
.defaultSuccessUrl("/main",true)
.permitAll()
.and()
.logout()
.logoutUrl("/member/logout")
.logoutSuccessUrl("/login")
.logoutSuccessHandler(logoutHnadler).permitAll();
}
/**
* 사용자 인증 관련 설정
* Custom User Detail Service를 지정하고 PasswordEncoder을 사용해서 비밀번호를 암호화 할 수 있다.
* 참고로 비밀번호는 같은 암호화방식을 사용해서 Database에 저장해야지 인증가능하다.
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(detailService).passwordEncoder(getPasswordEncoder());
}
@Bean
public BCryptPasswordEncoder getPasswordEncoder(){
return new BCryptPasswordEncoder();
}
}
4.5 사용하기
@Controller
public class PageController{
@GetMapping("/member")
public Member getLoginMemberInfo(@AuthenticationPrincipal MemberDetail detail){
return detail.getMember();
}
}
참고 자료
https://jangjjolkit.tistory.com/24
https://devuna.tistory.com/55
https://maeng0830-note.tistory.com/29
'Backend > Spring Boot' 카테고리의 다른 글
[Spring] OAuth 2.0이란? (0) | 2023.11.26 |
---|---|
[Spring] RDBMS의 이해 (0) | 2023.09.22 |
[Spring] JPA(Java Persistence API) 개념 정리 (0) | 2023.09.22 |
[Spring] 스프링 의존성 주입 3가지 방법 (0) | 2023.09.15 |
[Spring] REST API 설계 및 Controller 작성 & 설계 (0) | 2023.09.14 |
Blog is powered by
Tistory / Designed by
Tistory