2021-08-02
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -3,3 +3,4 @@
|
|||||||
/buildSrc/build/
|
/buildSrc/build/
|
||||||
/.idea/
|
/.idea/
|
||||||
/build/
|
/build/
|
||||||
|
/**/build/**
|
||||||
|
|||||||
17
README.md
Normal file
17
README.md
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# Spring-boot Examples
|
||||||
|
|
||||||
|
## File Upload
|
||||||
|
|
||||||
|
## Security
|
||||||
|
|
||||||
|
## Security plus JPA
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
## Validation
|
||||||
|
|
||||||
|
|
||||||
|
------
|
||||||
|
developed by Elex
|
||||||
|
|
||||||
|
https://www.elex-project.com/
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
plugins {
|
|
||||||
id("elex-application")
|
|
||||||
}
|
|
||||||
application{
|
|
||||||
mainClass.set("com.elex_project.sample.Application")
|
|
||||||
}
|
|
||||||
dependencies {
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
package com.elex_project.sample;
|
|
||||||
@@ -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")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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}"
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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")
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {
|
|
||||||
|
|
||||||
}
|
|
||||||
18
buildSrc/src/main/kotlin/elex-spring-boot.gradle.kts
Normal file
18
buildSrc/src/main/kotlin/elex-spring-boot.gradle.kts
Normal 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
1
file-upload/README.md
Normal file
@@ -0,0 +1 @@
|
|||||||
|
# 파일 업로드
|
||||||
28
file-upload/build.gradle.kts
Normal file
28
file-upload/build.gradle.kts
Normal 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")
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
/*
|
||||||
|
* Spring-boot Examples
|
||||||
|
*
|
||||||
|
* Copyright (c) 2021. Elex. All Rights Reserved.
|
||||||
|
* https://www.elex-project.com/
|
||||||
|
*/
|
||||||
|
|
||||||
|
package kr.pe.elex.examples;
|
||||||
9
file-upload/src/main/resources/application.yaml
Normal file
9
file-upload/src/main/resources/application.yaml
Normal 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
|
||||||
10
file-upload/src/main/resources/banner.txt
Normal file
10
file-upload/src/main/resources/banner.txt
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
('-. ('-. ) (`-.
|
||||||
|
_( OO) _( OO) ( OO ).
|
||||||
|
(,------.,--. (,------.(_/. \_)-.
|
||||||
|
| .---'| |.-') | .---' \ `.' /
|
||||||
|
| | | | OO ) | | \ /\
|
||||||
|
(| '--. | |`-' |(| '--. \ \ |
|
||||||
|
| .--'(| '---.' | .--' .' \_)
|
||||||
|
| `---.| | | `---. / .'. \
|
||||||
|
`------'`------' `------''--' '--'
|
||||||
|
powered by ELEX
|
||||||
48
file-upload/src/main/resources/logback-spring.xml
Normal file
48
file-upload/src/main/resources/logback-spring.xml
Normal 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>
|
||||||
15
file-upload/src/main/resources/templates/main.mustache
Normal file
15
file-upload/src/main/resources/templates/main.mustache
Normal 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>
|
||||||
7
gradle/wrapper/gradle-wrapper.properties
vendored
7
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -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
15
gradlew
vendored
@@ -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.
|
|
||||||
#
|
#
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
plugins {
|
|
||||||
id("elex-library")
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
package com.elex_project.sample;
|
|
||||||
@@ -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" />
|
||||||
|
|||||||
11
security-with-jpa/README.md
Normal file
11
security-with-jpa/README.md
Normal 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
|
||||||
33
security-with-jpa/build.gradle.kts
Normal file
33
security-with-jpa/build.gradle.kts
Normal 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")
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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");
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
/*
|
||||||
|
* Spring-boot Examples
|
||||||
|
*
|
||||||
|
* Copyright (c) 2021. Elex. All Rights Reserved.
|
||||||
|
* https://www.elex-project.com/
|
||||||
|
*/
|
||||||
|
|
||||||
|
package kr.pe.elex.examples;
|
||||||
17
security-with-jpa/src/main/resources/application.yaml
Normal file
17
security-with-jpa/src/main/resources/application.yaml
Normal 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
|
||||||
10
security-with-jpa/src/main/resources/banner.txt
Normal file
10
security-with-jpa/src/main/resources/banner.txt
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
('-. ('-. ) (`-.
|
||||||
|
_( OO) _( OO) ( OO ).
|
||||||
|
(,------.,--. (,------.(_/. \_)-.
|
||||||
|
| .---'| |.-') | .---' \ `.' /
|
||||||
|
| | | | OO ) | | \ /\
|
||||||
|
(| '--. | |`-' |(| '--. \ \ |
|
||||||
|
| .--'(| '---.' | .--' .' \_)
|
||||||
|
| `---.| | | `---. / .'. \
|
||||||
|
`------'`------' `------''--' '--'
|
||||||
|
powered by ELEX
|
||||||
44
security-with-jpa/src/main/resources/logback-spring.xml
Normal file
44
security-with-jpa/src/main/resources/logback-spring.xml
Normal 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>
|
||||||
15
security-with-jpa/src/main/resources/templates/home.mustache
Normal file
15
security-with-jpa/src/main/resources/templates/home.mustache
Normal 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}}
|
||||||
@@ -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>
|
||||||
@@ -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}}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
<h1>아무나 볼 수 있는 페이지</h1>
|
||||||
|
<p>This page doesn't contain any Important messages.</p>
|
||||||
|
{{> links}}
|
||||||
@@ -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
7
security/README.md
Normal 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
30
security/build.gradle.kts
Normal 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")
|
||||||
|
}
|
||||||
20
security/src/main/java/kr/pe/elex/examples/Application.java
Normal file
20
security/src/main/java/kr/pe/elex/examples/Application.java
Normal 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
37
security/src/main/java/kr/pe/elex/examples/MyController.java
Normal file
37
security/src/main/java/kr/pe/elex/examples/MyController.java
Normal 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";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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")
|
||||||
|
;
|
||||||
|
}
|
||||||
|
}
|
||||||
23
security/src/main/java/kr/pe/elex/examples/WebConfig.java
Normal file
23
security/src/main/java/kr/pe/elex/examples/WebConfig.java
Normal 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");
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
/*
|
||||||
|
* Spring-boot Examples
|
||||||
|
*
|
||||||
|
* Copyright (c) 2021. Elex. All Rights Reserved.
|
||||||
|
* https://www.elex-project.com/
|
||||||
|
*/
|
||||||
|
|
||||||
|
package kr.pe.elex.examples;
|
||||||
7
security/src/main/resources/application.yaml
Normal file
7
security/src/main/resources/application.yaml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
spring:
|
||||||
|
application:
|
||||||
|
name: My spring-boot project
|
||||||
|
mustache:
|
||||||
|
expose-request-attributes: true # 뷰에서 CSRF를 뿌려주기 위해서 필요합니다.
|
||||||
|
server:
|
||||||
|
port: 8080
|
||||||
10
security/src/main/resources/banner.txt
Normal file
10
security/src/main/resources/banner.txt
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
('-. ('-. ) (`-.
|
||||||
|
_( OO) _( OO) ( OO ).
|
||||||
|
(,------.,--. (,------.(_/. \_)-.
|
||||||
|
| .---'| |.-') | .---' \ `.' /
|
||||||
|
| | | | OO ) | | \ /\
|
||||||
|
(| '--. | |`-' |(| '--. \ \ |
|
||||||
|
| .--'(| '---.' | .--' .' \_)
|
||||||
|
| `---.| | | `---. / .'. \
|
||||||
|
`------'`------' `------''--' '--'
|
||||||
|
powered by ELEX
|
||||||
44
security/src/main/resources/logback-spring.xml
Normal file
44
security/src/main/resources/logback-spring.xml
Normal 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>
|
||||||
4
security/src/main/resources/templates/home.mustache
Normal file
4
security/src/main/resources/templates/home.mustache
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<h1>홈</h1>
|
||||||
|
<p>테스트 페이지입니다.</p>
|
||||||
|
|
||||||
|
{{> links}}
|
||||||
6
security/src/main/resources/templates/links.mustache
Normal file
6
security/src/main/resources/templates/links.mustache
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<div>
|
||||||
|
<a href="/">홈</a> |
|
||||||
|
<a href="/info">일반</a> |
|
||||||
|
<a href="/secure">중요</a> |
|
||||||
|
<a href="/login">로그인</a>
|
||||||
|
</div>
|
||||||
11
security/src/main/resources/templates/login.mustache
Normal file
11
security/src/main/resources/templates/login.mustache
Normal 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}}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
<h1>아무나 볼 수 있는 페이지</h1>
|
||||||
|
<p>This page doesn't contain any Important messages.</p>
|
||||||
|
{{> links}}
|
||||||
@@ -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}}
|
||||||
@@ -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
27
testing/build.gradle.kts
Normal 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")
|
||||||
|
}
|
||||||
20
testing/src/main/java/kr/pe/elex/examples/Application.java
Normal file
20
testing/src/main/java/kr/pe/elex/examples/Application.java
Normal 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
41
testing/src/main/java/kr/pe/elex/examples/MyController.java
Normal file
41
testing/src/main/java/kr/pe/elex/examples/MyController.java
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
17
testing/src/main/java/kr/pe/elex/examples/MyService.java
Normal file
17
testing/src/main/java/kr/pe/elex/examples/MyService.java
Normal 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.";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
/*
|
||||||
|
* Spring-boot Examples
|
||||||
|
*
|
||||||
|
* Copyright (c) 2021. Elex. All Rights Reserved.
|
||||||
|
* https://www.elex-project.com/
|
||||||
|
*/
|
||||||
|
|
||||||
|
package kr.pe.elex.examples;
|
||||||
2
testing/src/main/resources/templates/main.mustache
Normal file
2
testing/src/main/resources/templates/main.mustache
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
<h1>HOME</h1>
|
||||||
|
<p>Hello, {{name}}!</p>
|
||||||
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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")));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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
29
validation/README.md
Normal 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
|
||||||
29
validation/build.gradle.kts
Normal file
29
validation/build.gradle.kts
Normal 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")
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
/*
|
||||||
|
* Spring-boot Examples
|
||||||
|
*
|
||||||
|
* Copyright (c) 2021. Elex. All Rights Reserved.
|
||||||
|
* https://www.elex-project.com/
|
||||||
|
*/
|
||||||
|
|
||||||
|
package kr.pe.elex.examples;
|
||||||
5
validation/src/main/resources/application.yaml
Normal file
5
validation/src/main/resources/application.yaml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
spring:
|
||||||
|
application:
|
||||||
|
name: My spring-boot project
|
||||||
|
server:
|
||||||
|
port: 8080
|
||||||
10
validation/src/main/resources/banner.txt
Normal file
10
validation/src/main/resources/banner.txt
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
('-. ('-. ) (`-.
|
||||||
|
_( OO) _( OO) ( OO ).
|
||||||
|
(,------.,--. (,------.(_/. \_)-.
|
||||||
|
| .---'| |.-') | .---' \ `.' /
|
||||||
|
| | | | OO ) | | \ /\
|
||||||
|
(| '--. | |`-' |(| '--. \ \ |
|
||||||
|
| .--'(| '---.' | .--' .' \_)
|
||||||
|
| `---.| | | `---. / .'. \
|
||||||
|
`------'`------' `------''--' '--'
|
||||||
|
powered by ELEX
|
||||||
47
validation/src/main/resources/logback-spring.xml
Normal file
47
validation/src/main/resources/logback-spring.xml
Normal 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>
|
||||||
1
validation/src/main/resources/templates/home.mustache
Normal file
1
validation/src/main/resources/templates/home.mustache
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<h1>Test</h1>
|
||||||
Reference in New Issue
Block a user