diff --git a/build.gradle b/build.gradle index 570ab26..12e2790 100644 --- a/build.gradle +++ b/build.gradle @@ -19,18 +19,19 @@ repositories { } dependencies { - compileOnly 'org.projectlombok:lombok:1.18.22' - annotationProcessor 'org.projectlombok:lombok:1.18.22' - developmentOnly 'org.springframework.boot:spring-boot-devtools:2.5.6' - implementation 'org.springframework.boot:spring-boot-starter-web:2.5.6' - implementation 'org.springframework.boot:spring-boot-starter-thymeleaf:2.5.6' - implementation 'org.springframework.boot:spring-boot-starter-data-jpa:2.5.6' + compileOnly 'org.projectlombok:lombok' + annotationProcessor 'org.projectlombok:lombok' + implementation 'org.springframework.boot:spring-boot-starter-web' + developmentOnly 'org.springframework.boot:spring-boot-devtools' + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' + implementation group: 'nz.net.ultraq.thymeleaf', name: 'thymeleaf-layout-dialect', version: '2.5.3' - implementation 'org.springframework.boot:spring-boot-starter-security:2.5.6' - implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5:3.0.4.RELEASE' + implementation 'org.springframework.boot:spring-boot-starter-security' + implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5' // implementation 'io.jsonwebtoken:jjwt:0.9.1' - implementation group: 'org.mariadb.jdbc', name: 'mariadb-java-client', version: '2.7.0' + implementation group: 'org.mariadb.jdbc', name: 'mariadb-java-client', version: '2.7.4' implementation 'org.bgee.log4jdbc-log4j2:log4jdbc-log4j2-jdbc4.1:1.16' // implementation group: 'org.webjars', name: 'bootstrap', version: '5.1.3' diff --git a/src/main/java/com/dbnt/kcgfilemanager/BaseController.java b/src/main/java/com/dbnt/kcgfilemanager/BaseController.java index 150687c..aedd821 100644 --- a/src/main/java/com/dbnt/kcgfilemanager/BaseController.java +++ b/src/main/java/com/dbnt/kcgfilemanager/BaseController.java @@ -6,8 +6,9 @@ import org.springframework.web.bind.annotation.GetMapping; @Controller public class BaseController { + /** * 메인 페이지 이동 * @return */ @GetMapping("/") - public String home(){ - return "login"; + public String main() { + return "index"; } } diff --git a/src/main/java/com/dbnt/kcgfilemanager/config/MvcConfig.java b/src/main/java/com/dbnt/kcgfilemanager/config/MvcConfig.java deleted file mode 100644 index b1ec15d..0000000 --- a/src/main/java/com/dbnt/kcgfilemanager/config/MvcConfig.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.dbnt.kcgfilemanager.config; - -import org.springframework.context.annotation.Configuration; -import org.springframework.core.Ordered; -import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; -import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; - -@Configuration -public class MvcConfig implements WebMvcConfigurer { - - private static final String[] CLASSPATH_RESOURCE_LOCATIONS = { - "classpath:/static/", "classpath:/public/", "classpath:/", "classpath:/resources/", - "classpath:/META-INF/resources/", "classpath:/META-INF/resources/webjars/"}; - - @Override - public void addViewControllers(ViewControllerRegistry registry){ - registry.addViewController("/").setViewName("forward:/index"); - - registry.setOrder(Ordered.HIGHEST_PRECEDENCE); - } - - @Override - public void addResourceHandlers(ResourceHandlerRegistry registry){ - registry.addResourceHandler("/**").addResourceLocations(CLASSPATH_RESOURCE_LOCATIONS); - } -} diff --git a/src/main/java/com/dbnt/kcgfilemanager/config/Role.java b/src/main/java/com/dbnt/kcgfilemanager/config/Role.java new file mode 100644 index 0000000..0bf3310 --- /dev/null +++ b/src/main/java/com/dbnt/kcgfilemanager/config/Role.java @@ -0,0 +1,13 @@ +package com.dbnt.kcgfilemanager.config; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor +@Getter +public enum Role { + ADMIN("ROLE_ADMIN"), + USER("ROLE_USER"); + + private String value; +} diff --git a/src/main/java/com/dbnt/kcgfilemanager/config/SecurityConfig.java b/src/main/java/com/dbnt/kcgfilemanager/config/SecurityConfig.java new file mode 100644 index 0000000..ca0e78b --- /dev/null +++ b/src/main/java/com/dbnt/kcgfilemanager/config/SecurityConfig.java @@ -0,0 +1,57 @@ +package com.dbnt.kcgfilemanager.config; + +import com.dbnt.kcgfilemanager.userInfo.service.UserInfoService; +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.builders.WebSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.crypto.password.Pbkdf2PasswordEncoder; +import org.springframework.security.web.util.matcher.AntPathRequestMatcher; + +@Configuration +@EnableWebSecurity +@RequiredArgsConstructor +public class SecurityConfig extends WebSecurityConfigurerAdapter { + + private final UserInfoService userInfoService; + + @Bean + public PasswordEncoder passwordEncoder(){ + return new Pbkdf2PasswordEncoder(); + } + + @Override + public void configure(WebSecurity web) throws Exception { + web.ignoring().antMatchers("/css/**", "/img/**", "/js/**", "/lib/**", "/vendor/**"); + } + + @Override + protected void configure(HttpSecurity http) throws Exception { + http.authorizeRequests() // 페이지 권한 설정 + .antMatchers("/info").hasRole("MEMBER") // MEMBER, ADMIN만 접근 허용 + .antMatchers("/admin").hasRole("ADMIN") // ADMIN만 접근 허용 + .antMatchers("/**").permitAll() // 그외 모든 경로에 대해서는 권한 없이 접근 허용 + // .anyRequest().authenticated() // 나머지 요청들은 권한의 종류에 상관 없이 권한이 있어야 접근 가능 + .and() // 로그인 설정 + .formLogin() .loginPage("/user/login") // Custom login form 사용 + .failureUrl("/login-error") // 로그인 실패 시 이동 + .defaultSuccessUrl("/") // 로그인 성공 시 redirect 이동 + .and() // 로그아웃 설정 + .logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout")) // 로그아웃 시 URL 재정의 + .logoutSuccessUrl("/") // 로그아웃 성공 시 redirect 이동 + .invalidateHttpSession(true) // HTTP Session 초기화 + .deleteCookies("JSESSIONID") // 특정 쿠키 제거 + .and() // 403 예외처리 핸들링 + .exceptionHandling().accessDeniedPage("/denied"); + } + + @Override + public void configure(AuthenticationManagerBuilder auth) throws Exception{ + auth.userDetailsService(userInfoService).passwordEncoder(passwordEncoder()); + } +} diff --git a/src/main/java/com/dbnt/kcgfilemanager/config/ThymeleafViewResolverConfig.java b/src/main/java/com/dbnt/kcgfilemanager/config/ThymeleafViewResolverConfig.java new file mode 100644 index 0000000..538ed32 --- /dev/null +++ b/src/main/java/com/dbnt/kcgfilemanager/config/ThymeleafViewResolverConfig.java @@ -0,0 +1,48 @@ +package com.dbnt.kcgfilemanager.config; + +import lombok.RequiredArgsConstructor; +import nz.net.ultraq.thymeleaf.LayoutDialect; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.thymeleaf.extras.springsecurity5.dialect.SpringSecurityDialect; +import org.thymeleaf.spring5.SpringTemplateEngine; +import org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver; +import org.thymeleaf.spring5.view.ThymeleafViewResolver; + +@Configuration +@RequiredArgsConstructor +public class ThymeleafViewResolverConfig { + + private final ApplicationContext applicationContext; + + @Bean + public SpringResourceTemplateResolver templateResolver(){ + SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver(); + templateResolver.setApplicationContext(this.applicationContext); + templateResolver.setPrefix("classpath:templates/"); + templateResolver.setSuffix(".html"); + templateResolver.setTemplateMode("HTML5"); + templateResolver.setCacheable(false); + return templateResolver; + } + + @Bean + public SpringTemplateEngine templateEngine() { + SpringTemplateEngine templateEngine = new SpringTemplateEngine(); + templateEngine.setTemplateResolver(templateResolver()); + templateEngine.addDialect(new SpringSecurityDialect()); + templateEngine.addDialect(new LayoutDialect()); + return templateEngine; + } + + @Bean + public ThymeleafViewResolver viewResolver() { + ThymeleafViewResolver viewResolver = new ThymeleafViewResolver(); + viewResolver.setTemplateEngine(templateEngine()); + viewResolver.setCharacterEncoding("UTF-8"); + viewResolver.setOrder(0); + return viewResolver; + } + +} diff --git a/src/main/java/com/dbnt/kcgfilemanager/config/WebSecurityConfig.java b/src/main/java/com/dbnt/kcgfilemanager/config/WebSecurityConfig.java deleted file mode 100644 index 72eef7c..0000000 --- a/src/main/java/com/dbnt/kcgfilemanager/config/WebSecurityConfig.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.dbnt.kcgfilemanager.config; - -import com.dbnt.kcgfilemanager.userInfo.service.UserInfoService; -import lombok.RequiredArgsConstructor; -import org.springframework.boot.autoconfigure.security.servlet.PathRequest; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.config.BeanIds; -import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.builders.WebSecurity; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; -import org.springframework.security.config.http.SessionCreationPolicy; -import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.security.crypto.password.Pbkdf2PasswordEncoder; -import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; - -@RequiredArgsConstructor -@EnableWebSecurity -@Configuration -public class WebSecurityConfig extends WebSecurityConfigurerAdapter { - - private final UserInfoService userInfoService; - - @Bean - public PasswordEncoder passwordEncoder(){ - return new Pbkdf2PasswordEncoder(); - } - @Override - protected void configure(AuthenticationManagerBuilder auth) throws Exception { - auth.userDetailsService(userInfoService); - } - @Bean(name = BeanIds.AUTHENTICATION_MANAGER) - @Override - public AuthenticationManager authenticationManagerBean() throws Exception { - return super.authenticationManagerBean(); - } - @Override - protected void configure(HttpSecurity http) throws Exception { - http.csrf().disable().authorizeRequests().antMatchers("/authenticate") - .permitAll().anyRequest().authenticated() - .and().exceptionHandling().and().sessionManagement() - .sessionCreationPolicy(SessionCreationPolicy.STATELESS); - } -} diff --git a/src/main/java/com/dbnt/kcgfilemanager/userInfo/UserInfo.java b/src/main/java/com/dbnt/kcgfilemanager/userInfo/UserInfo.java index 474014e..8e60211 100644 --- a/src/main/java/com/dbnt/kcgfilemanager/userInfo/UserInfo.java +++ b/src/main/java/com/dbnt/kcgfilemanager/userInfo/UserInfo.java @@ -1,8 +1,6 @@ package com.dbnt.kcgfilemanager.userInfo; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; +import lombok.*; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; @@ -13,31 +11,60 @@ import java.util.Date; import java.util.HashSet; import java.util.Set; -@Data -@AllArgsConstructor +@Getter +@Setter @NoArgsConstructor @Entity @Table(name = "USER_INFO") -public class UserInfo{ +public class UserInfo implements UserDetails{ @Id - @GeneratedValue(strategy = GenerationType.SEQUENCE) - @Column(name = "user_seq", nullable = false) - private Integer userSeq; - + @Column(name = "USER_ID") private String userId; + @Column(name = "PASSWORD") private String password; + @Column(name = "NAME") private String name; + @Column(name = "POSITION") private int position; + @Column(name = "DEPARTMENT") private int department; + @Column(name = "USER_ROLE") private String userRole; + @Column(name = "CREATE_DATE") private Date createDate; - public Integer getUserSeq() { - return userSeq; + + @Override + public Collection getAuthorities() { + Set roles = new HashSet<>(); + for (String role : userRole.split(",")) { + roles.add(new SimpleGrantedAuthority(role)); + } + return roles; } - public void setUserSeq(Integer userSeq) { - this.userSeq = userSeq; + @Override + public String getUsername() { + return userId; } + @Override + public boolean isAccountNonExpired() { + return true; + } + + @Override + public boolean isAccountNonLocked() { + return true; + } + + @Override + public boolean isCredentialsNonExpired() { + return true; + } + + @Override + public boolean isEnabled() { + return true; + } } diff --git a/src/main/java/com/dbnt/kcgfilemanager/userInfo/controller/UserInfoController.java b/src/main/java/com/dbnt/kcgfilemanager/userInfo/controller/UserInfoController.java index 178a0e2..235d4aa 100644 --- a/src/main/java/com/dbnt/kcgfilemanager/userInfo/controller/UserInfoController.java +++ b/src/main/java/com/dbnt/kcgfilemanager/userInfo/controller/UserInfoController.java @@ -6,26 +6,59 @@ import lombok.RequiredArgsConstructor; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler; import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -@RequiredArgsConstructor @Controller +@RequiredArgsConstructor public class UserInfoController { private final UserInfoService userInfoService; - @PostMapping("/user") - public String signup(UserInfo userInfo){ - userInfoService.signup(userInfo); - return "redirect:/login"; + /** * 로그인 페이지 이동 * @return */ + @GetMapping("/user/login") + public String goLogin() { + return "login/login"; } - @GetMapping("/logout") - public String logoutPage(HttpServletRequest request, HttpServletResponse response){ - new SecurityContextLogoutHandler().logout(request, response, SecurityContextHolder.getContext().getAuthentication()); - return "redirect:/login"; + /** * 로그인 에러 * @param model * @return */ + @GetMapping("/login-error") + public String loginError(Model model) { + model.addAttribute("loginError", true); + return "/login/login"; + } + + /** * 회원가입 페이지 이동 * @return */ + @GetMapping("/signup") + public String goSignup() { + return "login/signup"; + } + + /** * 회원가입 처리 * @param memberDto * @return */ + @PostMapping("/signup") + public String signup(UserInfo userInfo) { + userInfoService.signup(userInfo); + return "redirect:/user/login"; + } + + /** * 접근 거부 페이지 이동 * @return */ + @GetMapping("/denied") + public String doDenied() { + return "login/denied"; + } + + /** * 내 정보 페이지 이동 * @return */ + @GetMapping("/info") + public String goMyInfo() { + return "login/myinfo"; + } + + /** * Admin 페이지 이동 * @return */ + @GetMapping("/admin") + public String goAdmin() { + return "login/admin"; } } diff --git a/src/main/java/com/dbnt/kcgfilemanager/userInfo/service/UserInfoService.java b/src/main/java/com/dbnt/kcgfilemanager/userInfo/service/UserInfoService.java index e301ff5..b4954c2 100644 --- a/src/main/java/com/dbnt/kcgfilemanager/userInfo/service/UserInfoService.java +++ b/src/main/java/com/dbnt/kcgfilemanager/userInfo/service/UserInfoService.java @@ -19,16 +19,15 @@ public class UserInfoService implements UserDetailsService { private final UserInfoRepository userInfoRepository; @Transactional - public int signup(UserInfo userInfo){ + public String signup(UserInfo userInfo){ Pbkdf2PasswordEncoder passwordEncoder = new Pbkdf2PasswordEncoder(); userInfo.setPassword(passwordEncoder.encode(userInfo.getPassword())); - return userInfoRepository.save(userInfo).getUserSeq(); + return userInfoRepository.save(userInfo).getUserId(); } @Override public UserDetails loadUserByUsername(String userId) throws UsernameNotFoundException { - UserInfo userInfo = userInfoRepository.findByUserId(userId).orElseThrow(()->new UsernameNotFoundException(userId)); - return new User(userInfo.getUserId(), userInfo.getPassword(), new ArrayList<>()); + return userInfoRepository.findByUserId(userId).orElseThrow(() -> new UsernameNotFoundException(userId)); } } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 004ac8f..67886a8 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,3 +1,4 @@ + spring.jpa.show-sql=true spring.jpa.generate-ddl=false diff --git a/src/main/resources/templates/fragments/footer.html b/src/main/resources/templates/fragments/footer.html new file mode 100644 index 0000000..b1ed95e --- /dev/null +++ b/src/main/resources/templates/fragments/footer.html @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/src/main/resources/templates/fragments/header.html b/src/main/resources/templates/fragments/header.html new file mode 100644 index 0000000..8956b29 --- /dev/null +++ b/src/main/resources/templates/fragments/header.html @@ -0,0 +1,8 @@ + + +
+
+ header.html 입니다. +
+
+ \ No newline at end of file diff --git a/src/main/resources/templates/fragments/leftMenu.html b/src/main/resources/templates/fragments/leftMenu.html new file mode 100644 index 0000000..813dd4e --- /dev/null +++ b/src/main/resources/templates/fragments/leftMenu.html @@ -0,0 +1,8 @@ + + +
+
+ menu 영역입니다. +
+
+ \ No newline at end of file diff --git a/src/main/resources/templates/index.html b/src/main/resources/templates/index.html new file mode 100644 index 0000000..386ff7d --- /dev/null +++ b/src/main/resources/templates/index.html @@ -0,0 +1,24 @@ + + +
+

This is Main Page.

+
+
+ 님 환영합니다. +
+ + 로그인 + 회원가입 + + + 로그아웃 + + + 내정보 + 어드민 +
+ \ No newline at end of file diff --git a/src/main/resources/templates/layout/layout.html b/src/main/resources/templates/layout/layout.html new file mode 100644 index 0000000..7821fe8 --- /dev/null +++ b/src/main/resources/templates/layout/layout.html @@ -0,0 +1,37 @@ + + + + + 해양경찰청 파일관리 시스템 + + + + + + + + + + + + + + + + +
+
+
+
+
+
+
+
+
+
+
+
+ + \ No newline at end of file diff --git a/src/main/resources/templates/login/admin.html b/src/main/resources/templates/login/admin.html new file mode 100644 index 0000000..96e523e --- /dev/null +++ b/src/main/resources/templates/login/admin.html @@ -0,0 +1,11 @@ + + + + + Admin + + +

This is Admin Page.

+
+ + \ No newline at end of file diff --git a/src/main/resources/templates/login/denied.html b/src/main/resources/templates/login/denied.html new file mode 100644 index 0000000..5a23f86 --- /dev/null +++ b/src/main/resources/templates/login/denied.html @@ -0,0 +1,11 @@ + + + + + denied + + +

This is denied Page.

+
+ + \ No newline at end of file diff --git a/src/main/resources/templates/login/login.html b/src/main/resources/templates/login/login.html new file mode 100644 index 0000000..2ae23f9 --- /dev/null +++ b/src/main/resources/templates/login/login.html @@ -0,0 +1,22 @@ + + + + + Login + + +

This is Login Page.

+
+ +
+ + +

Wrong user or password

+ + + + + +
+ + \ No newline at end of file diff --git a/src/main/resources/templates/login/myInfo.html b/src/main/resources/templates/login/myInfo.html new file mode 100644 index 0000000..cae0fb3 --- /dev/null +++ b/src/main/resources/templates/login/myInfo.html @@ -0,0 +1,11 @@ + + + + + MyInfo + + +

This is MyInfo Page.

+
+ + \ No newline at end of file diff --git a/src/main/resources/templates/login/signup.html b/src/main/resources/templates/login/signup.html new file mode 100644 index 0000000..869e022 --- /dev/null +++ b/src/main/resources/templates/login/signup.html @@ -0,0 +1,20 @@ + + + + + Signup + + +

This is Signup Page.

+
+ +
+ + + admin + + member
+ +
+ + \ No newline at end of file