스프링 시큐리티 테이블 구성

image

테이블은 role과 user로 구성되어 있다. user 테이블에서 user_name은 아이디를 의미한다. 이름은 user_nickname 컬럼에 들어간다.

image

role 테이블은 사용자가 어떤 역할을 수행하는지를 정의하는 컬럼이 들어있다. 어떤 사용자는 일반 사용자의 역할을 수행하고, 111122 사용자는 일반 사용자와 관리자 역할을 둘 다 수행하는 것을 확인할 수 있다. 한 사용자는 여러 개의 권한을 가질 수 있다.

image

스프링 시큐리티 설정을 위해서 구현해야 하는 인터페이스가 두 가지 있다. DB에서 유저 정보를 조회하는 기능을 하는 UserDetailsService를 구현한 객체와 사용자 정보를 제공하는 UserDetails를 구현한 객체이다.

CustomUserDetailsService

이 인터페이스에는 loadUserByUsername(String username) 메소드가 정의되어져 있다. 여기서 username이란 사용자를 고유하게 식별할 수 있는 값을 의미하는데, 아이디, 사번, 학번, 이메일, 고객번호 등이 될 수 있다.

    @Service
    public class CustomUserDetailsService implements UserDetailsService {

        @Autowired
        UserMapper userMapper;
        
        @Override
        public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
            return userMapper.getUserByEmail(username);
        }
    }

UserDetails

UserDetails 인터페이스에 정의되어 있는 메소드는 다음과 같다.

  • Collection<? extends GrantedAuthority> getAuthorities();
    • 계정의 권한 목록 반환
  • String getPassword();
  • String getUsername();
    • 사용자의 고유한 값 반환 (DB primary key값 혹은 이메일)
  • boolean isAccountNonExpired();
  • boolean isAccountNonLocked();
  • boolean isCredentialsNonExpired();
  • boolean isEnabled();
    public class User implements UserDetails {

        private static final long serialVersionUID = -4446871534767640001L;
        
        private int no;
        private String email;
        private String password;
        private String name;
        
        public int getNo() {
            return no;
        }
        public void setNo(int no) {
            this.no = no;
        }
        public String getEmail() {
            return email;
        }
        public void setEmail(String email) {
            this.email = email;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public void setPassword(String password) {
            this.password = password;
        }
        
        // 인증이 완료된 사용하는 항상 ROLE_USER 권한을 반환한다.
        @Override
        public Collection<? extends GrantedAuthority> getAuthorities() {
            List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
            authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
            return authorities;
        }
        
        @Override
        public String getPassword() {
            return password;
        }
        
        // 사용자를 고유하게 식별할 수 있는 값
        @Override
        public String getUsername() {
            return email;
        }
        
        @Override
        public boolean isAccountNonExpired() {
            return true;
        }
        
        @Override
        public boolean isAccountNonLocked() {
            return true;
        }
        
        @Override
        public boolean isCredentialsNonExpired() {
            return true;
        }
        
        @Override
        public boolean isEnabled() {
            return true;
        }
    }

WebSecurityConfigurerAdapter

다른 일반적인 설정은 SpringBoot와 같은데, 한가지 추가 설정이 필요하다. config 패키지 안의 WebSecurityConfig 클래스를 살펴보자. 해당 객체에는 크게 UserDetailsService와 PasswordEncoder를 주입해 주어야 한다.

    @EnableWebSecurity
    public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
        
        @Autowired
        CustomUserDetailsService customUserDetailsService;
        
        @Bean
        public PasswordEncoder passwordEncoder() {
            return new BCryptPasswordEncoder();
        }

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
                .csrf().disable()
                .authorizeHttpRequests()
                // 권한별 접근제어
                .antMatchers("/", "/register", "/login", "/error/*").permitAll()
                .antMatchers("/admin/*").hasRole("ADMIN")
                .anyRequest().hasRole("USER")
                // 접근거부 예외 처리(http 응답코드 : 403)
                .and()
                    .exceptionHandling().accessDeniedPage("/error/forbidden")
                // 로그인  설정
                .and()
                    .formLogin()					// 폼 로그인사용
                    .loginPage("/login")			// 사용자 로그인 페이지 지정, 로그인이 필요한 경우 /login을 시큐리티가 요청한다.
                    .loginProcessingUrl("/login")	// 로그인폼에서 로그인을 요청할 때 사용하는 URL을 지정
                    .defaultSuccessUrl("/login", true)	// 로그인 성공일 때 요청하는 URL 지정
                    .failureUrl("/login?error=true")// 로그인 실패했을 때 요청하는 URL 지정
                // 로그아웃 설정
                .and()
                    .logout()
                    .logoutUrl("/logout")			// 로그아웃을 요청하는 URL 지정
                    .logoutSuccessUrl("/")			// 로그아웃 성공일 때 요청하는 URL 지정
                    .deleteCookies("JSESSIONID");	// 로그아웃이 완료되면 쿠키값을 삭제
                    
        }
        
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            // UserDetails			- 인증된 사용자 정보를 제공하는 기능이 정의된 인터페이스다. 
            // UserDetailsService	- 사용자 인증처리를 수행하는 기능이 정의된 인터페이스다.
            
            // 스프링 시큐리티의 인증관리자객체에 사용자정의 UserDetailsService(사용자정보를 제공)와 
            // 비밀번호를 암호화하는 패스워드인코더를 전달한다.
            auth
                .userDetailsService(customUserDetailsService)
                .passwordEncoder(passwordEncoder());
        }

        @Override
        public void configure(WebSecurity web) throws Exception {
            // 스프링 시큐리티 인증과정을 사용하지 않는 요청 URL을 지정한다.
            // 주로 정적 자원(이미지, CSS, 자바스크립트)을 요청하는 URL을 지정한다.
            web.ignoring().antMatchers("/resources/**");
        }
    }

Tags:

Categories:

Updated: