spring security 도전기
springboot, jpa, thymeleaf 로 게시판 프로젝트 중에 로그인/로그아웃 + 권한제어 기능을 추가하려고 spring security 를 추가했습니다.
Spring Security 란?
- 스프링 기반의 어플리케이션의 인증과 권한을 담당하는 프레임워크
인증, 인가
- 로그인을 했을 때 DB 에 저장된 정보와 회원이 친 loginId, password 가 일치 했을 때 (현재 아이디의 정보가 누구인지 확인) = 인증 (Authentication)
- 관리자로써 권한을 갖고 로그인을 했을 때 일반 회원은 볼 수 없는 비밀 댓글의 내용 혹은 회원 정보등을 볼 수 있을 때 (권한 검사) = 인가 (Authorization)
Spring Security 구조
일단 사용하려면 dependency 부터 걸어줍시다.
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
그런 후에 spring security config 를 만들어서 셋팅 해줍시다. 예를 들어 로그인한 회원만 글 작성 혹은 글을 열람할 수 있고, 로그인하지 못한 게스트는 기능 제한을 해주는 세팅을 해줍시다.
SpringSecurityConfig
@Configuration
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.mvcMatchers("/users/**").hasRole("ADMIN")
.mvcMatchers("/posts/**").hasRole("MEMBER")
.anyRequest().permitAll()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.logoutSuccessUrl("/")
.and()
.csrf()
.disable();
}
}
메소드 | 설명 |
---|---|
authorizeRequests() | HttpServletRequest 를 이용해서 인증 처리 |
mvcMatchers() | mvcMatcher(String mvcPatter) - 제공된 Spring MVC 패턴과 일치하는 경우에만 HttpSecurity를 호출하도록 설정할 수 있습니다. |
antMatchers() | AntMatcher(String AntPatter) - 제공된 ant 패턴과 일치하는 경우에만 HttpSecurity를 호출하도록 설정할 수 있습니다 |
hasRole() | 특정 권한 있는 사람만 접근 가능합니다. |
permitAll() | 모든 사용자가 접근 가능하다는 것을 의미합니다. |
anyRequest().permitAll() | 그 외 모든 요청은 접근 가능 |
csrf() | Token 정보를 Header 정보에 포함하여 서버 요청을 시도하는 것 (쉽게 말해 보안 강화) |
spring security config 설정이 다 끝나면 service 에서 처리해줘야합니다. UserService 가 있지만 역할 분담을 위해 UserAuthService 를 따로 만들었습니다.
UserAuthService
public class UserAuthService implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUsername(username).orElseThrow(() -> new UsernameNotFoundException("존재하지않는 회원입니다"));
List<GrantedAuthority> authorities = new ArrayList<>();
GrantedAuthority authority = new SimpleGrantedAuthority(user.getRole());
authorities.add(authority);
return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), authorities);
}
}
User 가 두개가 있습니다. 제가 직접만든 User Entity, 그리고 spring security 에서 지원하는 User
코드 | 설명 |
---|---|
findByUserName | 로그인 처리를 하기 전 username (=loginId) 를 가져옵니다. |
UserDetails | Spring Security에서 사용자의 정보를 담는 인터페이스는 UserDetails 인터페이스우리가 이 인터페이스를 구현하게 되면 Spring Security에서 구현한 클래스를 사용자 정보로 인식하고 인증 작업을 합니다 |
UserDetailsService | DB에서 유저 정보를 직접 가져오는 인터페이스를 구현 |
GrantedAuthority | 권한 있는 사용자인지 판단해주는 타입 |
주의사항
- 회원 아이디를 받을 때 entity 에서 username 으로 받아줘야한다. (spring security 규약을 지키기위함)
- 회원 비밀번호를 받을 때 entity 에서 password 로 받아줘야한다. (//)
-> 규약을 지키지않으면 403 에러와 로그인처리가 전혀 되지않는다. (방법을 알고계신 분 eojin312@naver.com 으로 알려주세요 ㅠㅠ)
- role 을 같이 넘겨주지않으면 일반적으론 403 에러가 발생한다.