2021-08-02

This commit is contained in:
2021-08-02 17:02:06 +09:00
parent 7d3ad1cce0
commit d45139dd58
79 changed files with 1685 additions and 152 deletions

1
.gitignore vendored
View File

@@ -3,3 +3,4 @@
/buildSrc/build/ /buildSrc/build/
/.idea/ /.idea/
/build/ /build/
/**/build/**

17
README.md Normal file
View File

@@ -0,0 +1,17 @@
# Spring-boot Examples
## File Upload
## Security
## Security plus JPA
## Testing
## Validation
------
developed by Elex
https://www.elex-project.com/

View File

@@ -1,10 +0,0 @@
plugins {
id("elex-application")
}
application{
mainClass.set("com.elex_project.sample.Application")
}
dependencies {
}

View File

@@ -1 +0,0 @@
package com.elex_project.sample;

View File

@@ -1,10 +1,16 @@
/*
* Spring-boot Examples
*
* Copyright (c) 2021. Elex. All Rights Reserved.
* https://www.elex-project.com/
*/
buildscript { buildscript {
repositories { repositories {
maven { maven {
url = uri("https://repository.elex-project.com/repository/maven") url = uri("https://repository.elex-project.com/repository/maven")
} }
} }
dependencies { dependencies {
classpath ("com.jaredsburrows:gradle-license-plugin:0.8.90") classpath ("com.jaredsburrows:gradle-license-plugin:0.8.90")
} }

View File

@@ -1,3 +1,10 @@
/*
* Spring-boot Examples
*
* Copyright (c) 2021. Elex. All Rights Reserved.
* https://www.elex-project.com/
*/
plugins{ plugins{
`kotlin-dsl` `kotlin-dsl`
} }

View File

@@ -1,17 +0,0 @@
plugins {
id("elex-base")
application
}
tasks.jar {
manifest {
attributes(mapOf(
"Implementation-Title" to project.name,
"Implementation-Version" to project.version,
"Implementation-Vendor" to "ELEX co.,pte.",
"Main-Class" to application.mainClass,
"Automatic-Module-Name" to "com.elex_project.${project.name}"
))
}
}

View File

@@ -1,10 +1,19 @@
/*
* Spring-boot Examples
*
* Copyright (c) 2021. Elex. All Rights Reserved.
* https://www.elex-project.com/
*/
plugins { plugins {
java java
idea
} }
group = "com.elex-project" group = "kr.pe.elex.examples"
version = "1.0-SNAPSHOT" version = "1.0-SNAPSHOT"
description = ""//todo description = "Examples: Spring-boot"
repositories { repositories {
maven { maven {
@@ -29,12 +38,11 @@ configurations {
} }
tasks.jar { tasks.jar {
manifest { // todo manifest {
attributes(mapOf( attributes(mapOf(
"Implementation-Title" to project.name, "Implementation-Title" to project.name,
"Implementation-Version" to project.version, "Implementation-Version" to project.version,
"Implementation-Vendor" to "ELEX co.,pte.", "Implementation-Vendor" to "ELEX co.,pte."
"Automatic-Module-Name" to "com.elex_project.${project.name}"
)) ))
} }
} }
@@ -60,16 +68,9 @@ tasks.javadoc {
(options as StandardJavadocDocletOptions).docEncoding = "UTF-8" (options as StandardJavadocDocletOptions).docEncoding = "UTF-8"
} }
dependencies { dependencies {
implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar"))))
implementation("org.slf4j:slf4j-api:1.7.30")
implementation("org.jetbrains:annotations:21.0.1") implementation("org.jetbrains:annotations:21.0.1")
compileOnly("org.projectlombok:lombok:1.18.20")
annotationProcessor("org.projectlombok:lombok:1.18.20")
testAnnotationProcessor("org.projectlombok:lombok:1.18.20")
testImplementation("ch.qos.logback:logback-classic:1.2.3")
testImplementation("org.junit.jupiter:junit-jupiter:5.7.0")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.7.0")
} }

View File

@@ -1,88 +0,0 @@
plugins {
id ("elex-base")
`java-library`
`maven-publish`
}
publishing {
publications {
create<MavenPublication>("mavenJava") {
from(components["java"])
pom {
// todo
name.set(project.name)
description.set(project.description)
url.set("https://")
inceptionYear.set("2021")
properties.set(mapOf(
"myProp" to "value",
"prop.with.dots" to "anotherValue"
))
organization {
name.set("Elex co.,Pte.")
url.set("https://www.elex-project.com/")
}
licenses {
license {
// todo
name.set("BSD 3-Clause License")
url.set("licenseUrl")
comments.set("")
}
}
developers {
developer {
id.set("elex")
name.set("Elex")
url.set("https://www.elex.pe.kr/")
email.set("developer@elex-project.com")
organization.set("Elex Co.,Pte.")
organizationUrl.set("https://www.elex-project.com/")
roles.set(arrayListOf("Developer", "CEO"))
timezone.set("Asia/Seoul")
properties.set(mapOf("" to ""))
}
}
contributors {
contributor {
name.set("")
email.set("")
url.set("")
}
}
scm {
// todo
connection.set("scm:git:https://github.com/my-library.git")
developerConnection.set("scm:git:https://github.com/my-library.git")
url.set("https://github.com/my-library/")
}
}
}
}
repositories {
maven {
name = "mavenElex"
val urlRelease = uri("https://repository.elex-project.com/repository/maven-releases")
val urlSnapshot = uri("https://repository.elex-project.com/repository/maven-snapshots")
url = if (version.toString().endsWith("SNAPSHOT")) urlSnapshot else urlRelease
// Repository credential, Must be defined in ~/.gradle/gradle.properties
credentials {
username = project.findProperty("repo.username") as String
password = project.findProperty("repo.password") as String
}
}
maven { //todo
name = "mavenGithub"
url = uri("https://maven.pkg.github.com/elex-project/tmpl-java-library")
credentials {
username = project.findProperty("github.username") as String
password = project.findProperty("github.token") as String
}
}
}
}
dependencies {
}

View File

@@ -0,0 +1,18 @@
/*
* Spring-boot Examples
*
* Copyright (c) 2021. Elex. All Rights Reserved.
* https://www.elex-project.com/
*/
plugins {
id("elex-base")
}
dependencies{
implementation("org.jetbrains:annotations:21.0.1")
}

1
file-upload/README.md Normal file
View File

@@ -0,0 +1 @@
# 파일 업로드

View File

@@ -0,0 +1,28 @@
/*
* Spring-boot Examples
*
* Copyright (c) 2021. Elex. All Rights Reserved.
* https://www.elex-project.com/
*/
plugins {
id("elex-spring-boot")
id("org.springframework.boot") version "2.5.3"
id("io.spring.dependency-management") version "1.0.11.RELEASE"
}
dependencies {
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.springframework.boot:spring-boot-starter-mustache")
compileOnly("org.projectlombok:lombok")
developmentOnly("org.springframework.boot:spring-boot-devtools")
runtimeOnly("org.mariadb.jdbc:mariadb-java-client")
annotationProcessor("org.springframework.boot:spring-boot-configuration-processor")
annotationProcessor("org.projectlombok:lombok")
testImplementation("org.springframework.boot:spring-boot-starter-test")
}

View File

@@ -0,0 +1,20 @@
/*
* Spring-boot Examples
*
* Copyright (c) 2021. Elex. All Rights Reserved.
* https://www.elex-project.com/
*/
package kr.pe.elex.examples;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

View File

@@ -0,0 +1,72 @@
/*
* Spring-boot Examples
*
* Copyright (c) 2021. Elex. All Rights Reserved.
* https://www.elex-project.com/
*/
package kr.pe.elex.examples;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.http.ContentDisposition;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
@Slf4j
@Controller
public class MyController {
@Autowired
private StorageService storageService;
@GetMapping(path = {"/"})
public String index(@NotNull Model model) {
model.addAttribute("files", storageService.listFiles());
return "main";
}
/**
* 파일 업로드
* @param file
* @param redirectAttributes
* @return
*/
@PostMapping(path = {"/upload"})
public String upload(final @RequestParam("file") MultipartFile file,
final @NotNull RedirectAttributes redirectAttributes) {
storageService.store(file);
// 업로드 후 새로 고침
redirectAttributes.addFlashAttribute("message",
"You successfully uploaded " + file.getOriginalFilename() + "!");
return "redirect:/";
}
/**
* 파일 다운로드
* @param filename
* @return
*/
@GetMapping("/files/{filename:.+}")
@ResponseBody
public ResponseEntity<Resource> serveFile(@PathVariable String filename) {
final Resource file = storageService.loadAsResource(filename);
final ContentDisposition contentDisposition = ContentDisposition.attachment()
.filename(filename)
.build();
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION,
contentDisposition.toString())
.body(file);
}
}

View File

@@ -0,0 +1,62 @@
/*
* Spring-boot Examples
*
* Copyright (c) 2021. Elex. All Rights Reserved.
* https://www.elex-project.com/
*/
package kr.pe.elex.examples;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
@Slf4j
@Service
public class StorageService {
/*
테스트용 임시 파일 저장소
실제로는 파일시스템에 저장하거나 DB에 저장해야 한다.
*/
private final Map<String, byte[]> storage = new HashMap<>();
/**
* 저장된 파일 목록
* @return
*/
Set<String> listFiles(){
return storage.keySet();
}
/**
* 파일을 저장
* @param file
*/
void store(final @NotNull MultipartFile file) {
try {
storage.put(file.getOriginalFilename(), file.getBytes());
log.info("File Uploaded... {}", file.getOriginalFilename());
} catch (IOException e) {
log.error("Unable to save a file.", e);
}
}
/**
* 저장된 파일을 불러옴
* @param filename
* @return
*/
Resource loadAsResource(final String filename) {
//return new FileSystemResource(new File(filename));
return new ByteArrayResource(storage.get(filename));
}
}

View File

@@ -0,0 +1,8 @@
/*
* Spring-boot Examples
*
* Copyright (c) 2021. Elex. All Rights Reserved.
* https://www.elex-project.com/
*/
package kr.pe.elex.examples;

View File

@@ -0,0 +1,9 @@
spring:
application:
name: My spring-boot project
servlet:
multipart:
max-file-size: 128KB
max-request-size: 128KB
server:
port: 8080

View File

@@ -0,0 +1,10 @@
('-. ('-. ) (`-.
_( OO) _( OO) ( OO ).
(,------.,--. (,------.(_/. \_)-.
| .---'| |.-') | .---' \ `.' /
| | | | OO ) | | \ /\
(| '--. | |`-' |(| '--. \ \ |
| .--'(| '---.' | .--' .' \_)
| `---.| | | `---. / .'. \
`------'`------' `------''--' '--'
powered by ELEX

View File

@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Spring-boot Examples
~
~ Copyright (c) 2021. Elex. All Rights Reserved.
~ https://www.elex-project.com/
-->
<configuration>
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<include resource="org/springframework/boot/logging/logback/console-appender.xml"/>
<springProperty name="LOG_DIR" source="logging.path"
defaultValue="${user.home}/logs"/>
<property name="LOG_PATH" value="${LOG_DIR}/stephanie.log"/>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
</encoder>
</appender>
<appender name="ROLLING-FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<charset>UTF-8</charset>
<pattern>${FILE_LOG_PATTERN}</pattern>
</encoder>
<file>${LOG_PATH}</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_DIR}/sebastian_%d{yyyy-MM-dd}_%i.log.gz</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>10MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<maxHistory>60</maxHistory>
</rollingPolicy>
</appender>
<logger name="kr.pe.elex" level="debug" additivity="false">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="ROLLING-FILE"/>
</logger>
<root level="info">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="ROLLING-FILE"/>
</root>
</configuration>

View File

@@ -0,0 +1,15 @@
<h1>File Upload Example!!!</h1>
<form action="/upload" method="POST" enctype="multipart/form-data">
<label for="file">Select a file: </label>
<input id="file" name="file" type="file" title="Upload"/>
<div>
<button>Upload</button>
</div>
</form>
<h2>Uploaded Files</h2>
<p>Click to download.</p>
<ul>
{{#files}}
<li><a href="/files/{{.}}">{{.}}</a></li>
{{/files}}
</ul>

View File

@@ -1,3 +1,10 @@
#
# Spring-boot Examples
#
# Copyright (c) 2021. Elex. All Rights Reserved.
# https://www.elex-project.com/
#
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.1.1-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-7.1.1-all.zip

15
gradlew vendored
View File

@@ -1,19 +1,10 @@
#!/usr/bin/env sh #!/usr/bin/env sh
# #
# Copyright 2015 the original author or authors. # Spring-boot Examples
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Copyright (c) 2021. Elex. All Rights Reserved.
# you may not use this file except in compliance with the License. # https://www.elex-project.com/
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# #
############################################################################## ##############################################################################

View File

@@ -1,7 +0,0 @@
plugins {
id("elex-library")
}
dependencies {
}

View File

@@ -1 +0,0 @@
package com.elex_project.sample;

View File

@@ -1,4 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!--
~ Spring-boot Examples
~
~ Copyright (c) 2021. Elex. All Rights Reserved.
~ https://www.elex-project.com/
-->
<configuration> <configuration>
<property name="LOG_FILE" value="LogFile" /> <property name="LOG_FILE" value="LogFile" />
<property name="LOG_DIR" value="/var/log/application" /> <property name="LOG_DIR" value="/var/log/application" />

View File

@@ -0,0 +1,11 @@
# Security Examples
with Spring-boot + JPA
이 [예제](https://github.com/elex-project/spring-boot-security-examples)를 먼저 봐야 합니다.
---
developed by Elex
https://www.elex-project.com

View File

@@ -0,0 +1,33 @@
/*
* Spring-boot Examples
*
* Copyright (c) 2021. Elex. All Rights Reserved.
* https://www.elex-project.com/
*/
plugins {
id("elex-spring-boot")
id("org.springframework.boot") version "2.5.3"
id("io.spring.dependency-management") version "1.0.11.RELEASE"
}
dependencies {
implementation("org.springframework.boot:spring-boot-starter-mustache")
implementation("org.springframework.boot:spring-boot-starter-web")
implementation ("org.springframework.boot:spring-boot-starter-security")
testImplementation ("org.springframework.security:spring-security-test")
implementation ("org.springframework.boot:spring-boot-starter-data-jpa")
runtimeOnly("com.h2database:h2")
compileOnly("org.projectlombok:lombok")
developmentOnly("org.springframework.boot:spring-boot-devtools")
runtimeOnly("org.mariadb.jdbc:mariadb-java-client")
annotationProcessor("org.springframework.boot:spring-boot-configuration-processor")
annotationProcessor("org.projectlombok:lombok")
testImplementation("org.springframework.boot:spring-boot-starter-test")
}

View File

@@ -0,0 +1,45 @@
/*
* Spring-boot Examples
*
* Copyright (c) 2021. Elex. All Rights Reserved.
* https://www.elex-project.com/
*/
package kr.pe.elex.examples;
import kr.pe.elex.examples.model.User;
import kr.pe.elex.examples.model.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
@SpringBootApplication
public class Application {
@Autowired
private UserRepository repository;
@Autowired
private PasswordEncoder passwordEncoder;
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
/**
* 실제 프로젝트에서는 입력 폼을 통해서 회원 가입을 하겠지만, ...
*
*/
@Component
public class SetupUsers implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
User user = new User("Charlie", "user1", passwordEncoder.encode("pw1"));
repository.save(user);
}
}
}

View File

@@ -0,0 +1,71 @@
/*
* Spring-boot Examples
*
* Copyright (c) 2021. Elex. All Rights Reserved.
* https://www.elex-project.com/
*/
package kr.pe.elex.examples;
import kr.pe.elex.examples.model.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
@Slf4j
@Controller
public class MyController {
/**
* 로그인한 사용자 정보를 가져오려면, Authentication을 사용합니다.
* 또는, @AuthenticationPrincipal를 사용합니다.
*
* @param authentication
* @param model
* @return
*/
@GetMapping({"/"})
public String index(Authentication authentication, Model model) {
if (null != authentication) {
User user = (User) (authentication.getPrincipal());
log.info("USER: {}", user.getName());
model.addAttribute("user", user);
} else {
log.info("NO USER: ");
}
return "home";
}
@GetMapping({"/login"})
public String login() {
return "login";
}
@GetMapping({"/info"})
public String info() {
return "normal_info";
}
/**
* 로그인한 사용자 정보를 가져오기 위해, @AuthenticationPrincipal를 사용합니다.
*
* @param user
* @param model
* @return
*/
@GetMapping({"/secure"})
public String secure(@AuthenticationPrincipal User user, Model model) {
if (null != user) {
log.info("USER: {}", user.getName());
} else {
log.info("NO USER!!");
}
model.addAttribute("user", user);
return "secure_info";
}
}

View File

@@ -0,0 +1,72 @@
/*
* Spring-boot Examples
*
* Copyright (c) 2021. Elex. All Rights Reserved.
* https://www.elex-project.com/
*/
package kr.pe.elex.examples;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
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.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserService userService;
@Override
protected void configure(@NotNull HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/", "/info", "/h2-console").permitAll() // 아무나 접근 가능
.antMatchers("/h2-console/**").permitAll() // H2콘솔을 쓰기 위해 추가했음
//.antMatchers("/admin").hasAnyRole("ADMIN")
.anyRequest().authenticated() // 그 외에는 인증해야 접근 가능
.and()
.formLogin()// 커스텀 로그인 폼
.loginPage("/login")
.usernameParameter("user_id") // 로그인 폼 매개변수명 지정
.passwordParameter("user_pw")
.permitAll()
.and()
.logout()// 로그아웃
.logoutSuccessUrl("/")
.permitAll()
//.and().httpBasic();
.and().csrf().ignoringAntMatchers("/h2-console/**")// H2콘솔을 쓰기 위해 추가했음
.and().headers().frameOptions().sameOrigin()// H2콘솔을 쓰기 위해 추가했음
//.and().csrf().disable()
;
}
@Override
protected void configure(@NotNull AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService)
.passwordEncoder(passwordEncoder())
/*auth.inMemoryAuthentication()
.withUser("user1")
.password("{noop}pw1")
.authorities("ROLE_USER")
.and()
.withUser("user2")
.password("{noop}pw2")
.authorities("ROLE_USER")*/
;
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}

View File

@@ -0,0 +1,37 @@
/*
* Spring-boot Examples
*
* Copyright (c) 2021. Elex. All Rights Reserved.
* https://www.elex-project.com/
*/
package kr.pe.elex.examples;
import kr.pe.elex.examples.model.User;
import kr.pe.elex.examples.model.UserRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
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;
/**
* UserDetailsService를 구현해서 사용자를 조회할 수 있는 서비스를 만듭니다.
*/
@Slf4j
@Service
public class UserService implements UserDetailsService {
@Autowired
private UserRepository repository;
@Override
public UserDetails loadUserByUsername(final String username) throws UsernameNotFoundException {
final User user = repository.findByUsername(username);
if (null == user) {
throw new UsernameNotFoundException("Unable to find a user with " + username);
} else {
return user;
}
}
}

View File

@@ -0,0 +1,28 @@
/*
* Spring-boot Examples
*
* Copyright (c) 2021. Elex. All Rights Reserved.
* https://www.elex-project.com/
*/
package kr.pe.elex.examples;
import kr.pe.elex.examples.model.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.Authentication;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Slf4j
@Configuration
public class WebConfig implements WebMvcConfigurer {
/*
@Override
public void addViewControllers(@NotNull ViewControllerRegistry registry) {
registry.addViewController("/login");
}
*/
}

View File

@@ -0,0 +1,76 @@
/*
* Spring-boot Examples
*
* Copyright (c) 2021. Elex. All Rights Reserved.
* https://www.elex-project.com/
*/
package kr.pe.elex.examples.model;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import javax.persistence.*;
import java.util.*;
/**
* UserDetails를 구현해서 데이터 모델을 만듭니다.
*/
@Table(name = "User")
@Entity
@Getter
@Setter
@RequiredArgsConstructor
@ToString
public class User implements UserDetails {
@Column(nullable = false)
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String username;
private String password;
private String name;
private String role;
private boolean enabled;
public User(String name, String username, String password){
this();
this.username = username;
this.password = password;
this.name = name;
this.role = "ROLE_USER";
this.enabled = true;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return Arrays.asList(new SimpleGrantedAuthority(role));
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return enabled;
}
}

View File

@@ -0,0 +1,16 @@
/*
* Spring-boot Examples
*
* Copyright (c) 2021. Elex. All Rights Reserved.
* https://www.elex-project.com/
*/
package kr.pe.elex.examples.model;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
User findByUsername(String username);
}

View File

@@ -0,0 +1,8 @@
/*
* Spring-boot Examples
*
* Copyright (c) 2021. Elex. All Rights Reserved.
* https://www.elex-project.com/
*/
package kr.pe.elex.examples;

View File

@@ -0,0 +1,17 @@
spring:
application:
name: My spring-boot project
mustache:
expose-request-attributes: true # 뷰에서 CSRF를 뿌려주기 위해서 필요합니다.
h2:
console:
enabled: true
datasource:
url: jdbc:h2:mem:testdb
driverClassName: org.h2.Driver
username: sa
password: pw
jpa:
database-platform: org.hibernate.dialect.H2Dialect
server:
port: 8080

View File

@@ -0,0 +1,10 @@
('-. ('-. ) (`-.
_( OO) _( OO) ( OO ).
(,------.,--. (,------.(_/. \_)-.
| .---'| |.-') | .---' \ `.' /
| | | | OO ) | | \ /\
(| '--. | |`-' |(| '--. \ \ |
| .--'(| '---.' | .--' .' \_)
| `---.| | | `---. / .'. \
`------'`------' `------''--' '--'
powered by ELEX

View File

@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Spring-boot Examples
~
~ Copyright (c) 2021. Elex. All Rights Reserved.
~ https://www.elex-project.com/
-->
<configuration>
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<include resource="org/springframework/boot/logging/logback/console-appender.xml"/>
<springProperty name="LOG_DIR" source="logging.path"
defaultValue="${user.home}/logs"/>
<property name="LOG_PATH" value="${LOG_DIR}/stephanie.log"/>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
</encoder>
</appender>
<appender name="ROLLING-FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<charset>UTF-8</charset>
<pattern>${FILE_LOG_PATTERN}</pattern>
</encoder>
<file>${LOG_PATH}</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_DIR}/sebastian_%d{yyyy-MM-dd}_%i.log.gz</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>10MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<maxHistory>60</maxHistory>
</rollingPolicy>
</appender>
<root level="verbose">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="ROLLING-FILE"/>
</root>
</configuration>

View File

@@ -0,0 +1,15 @@
<h1>홈</h1>
<p>테스트 페이지입니다.</p>
{{#user}}
안녕, {{name}}.
<form action="/logout" method="POST">
{{! CSRF 토큰이 없으면 에러 남. }}
<input type="hidden" name="_csrf" value="{{_csrf.token}}">
<button type="submit">로그아웃</button>
</form>
{{/user}}
{{^user}}
{{/user}}
{{> links}}

View File

@@ -0,0 +1,7 @@
<div>
<a href="/">홈</a> |
<a href="/info">일반</a> |
<a href="/secure">중요</a> |
<a href="/login">로그인</a> |
<a href="/h2-console">DB</a>
</div>

View File

@@ -0,0 +1,11 @@
<h1>로그인</h1>
<form action="/login" method="POST">
<label for="user_id">ID:</label>
<input type="text" name="user_id" id="user_id"/>
<label for="user_pw">PW:</label>
<input type="password" name="user_pw" id="user_pw"/>
<input type="hidden" name="_csrf" value="{{_csrf.token}}">
<input type="submit" value="로그인"/>
</form>
{{> links}}

View File

@@ -0,0 +1,3 @@
<h1>아무나 볼 수 있는 페이지</h1>
<p>This page doesn't contain any Important messages.</p>
{{> links}}

View File

@@ -0,0 +1,4 @@
<h1>로그인해야 볼 수 있는 페이지</h1>
<p>This page contains a Important message.</p>
{{#user}}Hello, {{name}}{{/user}}
{{> links}}

7
security/README.md Normal file
View File

@@ -0,0 +1,7 @@
# Security Examples
with Spring-boot
---
developed by Elex
https://www.elex-project.com

30
security/build.gradle.kts Normal file
View File

@@ -0,0 +1,30 @@
/*
* Spring-boot Examples
*
* Copyright (c) 2021. Elex. All Rights Reserved.
* https://www.elex-project.com/
*/
plugins {
id("elex-spring-boot")
id("org.springframework.boot") version "2.5.3"
id("io.spring.dependency-management") version "1.0.11.RELEASE"
}
dependencies {
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.springframework.boot:spring-boot-starter-mustache")
implementation ("org.springframework.boot:spring-boot-starter-security")
testImplementation ("org.springframework.security:spring-security-test")
compileOnly("org.projectlombok:lombok")
developmentOnly("org.springframework.boot:spring-boot-devtools")
runtimeOnly("org.mariadb.jdbc:mariadb-java-client")
annotationProcessor("org.springframework.boot:spring-boot-configuration-processor")
annotationProcessor("org.projectlombok:lombok")
testImplementation("org.springframework.boot:spring-boot-starter-test")
}

View File

@@ -0,0 +1,20 @@
/*
* Spring-boot Examples
*
* Copyright (c) 2021. Elex. All Rights Reserved.
* https://www.elex-project.com/
*/
package kr.pe.elex.examples;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

View File

@@ -0,0 +1,37 @@
/*
* Spring-boot Examples
*
* Copyright (c) 2021. Elex. All Rights Reserved.
* https://www.elex-project.com/
*/
package kr.pe.elex.examples;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Slf4j
@Controller
public class MyController {
@GetMapping({"/"})
public String index() {
return "home";
}
@GetMapping({"/login"})
public String login() {
return "login";
}
@GetMapping({"/info"})
public String info() {
return "normal_info";
}
@GetMapping({"/secure"})
public String secure() {
return "secure_info";
}
}

View File

@@ -0,0 +1,55 @@
/*
* Spring-boot Examples
*
* Copyright (c) 2021. Elex. All Rights Reserved.
* https://www.elex-project.com/
*/
package kr.pe.elex.examples;
import org.jetbrains.annotations.NotNull;
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.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(@NotNull HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/", "/info").permitAll() // 아무나 접근 가능
//.antMatchers("/admin").hasAnyRole("ADMIN")
.anyRequest().authenticated() // 그 외에는 인증해야 접근 가능
.and()
.formLogin()// 커스텀 로그인 폼
.loginPage("/login")
.usernameParameter("user_id") // 로그인 폼 매개변수명 지정
.passwordParameter("user_pw")
.permitAll()
.and()
.logout()// 로그아웃
.logoutSuccessUrl("/")
.permitAll()
//.and().httpBasic();
//.and().csrf().disable()
;
}
@Override
protected void configure(@NotNull AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user1")
.password("{noop}pw1")
.authorities("ROLE_USER")
.and()
.withUser("user2")
.password("{noop}pw2")
.authorities("ROLE_USER")
;
}
}

View File

@@ -0,0 +1,23 @@
/*
* Spring-boot Examples
*
* Copyright (c) 2021. Elex. All Rights Reserved.
* https://www.elex-project.com/
*/
package kr.pe.elex.examples;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Slf4j
@Configuration
public class WebConfig implements WebMvcConfigurer {
/*
@Override
public void addViewControllers(@NotNull ViewControllerRegistry registry) {
registry.addViewController("/login");
}
*/
}

View File

@@ -0,0 +1,8 @@
/*
* Spring-boot Examples
*
* Copyright (c) 2021. Elex. All Rights Reserved.
* https://www.elex-project.com/
*/
package kr.pe.elex.examples;

View File

@@ -0,0 +1,7 @@
spring:
application:
name: My spring-boot project
mustache:
expose-request-attributes: true # 뷰에서 CSRF를 뿌려주기 위해서 필요합니다.
server:
port: 8080

View File

@@ -0,0 +1,10 @@
('-. ('-. ) (`-.
_( OO) _( OO) ( OO ).
(,------.,--. (,------.(_/. \_)-.
| .---'| |.-') | .---' \ `.' /
| | | | OO ) | | \ /\
(| '--. | |`-' |(| '--. \ \ |
| .--'(| '---.' | .--' .' \_)
| `---.| | | `---. / .'. \
`------'`------' `------''--' '--'
powered by ELEX

View File

@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Spring-boot Examples
~
~ Copyright (c) 2021. Elex. All Rights Reserved.
~ https://www.elex-project.com/
-->
<configuration>
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<include resource="org/springframework/boot/logging/logback/console-appender.xml"/>
<springProperty name="LOG_DIR" source="logging.path"
defaultValue="${user.home}/logs"/>
<property name="LOG_PATH" value="${LOG_DIR}/stephanie.log"/>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
</encoder>
</appender>
<appender name="ROLLING-FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<charset>UTF-8</charset>
<pattern>${FILE_LOG_PATTERN}</pattern>
</encoder>
<file>${LOG_PATH}</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_DIR}/sebastian_%d{yyyy-MM-dd}_%i.log.gz</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>10MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<maxHistory>60</maxHistory>
</rollingPolicy>
</appender>
<root level="verbose">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="ROLLING-FILE"/>
</root>
</configuration>

View File

@@ -0,0 +1,4 @@
<h1>홈</h1>
<p>테스트 페이지입니다.</p>
{{> links}}

View File

@@ -0,0 +1,6 @@
<div>
<a href="/">홈</a> |
<a href="/info">일반</a> |
<a href="/secure">중요</a> |
<a href="/login">로그인</a>
</div>

View File

@@ -0,0 +1,11 @@
<h1>로그인</h1>
<form action="/login" method="POST">
<label for="user_id">ID:</label>
<input type="text" name="user_id" id="user_id"/>
<label for="user_pw">PW:</label>
<input type="password" name="user_pw" id="user_pw"/>
<input type="hidden" name="_csrf" value="{{_csrf.token}}">
<input type="submit" value="로그인"/>
</form>
{{> links}}

View File

@@ -0,0 +1,3 @@
<h1>아무나 볼 수 있는 페이지</h1>
<p>This page doesn't contain any Important messages.</p>
{{> links}}

View File

@@ -0,0 +1,8 @@
<h1>로그인해야 볼 수 있는 페이지</h1>
<p>This page contains a Important message.</p>
<form action="/logout" method="POST">
{{! CSRF 토큰이 없으면 에러 남. }}
<input type="hidden" name="_csrf" value="{{_csrf.token}}">
<button type="submit">로그아웃</button>
</form>
{{> links}}

View File

@@ -1,2 +1,9 @@
/*
* Spring-boot Examples
*
* Copyright (c) 2021. Elex. All Rights Reserved.
* https://www.elex-project.com/
*/
rootProject.name = "spring-boot-examples" rootProject.name = "spring-boot-examples"
include("lib", "app") include("file-upload", "security", "security-with-jpa", "validation", "testing")

27
testing/build.gradle.kts Normal file
View File

@@ -0,0 +1,27 @@
/*
* Spring-boot Examples
*
* Copyright (c) 2021. Elex. All Rights Reserved.
* https://www.elex-project.com/
*/
plugins {
id("elex-spring-boot")
id("org.springframework.boot") version "2.5.3"
id("io.spring.dependency-management") version "1.0.11.RELEASE"
}
dependencies {
implementation("org.springframework.boot:spring-boot-starter-mustache")
implementation("org.springframework.boot:spring-boot-starter-web")
compileOnly("org.projectlombok:lombok")
developmentOnly("org.springframework.boot:spring-boot-devtools")
runtimeOnly("org.mariadb.jdbc:mariadb-java-client")
annotationProcessor("org.springframework.boot:spring-boot-configuration-processor")
annotationProcessor("org.projectlombok:lombok")
testImplementation("org.springframework.boot:spring-boot-starter-test")
}

View File

@@ -0,0 +1,20 @@
/*
* Spring-boot Examples
*
* Copyright (c) 2021. Elex. All Rights Reserved.
* https://www.elex-project.com/
*/
package kr.pe.elex.examples;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

View File

@@ -0,0 +1,41 @@
/*
* Spring-boot Examples
*
* Copyright (c) 2021. Elex. All Rights Reserved.
* https://www.elex-project.com/
*/
package kr.pe.elex.examples;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class MyController {
@Autowired
private MyService service;
@GetMapping({"/"})
public String index(Model model) {
model.addAttribute("name", "World");
return "main";
}
@GetMapping({"/{name}"})
public String index(@PathVariable @ModelAttribute String name) {
if (null == name) name = "World";
return "main";
}
@GetMapping("/greetings")
@ResponseBody
public String greetings() {
return service.sayHello();
}
}

View File

@@ -0,0 +1,17 @@
/*
* Spring-boot Examples
*
* Copyright (c) 2021. Elex. All Rights Reserved.
* https://www.elex-project.com/
*/
package kr.pe.elex.examples;
import org.springframework.stereotype.Service;
@Service
public class MyService {
public String sayHello() {
return "Hello, World.";
}
}

View File

@@ -0,0 +1,8 @@
/*
* Spring-boot Examples
*
* Copyright (c) 2021. Elex. All Rights Reserved.
* https://www.elex-project.com/
*/
package kr.pe.elex.examples;

View File

@@ -0,0 +1,2 @@
<h1>HOME</h1>
<p>Hello, {{name}}!</p>

View File

@@ -0,0 +1,28 @@
/*
* Spring-boot Examples
*
* Copyright (c) 2021. Elex. All Rights Reserved.
* https://www.elex-project.com/
*/
package kr.pe.elex.examples;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
/**
* 통합 테스트. 모든 빈을 로드한 다음, 테스트가 수행된다.
*/
@SpringBootTest(classes = Application.class)
class ApplicationTest {
@Autowired
private MyController controller;
@Test
void test() {
assertThat(controller).isNotNull();
}
}

View File

@@ -0,0 +1,55 @@
/*
* Spring-boot Examples
*
* Copyright (c) 2021. Elex. All Rights Reserved.
* https://www.elex-project.com/
*/
package kr.pe.elex.examples;
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.test.web.servlet.MockMvc;
import static org.hamcrest.Matchers.containsString;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
/**
* MvcMock을 사용해서 웹 요청과 응답을 테스트할 수 있다.
*/
@SpringBootTest//(classes = Application.class)
@AutoConfigureMockMvc
class ControllerTest1 {
@Autowired
private MockMvc mockMvc;
@Test
void getTest() throws Exception {
this.mockMvc.perform(get("/"))
.andDo(print())
.andExpect(status().isOk())
.andExpect(content().string(containsString("Hello, World")));
}
@Test
void getTest2() throws Exception {
this.mockMvc.perform(get("/charlie"))
.andDo(print())
.andExpect(status().isOk())
.andExpect(content().string(containsString("Hello, charlie")));
}
@Test
void postTest() throws Exception {
this.mockMvc.perform(post("/"))
.andDo(print())
.andExpect(status().isMethodNotAllowed());
}
}

View File

@@ -0,0 +1,51 @@
/*
* Spring-boot Examples
*
* Copyright (c) 2021. Elex. All Rights Reserved.
* https://www.elex-project.com/
*/
package kr.pe.elex.examples;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.web.servlet.MockMvc;
import static org.hamcrest.Matchers.containsString;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
/**
* Mockito를 사용해서 테스트용 서비스를 모킹한다.
*/
@WebMvcTest(MyController.class)
class ControllerTest2 {
@Autowired
private MockMvc mockMvc;
@MockBean
private MyService service;
@BeforeEach
void beforeAll() {
Mockito.when(service.sayHello())
.thenReturn("Hello, Mock");
}
@Test
void greetingsTest() throws Exception {
this.mockMvc.perform(get("/greetings"))
.andDo(print())
.andExpect(status().isOk())
.andExpect(content().string(containsString("Hello, Mock")));
}
}

View File

@@ -0,0 +1,51 @@
/*
* Spring-boot Examples
*
* Copyright (c) 2021. Elex. All Rights Reserved.
* https://www.elex-project.com/
*/
package kr.pe.elex.examples;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.test.web.servlet.MockMvc;
import static org.hamcrest.Matchers.containsString;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
/**
* 테스트 설정 클래스를 만든 다음, 테스트용 빈을 등록한다.
*/
@WebMvcTest(MyController.class)
class ControllerTest3 {
@TestConfiguration
static class TestConfig {
@Bean
MyService myService() {
return new MyService() {
@Override
public String sayHello() {
return "Hello, Mock";
}
};
}
}
@Autowired
private MockMvc mockMvc;
@Test
void greetingsTest() throws Exception {
this.mockMvc.perform(get("/greetings"))
.andDo(print())
.andExpect(status().isOk())
.andExpect(content().string(containsString("Hello, Mock")));
}
}

29
validation/README.md Normal file
View File

@@ -0,0 +1,29 @@
# spring-boot-validation-examples
```kotlin
implementation ("org.springframework.boot:spring-boot-starter-validation")
```
## Annotations
* @Digits
* @Email
* @Max
* @Min
* @Negative
* @NotBlank
* @NotEmpty
* @NotNull
* @Null
* @Pattern
* @Positive
* @Size
https://docs.jboss.org/hibernate/beanvalidation/spec/2.0/api/
---
developed by Elex
https://www.elex-project.com

View File

@@ -0,0 +1,29 @@
/*
* Spring-boot Examples
*
* Copyright (c) 2021. Elex. All Rights Reserved.
* https://www.elex-project.com/
*/
plugins {
id("elex-spring-boot")
id("org.springframework.boot") version "2.5.3"
id("io.spring.dependency-management") version "1.0.11.RELEASE"
}
dependencies {
implementation("org.springframework.boot:spring-boot-starter-mustache")
implementation("org.springframework.boot:spring-boot-starter-web")
implementation ("org.springframework.boot:spring-boot-starter-validation")
compileOnly("org.projectlombok:lombok")
developmentOnly("org.springframework.boot:spring-boot-devtools")
runtimeOnly("org.mariadb.jdbc:mariadb-java-client")
annotationProcessor("org.springframework.boot:spring-boot-configuration-processor")
annotationProcessor("org.projectlombok:lombok")
testImplementation("org.springframework.boot:spring-boot-starter-test")
}

View File

@@ -0,0 +1,20 @@
/*
* Spring-boot Examples
*
* Copyright (c) 2021. Elex. All Rights Reserved.
* https://www.elex-project.com/
*/
package kr.pe.elex.examples;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

View File

@@ -0,0 +1,63 @@
/*
* Spring-boot Examples
*
* Copyright (c) 2021. Elex. All Rights Reserved.
* https://www.elex-project.com/
*/
package kr.pe.elex.examples;
import kr.pe.elex.examples.model.Person;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.util.HashMap;
import java.util.Map;
@Slf4j
@Controller
public class MyController {
@GetMapping({"/"})
public String index() {
return "home";
}
@PostMapping(value = {"/test1"})
@ResponseBody
public ResponseEntity<String> test1(@Valid @RequestBody Person person, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
log.info("ERROR: {}", person);
return ResponseEntity.badRequest().body("BAD");
} else {
log.info("OK: {}", person);
return ResponseEntity.ok("GOOD");
}
}
@PostMapping(value = {"/test2"})
@ResponseBody
public ResponseEntity<String> test2(@Valid @RequestBody Person person) {
return ResponseEntity.ok("GOOD");
}
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler({MethodArgumentNotValidException.class})
@ResponseBody
public Map<String, String> handleValidation(MethodArgumentNotValidException e) {
Map<String, String> result = new HashMap<>();
e.getBindingResult().getAllErrors().forEach(item -> {
result.put(((FieldError) item).getField(),
item.getDefaultMessage());
});
return result;
}
}

View File

@@ -0,0 +1,29 @@
/*
* Spring-boot Examples
*
* Copyright (c) 2021. Elex. All Rights Reserved.
* https://www.elex-project.com/
*/
package kr.pe.elex.examples.model;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import javax.validation.constraints.Email;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
@Data
public class Person {
@NotNull @NotEmpty
@JsonProperty
private String name;
@Min(13)
@JsonProperty
private int age;
@Email
@JsonProperty
private String email;
}

View File

@@ -0,0 +1,8 @@
/*
* Spring-boot Examples
*
* Copyright (c) 2021. Elex. All Rights Reserved.
* https://www.elex-project.com/
*/
package kr.pe.elex.examples;

View File

@@ -0,0 +1,5 @@
spring:
application:
name: My spring-boot project
server:
port: 8080

View File

@@ -0,0 +1,10 @@
('-. ('-. ) (`-.
_( OO) _( OO) ( OO ).
(,------.,--. (,------.(_/. \_)-.
| .---'| |.-') | .---' \ `.' /
| | | | OO ) | | \ /\
(| '--. | |`-' |(| '--. \ \ |
| .--'(| '---.' | .--' .' \_)
| `---.| | | `---. / .'. \
`------'`------' `------''--' '--'
powered by ELEX

View File

@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Spring-boot Examples
~
~ Copyright (c) 2020-2021. Elex. All Rights Reserved.
~ https://www.elex-project.com/
-->
<configuration>
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<include resource="org/springframework/boot/logging/logback/console-appender.xml"/>
<springProperty name="LOG_DIR" source="logging.path"
defaultValue="${user.home}/logs"/>
<property name="LOG_PATH" value="${LOG_DIR}/stephanie.log"/>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
</encoder>
</appender>
<appender name="ROLLING-FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<charset>UTF-8</charset>
<pattern>${FILE_LOG_PATTERN}</pattern>
</encoder>
<file>${LOG_PATH}</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_DIR}/sebastian_%d{yyyy-MM-dd}_%i.log.gz</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>10MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<maxHistory>60</maxHistory>
</rollingPolicy>
</appender>
<logger name="kr.pe.elex" level="debug" additivity="false">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="ROLLING-FILE"/>
</logger>
<root level="info">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="ROLLING-FILE"/>
</root>
</configuration>

View File

@@ -0,0 +1 @@
<h1>Test</h1>