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 에러가 발생한다.
