ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Spring Security - 로그인
    Framework & Library/Spring Security 2021. 10. 1. 15:06

    로그인 설정

    SecurityConfig 클래스 수정
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable();
        http.authorizeRequests()
                .antMatchers("/user/**").authenticated()   // 인증 필요
                .antMatchers("/manager/**").access("hasRole('ROLE_ADMIN') or hasRole('ROLE_MANAGER')")   // ROLE_ADMIN, ROLE_MANAGER 권한 필요
                .antMatchers("/admin/**").access("hasRole('ROLE_ADMIN')")   // ROLE_ADMIN 권한 필요
                .anyRequest().permitAll()   // 다른 요청은 모두 허용
                .and().formLogin().loginPage("/loginForm")   // 로그인 페이지의 기본 경로를 설정
                .loginProcessingUrl("/login")
                .defaultSuccessUrl("/");
    }

    ㆍ SecurityConfig 클래스의 configure() 메서드를 위 코드와 같이 수정한다.

    ㆍ loginPrecessinUrl("/login") 메서드는 지정된 주소가 호출이 되면 시큐리티가 낚아채서 대신 로그인을 수행해준다. 따라서 Controller 영역에 로그인을 수행하는 메서드를 구현할 필요가 없다.

    ㆍ.defaultSuccessUrl("/") 메서드는 로그인이 완료되면 이동 url을 설정하는 메서드이다.

     

    loginForm.html 수정
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>로그인 페이지</title>
    </head>
    <body>
    <h1>로그인 페이지</h1>
    <hr/>
    <form action="/login" method="post">
        <input type="text" name="username" placeholder="Username"/>
        <br/>
        <input type="password" name="password" placeholder="Password"/>
        <br/>
        <button>로그인</button>
    </form>
    <a href="/oauth2/authorization/google">구글 로그인</a>
    <a href="/joinForm">회원가입</a>
    </body>
    </html>

    ㆍ 로그인을 수행하기 위해 위 코드와 같이 action과 method 속성을 추가해준다.


    UserDetails 구현

    Spring Security Session

    스프링은 로그인이 정상적으로 완료되면 스프링 시큐리티를 위한 Session을 만들어 로그인 처리를 해준다.

    ㆍ 이때, 스프링 시큐리티 Session에 들어갈 수 있는 객체는 Authentication이어야 한다.

     

    Authentication

    ㆍ 스프링 시큐리티 Session안에 있는 Authentication 객체는 로그인을 한 User에 대한 정보가 들어있어야 한다.

    ㆍ 이때 User의 타입은 그냥 User 객체가 아닌 UserDetails 타입이어야 한다.

     

    UserDetails의 구현
    @Data
    public class PrincipalDetails implements UserDetails {
    
        private User user;
    
        public PrincipalDetails(User user) {
            this.user = user;
        }
    
        @Override
        public Collection<? extends GrantedAuthority> getAuthorities() {
        	return null;
        }
    
        @Override
        public String getPassword() {
        	return null;
        }
    
        @Override
        public String getUsername() {
        	return null;
        }
    
        @Override
        public boolean isAccountNonExpired() {
        	return null;
        }
    
        @Override
        public boolean isAccountNonLocked() {
        	return null;
        }
    
        @Override
        public boolean isCredentialsNonExpired() {
        	return null;
        }
    
        @Override
        public boolean isEnabled() {
        	return null;
        }
    }

    ㆍ config 패키지 내에 auth 패키지를 생성하고 PrincipalDetails 클래스를 만든다.

    ㆍ PrincipalDetails 클래스는 UserDetails 구현하고 메서드들을 오버라이딩하는 형태이다.

    ㆍ 클래스 내에 사용자 정보를 가지고 있는 User 객체를 선언하고, 생성자를 만든다.

    PrincipalDetails 클래스를 통해 로그인에 대한 다양한 처리가 가능한다.

     

    getAuthorities() 메서드 오버라이딩
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        Collection<GrantedAuthority> collection = new ArrayList<>();
    
        collection.add(new GrantedAuthority() {
            @Override
            public String getAuthority() {
                return user.getRole();
            }
        });
    
        return collection;
    }

    ㆍ getAuthorities() 메서드는 로그인을 한 사용자의 권한을 리턴하는 메서드이다.

    ㆍ 현재 User 객체의 role 변수의 타입이 String이기 때문에 시큐리티가 인식하기 위해서는 변환이 필요하다.

    ㆍ getAuthorities() 메서드를 오버라이딩 함으로써 String 타입의 권한을 GrantedAuthority 타입으로 변환한다.


    UserDetailsService 구현

    UserDetailsService

    ㆍ 로그인 요청이 오면 DB에서 사용자 정보를 가져와 비교하는 과정을 거치게 된다.

    ㆍ 해당 과정을 수행하는 인터페이스가 바로 UserDetailsService이며, 로그인 요청이 오게 되면 인터페이스 내 loadUserByUsername() 메서드가 수행됨으로써 그 역할을 수행한다.

     

    PrincipalDetailsService 클래스
    @Service
    public class PrincipalDetailsService implements UserDetailsService {
    
        @Override
        public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
            return null;
        }
    }

    ㆍ auth 패키지에 PrincipalDetailsService 클래스를 생성하고 UserDetailsService 인터페이스를 구현한다.

    loadUserByUsername() 메서드를 오버라이딩 함으로써 DB 내의 사용자 정보를 불러오도록 한다.

     

    loadUserByUsername() 메서드 오버라이딩
    @Service
    public class PrincipalDetailsService implements UserDetailsService {
    
        @Autowired
        private UserRepository userRepository;
    
        @Override
        public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    
            User userEntity = userRepository.findByUsername(username);
    
            if (userEntity != null) {   // DB에 등록된 사용자 이름이 있는지 확인
                return new PrincipalDetails(userEntity);
            }
    
            return null;
        }
    }

    ㆍ DB에 접근하기 하기 위해 UserReository 객체를 선언한다.

    loadUserByUsername() 메서드를 위 코드와 같이 작성한다.

    ㆍ DB에 사용자 정보가 있다면 PrincipalDetails 객체를 리턴하고, 그렇지 않으면 null을 리턴한다.

    ㆍ loadUserByUsername() 메서드에 의해서 리턴된 PrincipalDetails 객체는 Authentication 객체에 들어가게 되고, Authentication 객체는 스프링 시큐리티 Session 안에 들어가게 됨으로써 로그인이 완료된다.


     

    GitHub - qlsdud0604/spring-security-basic: 스프링 시큐리티의 기초를 학습하는 공간

    스프링 시큐리티의 기초를 학습하는 공간. Contribute to qlsdud0604/spring-security-basic development by creating an account on GitHub.

    github.com

     

    728x90

    댓글

Designed by Tistory.