diff --git a/jpablog/src/main/java/com/example/jpablog/config/SecurityConfig.java b/jpablog/src/main/java/com/example/jpablog/config/SecurityConfig.java index 1e1a69ef..4049d224 100644 --- a/jpablog/src/main/java/com/example/jpablog/config/SecurityConfig.java +++ b/jpablog/src/main/java/com/example/jpablog/config/SecurityConfig.java @@ -1,5 +1,7 @@ package com.example.jpablog.config; +import com.example.jpablog.config.auth.PrincipalDetailService; +import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; @@ -10,22 +12,36 @@ import org.springframework.security.config.annotation.web.configuration.WebSecur import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; @Configuration // bean 등록 +@RequiredArgsConstructor @EnableWebSecurity // 시큐리티 필터 등록 @EnableGlobalMethodSecurity(prePostEnabled = true) // 특정 주소 접근시 먼저 권한 및 인증 체크 public class SecurityConfig extends WebSecurityConfigurerAdapter { + private final PrincipalDetailService principalDetailService; + @Bean public BCryptPasswordEncoder encodePWD() { return new BCryptPasswordEncoder(); } + // 시큐리티가 대신 로그인 할때 어떤 해시를 사용했는지 알아야 DB와 비교가능 + + + @Override + protected void configure(AuthenticationManagerBuilder auth) throws Exception { + auth.userDetailsService(principalDetailService).passwordEncoder(encodePWD()); + } + @Override protected void configure(HttpSecurity http) throws Exception { http - .csrf().disable() // csrf 토큰 비활성화 - .authorizeRequests() - .antMatchers("/", "/auth/**", "/js/**", "/css/**", "/images/**").permitAll() - .anyRequest().authenticated() - .and().formLogin().loginPage("/auth/loginForm"); + .csrf().disable() // csrf 토큰 비활성화 + .authorizeRequests() // 요청에 대한 인증 + .antMatchers("/", "/auth/**", "/js/**", "/css/**", "/images/**").permitAll() // 해당 주소로 들어오면 모두 허가 + .anyRequest().authenticated() // 나머지 요청은 모두 인증을 거쳐야 함 + .and() + .formLogin().loginPage("/auth/loginForm") // 로그인 폼 지정 + .loginProcessingUrl("/auth/loginProc") // 해당 주소로 들어오는 로그인 요청을 가로채서 스프링 시큐리티가 로그인 + .defaultSuccessUrl("/"); // 정상 로그인 완료 후 이동 URL } } diff --git a/jpablog/src/main/java/com/example/jpablog/config/auth/PrincipalDetail.java b/jpablog/src/main/java/com/example/jpablog/config/auth/PrincipalDetail.java new file mode 100644 index 00000000..fa98c682 --- /dev/null +++ b/jpablog/src/main/java/com/example/jpablog/config/auth/PrincipalDetail.java @@ -0,0 +1,58 @@ +package com.example.jpablog.config.auth; + +import com.example.jpablog.model.User; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; + +import java.util.ArrayDeque; +import java.util.Collection; + +public class PrincipalDetail implements UserDetails { + private final User user; + + public PrincipalDetail(User user) { + this.user = user; + } + + @Override + public String getPassword() { + return user.getPassword(); + } + + @Override + public String getUsername() { + return user.getUsername(); + } + + // 계정 만료 여부 ( false : 만료 ) + @Override + public boolean isAccountNonExpired() { + return true; + } + + // 계정이 잠금 여부 ( false : 잠김 ) + @Override + public boolean isAccountNonLocked() { + return true; + } + + // 비밀번호 만류 여부 ( false : 만료 ) + @Override + public boolean isCredentialsNonExpired() { + return true; + } + + // 계정 활성화 여부 ( false : 비활성화 ) + @Override + public boolean isEnabled() { + return true; + } + + // 계정의 권한 목록 리턴 + @Override + public Collection getAuthorities() { + Collection collections = new ArrayDeque<>(); + collections.add(() -> "ROLE_" + user.getRole()); + return collections; + } +} diff --git a/jpablog/src/main/java/com/example/jpablog/config/auth/PrincipalDetailService.java b/jpablog/src/main/java/com/example/jpablog/config/auth/PrincipalDetailService.java new file mode 100644 index 00000000..37f01af7 --- /dev/null +++ b/jpablog/src/main/java/com/example/jpablog/config/auth/PrincipalDetailService.java @@ -0,0 +1,27 @@ +package com.example.jpablog.config.auth; + +import com.example.jpablog.model.User; +import com.example.jpablog.repository.UserRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class PrincipalDetailService implements UserDetailsService { + + private final UserRepository userRepository; + + // 스프링이 로그인 요청시 username, password 변수 체크 + // password 는 스프링 시큐리티가 알아서 처리 + // username 은 DB에 있는지 체크 + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + User principal = userRepository.findByUsername(username) + .orElseThrow(() -> new UsernameNotFoundException("해당 사용자를 찾을 수 없습니다.")); + + return new PrincipalDetail(principal); // 시큐리티 세션에 유저 정보 저장 + } +} diff --git a/jpablog/src/main/java/com/example/jpablog/controller/BoardController.java b/jpablog/src/main/java/com/example/jpablog/controller/BoardController.java index 589c9d5b..f4206a9d 100644 --- a/jpablog/src/main/java/com/example/jpablog/controller/BoardController.java +++ b/jpablog/src/main/java/com/example/jpablog/controller/BoardController.java @@ -1,11 +1,14 @@ package com.example.jpablog.controller; +import com.example.jpablog.config.auth.PrincipalDetail; +import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; @Controller public class BoardController { + // 파라미터로 시큐리티 세션 접근 : @AuthenticationPrincipal PrincipalDetail principal @GetMapping("/") public String index() { return "index"; diff --git a/jpablog/src/main/java/com/example/jpablog/repository/UserRepository.java b/jpablog/src/main/java/com/example/jpablog/repository/UserRepository.java index 3e97251c..ee875dc4 100644 --- a/jpablog/src/main/java/com/example/jpablog/repository/UserRepository.java +++ b/jpablog/src/main/java/com/example/jpablog/repository/UserRepository.java @@ -5,10 +5,13 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.stereotype.Repository; +import java.util.Optional; + @Repository // 생략가능 public interface UserRepository extends JpaRepository { - + // select * from user where username =? + Optional findByUsername(String username); diff --git a/jpablog/src/main/resources/application.yml b/jpablog/src/main/resources/application.yml index c0c39ba5..07904d9e 100644 --- a/jpablog/src/main/resources/application.yml +++ b/jpablog/src/main/resources/application.yml @@ -22,7 +22,7 @@ spring: jpa: open-in-view: true hibernate: - ddl-auto: create + ddl-auto: update # naming: # physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl use-new-id-generator-mappings: false diff --git a/jpablog/src/main/webapp/WEB-INF/views/user/loginForm.jsp b/jpablog/src/main/webapp/WEB-INF/views/user/loginForm.jsp index dcc5e215..5be4256e 100644 --- a/jpablog/src/main/webapp/WEB-INF/views/user/loginForm.jsp +++ b/jpablog/src/main/webapp/WEB-INF/views/user/loginForm.jsp @@ -3,7 +3,7 @@ <%@include file="../layout/header.jsp"%>
-
+
@@ -12,11 +12,6 @@
-
- -