From c5eb7ef8956447f53e4f410b5b12da15d61d46fc Mon Sep 17 00:00:00 2001 From: jinia91 Date: Sun, 7 Nov 2021 23:51:36 +0900 Subject: [PATCH] =?UTF-8?q?211107=20=EB=A9=94=EC=9D=B8=20=ED=99=94?= =?UTF-8?q?=EB=A9=B4=20=EC=99=80=EC=9D=B4=EC=96=B4=ED=94=84=EB=A0=88?= =?UTF-8?q?=EC=9E=84,=20=EB=AA=A9=EC=97=85=20=EA=B0=9C=EB=B0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/myblog/blog/BlogApplication.java | 2 + .../article/controller/ArticleController.java | 9 + .../myblog/blog/article/domain/Article.java | 33 ++ .../article/repository/ArticleRepository.java | 11 + .../blog/article/service/ArticleService.java | 29 ++ .../blog/article/service/NewArticleDto.java | 18 + .../{ => base}/config/SecurityConfig.java | 28 +- .../myblog/blog/base/domain/BasicEntity.java | 24 ++ .../exception/DuplicateEmailException.java | 24 -- .../blog/exception/ExceptionController.java | 23 ++ .../blog/exception/LoginFailHandler.java | 34 ++ .../blog/{ => main}/MainController.java | 8 +- .../blog/member/auth/PrincipalDetails.java | 8 +- .../blog/member/auth/UserInfoFactory.java | 4 +- .../member/controller/MemberController.java | 23 ++ .../myblog/blog/member/doamin/Member.java | 11 +- .../java/myblog/blog/member/doamin/Role.java | 10 +- .../member/service/Oauth2MemberService.java | 5 +- src/main/resources/static/css/login.css | 58 +++ src/main/resources/static/css/mainCss.css | 333 ++++++++++++++++++ src/main/resources/static/img/facebook.svg | 10 + src/main/resources/static/img/google.svg | 23 ++ src/main/resources/static/img/kakao.png | Bin 0 -> 2946 bytes src/main/resources/static/img/naver.png | Bin 0 -> 11892 bytes src/main/resources/templates/index.html | 24 -- .../resources/templates/layout/layout.html | 294 ++++++++++++++++ src/main/resources/templates/login.html | 54 +++ src/main/resources/templates/main.html | 332 +++++++++++++++++ .../java/myblog/blog/MainControllerTest.java | 33 ++ .../service/Oauth2MemberServiceTest.java | 37 ++ 30 files changed, 1429 insertions(+), 73 deletions(-) create mode 100644 src/main/java/myblog/blog/article/controller/ArticleController.java create mode 100644 src/main/java/myblog/blog/article/domain/Article.java create mode 100644 src/main/java/myblog/blog/article/repository/ArticleRepository.java create mode 100644 src/main/java/myblog/blog/article/service/ArticleService.java create mode 100644 src/main/java/myblog/blog/article/service/NewArticleDto.java rename src/main/java/myblog/blog/{ => base}/config/SecurityConfig.java (54%) create mode 100644 src/main/java/myblog/blog/base/domain/BasicEntity.java delete mode 100644 src/main/java/myblog/blog/exception/DuplicateEmailException.java create mode 100644 src/main/java/myblog/blog/exception/ExceptionController.java create mode 100644 src/main/java/myblog/blog/exception/LoginFailHandler.java rename src/main/java/myblog/blog/{ => main}/MainController.java (59%) create mode 100644 src/main/java/myblog/blog/member/controller/MemberController.java create mode 100644 src/main/resources/static/css/login.css create mode 100644 src/main/resources/static/css/mainCss.css create mode 100644 src/main/resources/static/img/facebook.svg create mode 100644 src/main/resources/static/img/google.svg create mode 100644 src/main/resources/static/img/kakao.png create mode 100644 src/main/resources/static/img/naver.png delete mode 100644 src/main/resources/templates/index.html create mode 100644 src/main/resources/templates/layout/layout.html create mode 100644 src/main/resources/templates/login.html create mode 100644 src/main/resources/templates/main.html create mode 100644 src/test/java/myblog/blog/MainControllerTest.java create mode 100644 src/test/java/myblog/blog/member/service/Oauth2MemberServiceTest.java diff --git a/src/main/java/myblog/blog/BlogApplication.java b/src/main/java/myblog/blog/BlogApplication.java index d5d40b0..e86eca6 100644 --- a/src/main/java/myblog/blog/BlogApplication.java +++ b/src/main/java/myblog/blog/BlogApplication.java @@ -2,8 +2,10 @@ package myblog.blog; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.data.jpa.repository.config.EnableJpaAuditing; @SpringBootApplication +@EnableJpaAuditing public class BlogApplication { public static void main(String[] args) { diff --git a/src/main/java/myblog/blog/article/controller/ArticleController.java b/src/main/java/myblog/blog/article/controller/ArticleController.java new file mode 100644 index 0000000..898660f --- /dev/null +++ b/src/main/java/myblog/blog/article/controller/ArticleController.java @@ -0,0 +1,9 @@ +package myblog.blog.article.controller; + +import org.springframework.stereotype.Controller; + +@Controller +public class ArticleController { + + +} diff --git a/src/main/java/myblog/blog/article/domain/Article.java b/src/main/java/myblog/blog/article/domain/Article.java new file mode 100644 index 0000000..4442226 --- /dev/null +++ b/src/main/java/myblog/blog/article/domain/Article.java @@ -0,0 +1,33 @@ +package myblog.blog.article.domain; + +import lombok.Getter; +import myblog.blog.member.doamin.Member; + +import javax.persistence.*; + +@Entity +@Getter +@SequenceGenerator( + name = "article_seq_generator", + sequenceName = "article_seq", + initialValue = 1, allocationSize = 50) +public class Article { + + @Id + @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "article_generator") + @Column(name = "article_id") + private Long id; + + @Column(nullable = false) + private String title; + @Column(nullable = false) + private String content; + private Long hit; + private String toc; + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "member_id") + private Member member; + + + +} diff --git a/src/main/java/myblog/blog/article/repository/ArticleRepository.java b/src/main/java/myblog/blog/article/repository/ArticleRepository.java new file mode 100644 index 0000000..e335067 --- /dev/null +++ b/src/main/java/myblog/blog/article/repository/ArticleRepository.java @@ -0,0 +1,11 @@ +package myblog.blog.article.repository; + +import myblog.blog.article.domain.Article; +import org.springframework.data.jpa.repository.JpaRepository; + + +public interface ArticleRepository extends JpaRepository { + + + +} diff --git a/src/main/java/myblog/blog/article/service/ArticleService.java b/src/main/java/myblog/blog/article/service/ArticleService.java new file mode 100644 index 0000000..5370388 --- /dev/null +++ b/src/main/java/myblog/blog/article/service/ArticleService.java @@ -0,0 +1,29 @@ +package myblog.blog.article.service; + +import lombok.RequiredArgsConstructor; +import myblog.blog.article.domain.Article; +import myblog.blog.article.repository.ArticleRepository; +import org.modelmapper.ModelMapper; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class ArticleService { + + private final ArticleRepository articleRepository; + private final ModelMapper modelMapper; + + public Long writeArticle(NewArticleDto newArticleDto){ + + Article article = modelMapper.map(newArticleDto, Article.class); + + articleRepository.save(article); + + return article.getId(); + + } + + + + +} diff --git a/src/main/java/myblog/blog/article/service/NewArticleDto.java b/src/main/java/myblog/blog/article/service/NewArticleDto.java new file mode 100644 index 0000000..4b717ea --- /dev/null +++ b/src/main/java/myblog/blog/article/service/NewArticleDto.java @@ -0,0 +1,18 @@ +package myblog.blog.article.service; + +import myblog.blog.member.doamin.Member; + +import javax.persistence.*; +import javax.validation.constraints.NotBlank; + +public class NewArticleDto { + + @NotBlank + private String title; + @NotBlank + private String content; + private String toc; + @NotBlank + private Long memberId; + +} diff --git a/src/main/java/myblog/blog/config/SecurityConfig.java b/src/main/java/myblog/blog/base/config/SecurityConfig.java similarity index 54% rename from src/main/java/myblog/blog/config/SecurityConfig.java rename to src/main/java/myblog/blog/base/config/SecurityConfig.java index 8198f1f..a376ec5 100644 --- a/src/main/java/myblog/blog/config/SecurityConfig.java +++ b/src/main/java/myblog/blog/base/config/SecurityConfig.java @@ -1,9 +1,13 @@ -package myblog.blog.config; +package myblog.blog.base.config; import lombok.RequiredArgsConstructor; +import myblog.blog.exception.LoginFailHandler; +import myblog.blog.member.doamin.Role; import myblog.blog.member.service.Oauth2MemberService; +import org.springframework.boot.autoconfigure.security.servlet.PathRequest; import org.springframework.context.annotation.Configuration; 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; @@ -13,32 +17,40 @@ import org.springframework.security.config.annotation.web.configuration.WebSecur public class SecurityConfig extends WebSecurityConfigurerAdapter { private final Oauth2MemberService oauth2MemberService; + private final LoginFailHandler loginFailHandler; + @Override + public void configure(WebSecurity web) throws Exception { + web + .ignoring() + .antMatchers("/css/**", "/node_modules/**") + .requestMatchers(PathRequest.toStaticResources().atCommonLocations()); + } @Override protected void configure(HttpSecurity http) throws Exception { http - .csrf().disable() - .authorizeRequests() - .antMatchers("/user").authenticated() - .antMatchers("/admin").access("hasRole('ROLE_ADMIN')") - .anyRequest() - .permitAll() + .antMatchers("/admin").hasRole(Role.ADMIN.name()) + .anyRequest().permitAll() .and() .formLogin() + .loginPage("/login") .and() .logout() .logoutSuccessUrl("/") + .deleteCookies("JSESSIONID","remember-me") + .and() .oauth2Login() + .loginPage("/login") + .failureHandler(loginFailHandler) .userInfoEndpoint() .userService(oauth2MemberService) - ; } } diff --git a/src/main/java/myblog/blog/base/domain/BasicEntity.java b/src/main/java/myblog/blog/base/domain/BasicEntity.java new file mode 100644 index 0000000..818f847 --- /dev/null +++ b/src/main/java/myblog/blog/base/domain/BasicEntity.java @@ -0,0 +1,24 @@ +package myblog.blog.base.domain; + +import lombok.Getter; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.LastModifiedDate; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; + +import javax.persistence.Column; +import javax.persistence.EntityListeners; +import javax.persistence.MappedSuperclass; +import java.time.LocalDateTime; + +@EntityListeners(AuditingEntityListener.class) +@MappedSuperclass +@Getter +public abstract class BasicEntity { + + @CreatedDate + @Column(updatable = false) + private LocalDateTime createdDate; + @LastModifiedDate + private LocalDateTime updatedDate; + +} diff --git a/src/main/java/myblog/blog/exception/DuplicateEmailException.java b/src/main/java/myblog/blog/exception/DuplicateEmailException.java deleted file mode 100644 index 3d1dd02..0000000 --- a/src/main/java/myblog/blog/exception/DuplicateEmailException.java +++ /dev/null @@ -1,24 +0,0 @@ -package myblog.blog.exception; - -public class DuplicateEmailException extends RuntimeException { - - public DuplicateEmailException() { - super("이미 가입한 이메일입니다."); - } - - public DuplicateEmailException(String message) { - super(message); - } - - public DuplicateEmailException(String message, Throwable cause) { - super(message, cause); - } - - public DuplicateEmailException(Throwable cause) { - super(cause); - } - - protected DuplicateEmailException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { - super(message, cause, enableSuppression, writableStackTrace); - } -} diff --git a/src/main/java/myblog/blog/exception/ExceptionController.java b/src/main/java/myblog/blog/exception/ExceptionController.java new file mode 100644 index 0000000..5e4ac64 --- /dev/null +++ b/src/main/java/myblog/blog/exception/ExceptionController.java @@ -0,0 +1,23 @@ +package myblog.blog.exception; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.ControllerAdvice; + +@ControllerAdvice +@Slf4j +public class ExceptionController { + +// @ExceptionHandler +// public String handleRuntimeException(Principal principal, HttpServletRequest req, RuntimeException e) { +// if (principal != null) { +// log.info("'{}' requested '{}' ", principal.getName(), req.getRequestURI()); +// } else { +// log.info("requested '{}'", req.getRequestURI()); +// } +// +// log.error("bad request", e); +// return ""; +// } + + +} diff --git a/src/main/java/myblog/blog/exception/LoginFailHandler.java b/src/main/java/myblog/blog/exception/LoginFailHandler.java new file mode 100644 index 0000000..510231d --- /dev/null +++ b/src/main/java/myblog/blog/exception/LoginFailHandler.java @@ -0,0 +1,34 @@ +package myblog.blog.exception; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.authentication.AuthenticationServiceException; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.oauth2.core.OAuth2AuthenticationException; +import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler; +import org.springframework.stereotype.Component; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +@Component +@Slf4j +public class LoginFailHandler extends SimpleUrlAuthenticationFailureHandler { + + @Override + public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException { + + String errMsg = "error"; + + if(exception instanceof OAuth2AuthenticationException){ + errMsg = "duplicatedEmail"; + request.setAttribute("errMsg", errMsg); + + } + + setDefaultFailureUrl("/login?error="+errMsg); + super.onAuthenticationFailure(request, response, exception); + } +} diff --git a/src/main/java/myblog/blog/MainController.java b/src/main/java/myblog/blog/main/MainController.java similarity index 59% rename from src/main/java/myblog/blog/MainController.java rename to src/main/java/myblog/blog/main/MainController.java index a8bc15c..4b3a13f 100644 --- a/src/main/java/myblog/blog/MainController.java +++ b/src/main/java/myblog/blog/main/MainController.java @@ -1,21 +1,17 @@ -package myblog.blog; +package myblog.blog.main; -import lombok.Getter; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.ResponseBody; @Controller @RequiredArgsConstructor public class MainController { - @GetMapping("/") public String main() { - return "index"; + return "main"; } diff --git a/src/main/java/myblog/blog/member/auth/PrincipalDetails.java b/src/main/java/myblog/blog/member/auth/PrincipalDetails.java index b27f70d..81eda5d 100644 --- a/src/main/java/myblog/blog/member/auth/PrincipalDetails.java +++ b/src/main/java/myblog/blog/member/auth/PrincipalDetails.java @@ -2,9 +2,13 @@ package myblog.blog.member.auth; import myblog.blog.member.doamin.Member; +import myblog.blog.member.doamin.Role; import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.oauth2.core.user.OAuth2User; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Map; @@ -27,7 +31,9 @@ public class PrincipalDetails implements OAuth2User { @Override public Collection getAuthorities() { - return null; + Collection authorities = new ArrayList<>(); + authorities.add(new SimpleGrantedAuthority(member.getRole().getValue())); + return authorities; } @Override diff --git a/src/main/java/myblog/blog/member/auth/UserInfoFactory.java b/src/main/java/myblog/blog/member/auth/UserInfoFactory.java index 6eef9cb..2dc4d33 100644 --- a/src/main/java/myblog/blog/member/auth/UserInfoFactory.java +++ b/src/main/java/myblog/blog/member/auth/UserInfoFactory.java @@ -1,6 +1,7 @@ package myblog.blog.member.auth; import myblog.blog.member.auth.userinfo.*; +import org.springframework.security.access.AccessDeniedException; import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest; import org.springframework.security.oauth2.core.user.OAuth2User; import org.springframework.stereotype.Component; @@ -19,7 +20,8 @@ public class UserInfoFactory { } else if (oAuth2UserRequest.getClientRegistration().getRegistrationId().equals("naver")) { return new NaverUserInfo(oAuth2User.getAttribute("response")); } - else return null; + else { + throw new IllegalArgumentException("지원하지 않는 Oauth 인증 시도입니다");} } diff --git a/src/main/java/myblog/blog/member/controller/MemberController.java b/src/main/java/myblog/blog/member/controller/MemberController.java new file mode 100644 index 0000000..79eb4eb --- /dev/null +++ b/src/main/java/myblog/blog/member/controller/MemberController.java @@ -0,0 +1,23 @@ +package myblog.blog.member.controller; + +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; + +@Controller +public class MemberController { + + @GetMapping("/login") + public String loginFrom(@RequestParam(value = "error",required = false) String error, Model model){ + + if(error!=null&&error.equals("duplicatedEmail")){ + model.addAttribute("errMsg","이미 가입된 이메일입니다."); + } + + return "login"; + + } + + +} diff --git a/src/main/java/myblog/blog/member/doamin/Member.java b/src/main/java/myblog/blog/member/doamin/Member.java index cd17056..58310dc 100644 --- a/src/main/java/myblog/blog/member/doamin/Member.java +++ b/src/main/java/myblog/blog/member/doamin/Member.java @@ -2,8 +2,11 @@ package myblog.blog.member.doamin; import lombok.Builder; import lombok.Getter; +import myblog.blog.article.domain.Article; +import myblog.blog.base.domain.BasicEntity; import javax.persistence.*; +import java.util.List; @Entity @SequenceGenerator( @@ -11,10 +14,11 @@ import javax.persistence.*; sequenceName = "MEMBER_SEQ", initialValue = 1, allocationSize = 50) @Getter -public class Member { +public class Member extends BasicEntity { @Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "MEMBER_SEQ_GENERATOR") + @Column(name = "member_id") private Long id; @Column(nullable = false) @@ -23,7 +27,7 @@ public class Member { @Column(nullable = false) private String userId; - @Column(nullable = false) + @Column(nullable = false, unique = true) private String email; private String picUrl; @@ -35,6 +39,9 @@ public class Member { private String providerId; + @OneToMany(mappedBy = "member") + private List
articles; + protected Member() { } diff --git a/src/main/java/myblog/blog/member/doamin/Role.java b/src/main/java/myblog/blog/member/doamin/Role.java index dad9467..c7f8c65 100644 --- a/src/main/java/myblog/blog/member/doamin/Role.java +++ b/src/main/java/myblog/blog/member/doamin/Role.java @@ -3,12 +3,12 @@ package myblog.blog.member.doamin; import lombok.Getter; import lombok.RequiredArgsConstructor; -@Getter @RequiredArgsConstructor +@Getter public enum Role { - USER("ROLE_USER","일반사용자"), - ADMIN("ROLE_ADMIN","관리자"); + USER("ROLE_USER"), + ADMIN("ROLE_ADMIN"); + + private final String value; - private final String key; - private final String title; } diff --git a/src/main/java/myblog/blog/member/service/Oauth2MemberService.java b/src/main/java/myblog/blog/member/service/Oauth2MemberService.java index 05c061d..4d84996 100644 --- a/src/main/java/myblog/blog/member/service/Oauth2MemberService.java +++ b/src/main/java/myblog/blog/member/service/Oauth2MemberService.java @@ -1,7 +1,6 @@ package myblog.blog.member.service; import lombok.RequiredArgsConstructor; -import myblog.blog.exception.DuplicateEmailException; import myblog.blog.member.auth.userinfo.Oauth2UserInfo; import myblog.blog.member.auth.UserInfoFactory; import myblog.blog.member.repository.MemberRepository; @@ -25,6 +24,7 @@ public class Oauth2MemberService extends DefaultOAuth2UserService { private final MemberRepository memberRepository; private final UserInfoFactory userInfoFactory; + // 앱 구동시 ADMIN 계정 Insert @Value("${admin.username}") private String adminUsername; @Value("${admin.picUrl}") @@ -37,6 +37,7 @@ public class Oauth2MemberService extends DefaultOAuth2UserService { private String adminProvider; @Override + @Transactional public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException { Oauth2UserInfo userInfo = @@ -54,7 +55,7 @@ public class Oauth2MemberService extends DefaultOAuth2UserService { if(member == null) { if(memberRepository.findByEmail(userInfo.getEmail()) != null) - throw new DuplicateEmailException(); + throw new OAuth2AuthenticationException("duplicateEmail"); member = Member.builder() .username(userInfo.getUserName()) diff --git a/src/main/resources/static/css/login.css b/src/main/resources/static/css/login.css new file mode 100644 index 0000000..fad7820 --- /dev/null +++ b/src/main/resources/static/css/login.css @@ -0,0 +1,58 @@ + +button img{ + width: 200px; + height:50px; +} + +/* Social Buttons Style */ +.btnCtm { + background: none; + border: none; + background-position: 1em; + background-repeat: no-repeat; + background-size: 2em; + border-radius: 0.5em; + border: none; + color: white; + cursor: pointer; + font-size: 0.9em; + line-height: 1em; + padding: 0 2em 0 4em; + text-decoration: none; + transition: all 0.5s; + height: 50px; + width: 200px; + margin-bottom: 5px; +} + +.btn-google { + background-color: #dd4b39; + background-image: url("/img/google.svg"); +} +.btn-google:hover { + background-color: #e47365; +} +.btn-google:active { + background-color: #c23321; +} +.btn-facebook { + background-color: #3b5998; + background-image: url("/img/facebook.svg"); +} +.btn-facebook:hover { + background-color: #4c70ba; +} +.btn-facebook:active { + background-color: #2d4373; +} + +.btn-kakao{ + background: none; + border: none; +} + +.btn-naver{ + background: none; + border: none; + +} diff --git a/src/main/resources/static/css/mainCss.css b/src/main/resources/static/css/mainCss.css new file mode 100644 index 0000000..3998ec6 --- /dev/null +++ b/src/main/resources/static/css/mainCss.css @@ -0,0 +1,333 @@ +@font-face { + font-family: "IM_Hyemin-Bold"; + src: url("https://cdn.jsdelivr.net/gh/projectnoonnu/noonfonts_2106@1.1/IM_Hyemin-Bold.woff2") + format("woff"); + font-weight: normal; + font-style: normal; +} + +a { + text-decoration: none; + color: rgb(65, 65, 65); +} +a:visited { + text-decoration: none; + color: rgb(65, 65, 65); +} +a:active { + text-decoration: none; + color: rgb(65, 65, 65); +} +a:hover { + text-decoration: none; + color: rgb(65, 65, 65); +} + +body { + background-color: #fcf6f5; + font-family: "IM_Hyemin-Bold"; +} + +body::-webkit-scrollbar { + width: 15px; +} + +body::-webkit-scrollbar-thumb { + background-color: rgb(233, 224, 224); + background-clip: padding-box; + border: 4px solid transparent; + border-top-left-radius: 100px; + border-bottom-right-radius: 100px; +} + +body::-webkit-scrollbar-track { + background-color: #f1f1f1; +} + +.offcanvas-body::-webkit-scrollbar { + width: 15px; +} + +.offcanvas-body::-webkit-scrollbar-thumb { + background-color: rgb(233, 224, 224); + background-clip: padding-box; + border: 4px solid transparent; + border-top-left-radius: 100px; + border-bottom-right-radius: 100px; +} + +.offcanvas-body::-webkit-scrollbar-track { + background-color: #fcf6f5; +} + +.btn-toggle { + display: inline-flex; + align-items: center; + padding: 0.25rem 0.5rem; + font-weight: 600; + color: rgba(0, 0, 0, 0.65); + background-color: transparent; + border: 0; +} +.btn-toggle:hover, +.btn-toggle:focus { + color: rgba(0, 0, 0, 0.85); + background-color: #d2f4ea; +} + +.btn-toggle::before { + width: 1.25em; + line-height: 0; + content: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='rgba%280,0,0,.5%29' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M5 14l6-6-6-6'/%3e%3c/svg%3e"); + transition: transform 0.35s ease; + transform-origin: 0.5em 50%; +} + +.btn-toggle[aria-expanded="true"] { + color: rgba(0, 0, 0, 0.85); +} +.btn-toggle[aria-expanded="true"]::before { + transform: rotate(90deg); +} + +.btn-toggle-nav a { + display: inline-flex; + padding: 0.1875rem 0.5rem; + margin-top: 0.125rem; + margin-left: 1.25rem; + text-decoration: none; +} +.btn-toggle-nav a:hover, +.btn-toggle-nav a:focus { + background-color: #d2f4ea; +} + +.navbar { + opacity: 0.98; +} +.navbar-toggler { + border: whitesmoke; +} +.fa-address-card { + font-size: 24px; + color: rgb(119, 119, 119); +} + +.cover { + object-fit: cover; +} + +.carousel-item { + cursor: pointer; +} + +.cards-container { + max-width: 1200px; +} + +.popular-card { + overflow: hidden; + cursor: pointer; +} + +.card-description { + position: absolute; + top: calc(100% - 50px); + height: 50px; + overflow: hidden; + + background: rgb(56, 55, 55); + color: aliceblue; + width: 100%; + opacity: 0.8; + + transition-property: all; + transition-duration: 0.7s; +} + +.card-des-overlay { + position: absolute; + overflow: hidden; + background: rgb(56, 55, 55); + color: aliceblue; + width: 100%; + height: 100%; + top: 100%; + opacity: 0.8; + + transition-property: all; + transition-duration: 0.7s; +} + +.popular-card:hover .card-des-overlay { + top: 0%; +} +.popular-card:hover .card-description { + top: 100%; +} + +.recent-card-text { + overflow: hidden; + position: relative; + line-height: 1.2em; + max-height: 3.6em; + margin-right: -1em; +} + +.recent-card-text:before { + content: "..."; + position: absolute; + right: 4px; + bottom: 0; +} + +.recent-card-text:after { + content: ""; + position: absolute; + right: 4px; + width: 1em; + height: 1em; + margin-top: 0.2em; + background: white; +} + +.recent-card { + transition-property: all; + transition-duration: 1s; +} + +.recent-card:hover { + box-shadow: 0px 4px 8px rgba(38, 38, 38, 0.2); + top: -4px; + border: 1px solid #cccccc; + background-color: white; + cursor: pointer; +} + +.arrow-up { + position: fixed; + bottom: 50px; + right: 50px; + font-size: 50px; + border-radius: 100%; + border: white; + width: 70px; + height: 70px; + color: white; + background-color: rgb(241, 226, 89); + z-index: 5; + opacity: 0.7; +} + +.sidebar { + position: fixed; + height: 100vh; + z-index: 1000; +} + +.sidebar::-webkit-scrollbar { + width: 15px; +} + +.sidebar::-webkit-scrollbar-thumb { + background-color: rgb(233, 224, 224, 0.7); + background-clip: padding-box; + border: 4px solid transparent; + border-top-left-radius: 100px; + border-bottom-right-radius: 100px; +} + +.sidebar::-webkit-scrollbar-track { + background-color: #f1f1f1; +} + +@media (max-width: 767.98px) { + small { + font-size: 10px; + } + + .arrow-up { + width: 50px; + height: 50px; + font-size: 30px; + line-height: 50px; + right: 16px; + bottom: 16px; + } +} + +/* list */ +.pagination-outer { + text-align: center; +} +.pagination { + display: inline-flex; +} +.pagination li { + margin: 0 5px; +} +.pagination li a.page-link { + color: #999; + background: #e7e7e7; + font-size: 16px; + font-weight: 700; + line-height: 30px; + height: 30px; + width: 30px; + padding: 0; + border: none; + border-radius: 5px; + display: block; + position: relative; +} +.pagination li.active a.page-link, +.pagination li a.page-link:hover, +.pagination li.active a.page-link:hover, +.pagination li a.page-link:focus { + color: rgb(12, 11, 11); + background: linear-gradient(#f0f5b0, #f8fae3); + border: none; +} + +@media only screen and (max-width: 480px) { + .pagination { + font-size: 0; + display: block; + } + .pagination li { + margin: 5px 0; + display: inline-block; + } +} + +.form-color { + background-color: #e0dedd; +} + +.form-control { + border-radius: 25px; + height: fit-content; + resize: none; +} + +.form-control:focus { + color: #495057; + background-color: #fcf6f5; + border-color: #35b69f; + outline: 0; + box-shadow: none; + text-indent: 10px; +} + +.comment-text { + font-size: 13px; +} + +.comment-reply { + background-color: #e0dedd; +} + +.user-feed { + font-size: 14px; + margin-top: 12px; +} diff --git a/src/main/resources/static/img/facebook.svg b/src/main/resources/static/img/facebook.svg new file mode 100644 index 0000000..0cd9ea9 --- /dev/null +++ b/src/main/resources/static/img/facebook.svg @@ -0,0 +1,10 @@ + + + + + + diff --git a/src/main/resources/static/img/google.svg b/src/main/resources/static/img/google.svg new file mode 100644 index 0000000..61b41af --- /dev/null +++ b/src/main/resources/static/img/google.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + diff --git a/src/main/resources/static/img/kakao.png b/src/main/resources/static/img/kakao.png new file mode 100644 index 0000000000000000000000000000000000000000..09bb358843a08576d0029ff8427120c9f5f5c36d GIT binary patch literal 2946 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/templates/index.html b/src/main/resources/templates/index.html deleted file mode 100644 index 3129e86..0000000 --- a/src/main/resources/templates/index.html +++ /dev/null @@ -1,24 +0,0 @@ - - - - - Title - - -ㅎㅇ - -
-
- 아이디
-
- - -구글 로그인 -페이스북 로그인 -네이버 로그인 -카카오 로그인 -로그아웃 - - - \ 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..328a576 --- /dev/null +++ b/src/main/resources/templates/layout/layout.html @@ -0,0 +1,294 @@ + + + + + + + Jinia's Log + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+ + +
+ + + + + + + +
+
+ + + +
+ +
+ + +
+
+
Copyright ©Jinia
+
+
+
+
+ + + + + + + + + + + diff --git a/src/main/resources/templates/login.html b/src/main/resources/templates/login.html new file mode 100644 index 0000000..917727c --- /dev/null +++ b/src/main/resources/templates/login.html @@ -0,0 +1,54 @@ + + + + + Jinia's Log - 로그인 + + + + +
+ +
+ +
+
+ +

로그인 오류

+ + + + + +
+ + + +
+ +
+ + + +
+
+
+ +
+ + +
+ + + \ No newline at end of file diff --git a/src/main/resources/templates/main.html b/src/main/resources/templates/main.html new file mode 100644 index 0000000..b500cea --- /dev/null +++ b/src/main/resources/templates/main.html @@ -0,0 +1,332 @@ + + + + + Jinia's Log + + + + +
+ + + + + +
+ +
+ +
+

최신 포스팅

+
+ + + + + + + + + + + +
+
+
+ + + \ No newline at end of file diff --git a/src/test/java/myblog/blog/MainControllerTest.java b/src/test/java/myblog/blog/MainControllerTest.java new file mode 100644 index 0000000..03373b4 --- /dev/null +++ b/src/test/java/myblog/blog/MainControllerTest.java @@ -0,0 +1,33 @@ +package myblog.blog; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; + +import static org.junit.jupiter.api.Assertions.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@SpringBootTest +@AutoConfigureMockMvc +class MainControllerTest { + + @Autowired + private MockMvc mockMvc; + + @Test + @WithMockUser(roles = "ADMIN") + public void 권한테스트() throws Exception { + // given + // when + ResultActions admin = mockMvc.perform(get("/admin")); + // then + admin.andExpect(status().isOk()); + + } +} \ No newline at end of file diff --git a/src/test/java/myblog/blog/member/service/Oauth2MemberServiceTest.java b/src/test/java/myblog/blog/member/service/Oauth2MemberServiceTest.java new file mode 100644 index 0000000..8cdd873 --- /dev/null +++ b/src/test/java/myblog/blog/member/service/Oauth2MemberServiceTest.java @@ -0,0 +1,37 @@ +package myblog.blog.member.service; + +import myblog.blog.member.doamin.Member; +import myblog.blog.member.doamin.Role; +import myblog.blog.member.repository.MemberRepository; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import java.util.Optional; + +import static org.assertj.core.api.Assertions.*; + +@SpringBootTest +class Oauth2MemberServiceTest { + + @Autowired + Oauth2MemberService oauth2MemberService; + + @Autowired + MemberRepository memberRepository; + + @Test + public void 권한테스트() throws Exception { + // given + Optional byId = memberRepository.findById(1L); + Member admin = byId.get(); + + + // when + + // then + assertThat(admin.getRole().toString()).isEqualTo(Role.ADMIN.toString()); + + } + +} \ No newline at end of file