first commit
Some checks failed
Vulhub Format Check and Lint / format-check (push) Has been cancelled
Vulhub Format Check and Lint / markdown-check (push) Has been cancelled
Vulhub Docker Image CI / longtime-images-test (push) Has been cancelled
Vulhub Docker Image CI / images-test (push) Has been cancelled

This commit is contained in:
2025-09-06 16:08:15 +08:00
commit 63285f61aa
2624 changed files with 88491 additions and 0 deletions

View File

@@ -0,0 +1,14 @@
FROM maven:3-jdk-8 AS builder
COPY ./ /usr/src
RUN cd /usr/src; \
mvn clean install
FROM openjdk:8-jre
LABEL maintainer="phithon <root@leavesongs.com>"
COPY --from=builder /usr/src/target/spring-data-web-example-2.0.0.RELEASE.jar /spring-data-web-example-2.0.0.RELEASE.jar
CMD ["java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "/spring-data-web-example-2.0.0.RELEASE.jar"]

View File

@@ -0,0 +1,70 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-data-web-example</artifactId>
<name>Spring Data - Basic web example</name>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.0.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-commons</artifactId>
<version>2.0.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,80 @@
/*
* Copyright 2015-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://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.
*/
package example;
import example.users.Password;
import example.users.UserManagement;
import example.users.Username;
import java.util.stream.IntStream;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.data.domain.Pageable;
import org.springframework.data.web.config.EnableSpringDataWebSupport;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
/**
* Central Spring Boot application class to bootstrap the application. Excludes Spring Security auto-configuration as we
* don't need it for the example but only want to use a {@link PasswordEncoder} (see {@link #passwordEncoder()}).
* <p>
* Spring Data web support is transparently activated by Boot for you. In case you want to manually activate it, use
* {@link EnableSpringDataWebSupport}. The core aspects of the enabled functionality shown in this example are:
* <ol>
* <li>Automatic population of a {@link Pageable} instances from request parameters (see
* {@link example.users.web.UserController#users(Pageable)})</li>
* <li>The ability to use proxy-backed interfaces to bind request payloads (see
* {@link example.users.web.UserController.UserForm})</li>
* </ol>
*
* @author Oliver Gierke
* @author Mark Paluch
*/
@SpringBootApplication
public class Application {
public static void main(String... args) {
SpringApplication.run(Application.class, args);
}
@Autowired UserManagement userManagement;
/**
* Creates a few sample users.
*/
@PostConstruct
public void init() {
IntStream.range(0, 41).forEach(index -> {
userManagement.register(new Username("user" + index), Password.raw("foobar"));
});
}
/**
* A Spring Security {@link PasswordEncoder} to encrypt passwords for newly created users, used in
* {@link UserManagement}.
*
* @return
*/
public @Bean PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}

View File

@@ -0,0 +1,76 @@
/*
* Copyright 2015-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://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.
*/
package example.users;
import javax.persistence.Embeddable;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.experimental.Delegate;
/**
* A value object to represent {@link Password}s in encrypted and unencrypted state. Note how the methods to create a
* {@link Password} in encrypted state are restricted to package scope so that only the user subsystem is actually able
* to encrypted passwords.
*
* @author Oliver Gierke
*/
@EqualsAndHashCode
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@Getter(AccessLevel.PACKAGE)
@Embeddable
public class Password implements CharSequence {
private @Delegate final String password;
private @Getter transient boolean encrypted;
Password() {
this.password = null;
this.encrypted = true;
}
/**
* Creates a new raw {@link Password} for the given source {@link String}.
*
* @param password must not be {@literal null} or empty.
* @return
*/
public static Password raw(String password) {
return new Password(password, false);
}
/**
* Creates a new encrypted {@link Password} for the given {@link String}. Note how this method is package protected so
* that encrypted passwords can only created by components in this package and not accidentally by clients using the
* type from other packages.
*
* @param password must not be {@literal null} or empty.
* @return
*/
static Password encrypted(String password) {
return new Password(password, true);
}
/*
* (non-Javadoc)
* @see java.lang.Object#toString()
*/
public String toString() {
return encrypted ? password : "********";
}
}

View File

@@ -0,0 +1,63 @@
/*
* Copyright 2015-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://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.
*/
package example.users;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.PrePersist;
import javax.persistence.PreUpdate;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
/**
* A {@link User} domain object. The primary entity of this example. Basically a combination of a {@link Username} and
* {@link Password}.
*
* @author Oliver Gierke
*/
@Entity
@Getter
@RequiredArgsConstructor
@AllArgsConstructor(access = AccessLevel.PACKAGE)
@EqualsAndHashCode(of = "id")
public class User {
private @GeneratedValue @Id Long id;
private final Username username;
private final Password password;
User() {
this.username = null;
this.password = null;
}
/**
* Makes sure only {@link User}s with encrypted {@link Password} can be persisted.
*/
@PrePersist
@PreUpdate
void assertEncrypted() {
if (!password.isEncrypted()) {
throw new IllegalStateException("Tried to persist/load a user with a non-encrypted password!");
}
}
}

View File

@@ -0,0 +1,90 @@
/*
* Copyright 2015-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://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.
*/
package example.users;
import java.util.Optional;
import javax.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
/**
* Domain service to register {@link User}s in the system.
*
* @author Oliver Gierke
*/
@Transactional
@Service
@RequiredArgsConstructor
public class UserManagement {
private final UserRepository repository;
private final PasswordEncoder encoder;
/**
* Registers a {@link User} with the given {@link Username} and {@link Password}.
*
* @param username must not be {@literal null}.
* @param password must not be {@literal null}.
* @return
*/
public User register(Username username, Password password) {
Assert.notNull(username, "Username must not be null!");
Assert.notNull(password, "Password must not be null!");
repository.findByUsername(username).ifPresent(user -> {
throw new IllegalArgumentException("User with that name already exists!");
});
Password encryptedPassword = Password.encrypted(encoder.encode(password));
return repository.save(new User(username, encryptedPassword));
}
/**
* Returns a {@link Page} of {@link User} for the given {@link Pageable}.
*
* @param pageable must not be {@literal null}.
* @return
*/
public Page<User> findAll(Pageable pageable) {
Assert.notNull(pageable, "Pageable must not be null!");
return repository.findAll(pageable);
}
/**
* Returns the {@link User} with the given {@link Username}.
*
* @param username must not be {@literal null}.
* @return
*/
public Optional<User> findByUsername(Username username) {
Assert.notNull(username, "Username must not be null!");
return repository.findByUsername(username);
}
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright 2015-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://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.
*/
package example.users;
import java.util.Optional;
import org.springframework.data.repository.PagingAndSortingRepository;
/**
* A Spring Data repository to persist {@link User}s.
*
* @author Oliver Gierke
*/
interface UserRepository extends PagingAndSortingRepository<User, Long> {
/**
* Returns the user with the given {@link Username}.
*
* @param username can be {@literal null}.
* @return
*/
Optional<User> findByUsername(Username username);
}

View File

@@ -0,0 +1,61 @@
/*
* Copyright 2015-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://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.
*/
package example.users;
import javax.persistence.Embeddable;
import lombok.EqualsAndHashCode;
import org.springframework.util.StringUtils;
/**
* value object to represent user names.
*
* @author Oliver Gierke
*/
@EqualsAndHashCode
@Embeddable
public class Username {
private final String username;
Username() {
this.username = null;
}
/**
* Creates a new {@link Username}.
*
* @param username must not be {@literal null} or empty.
*/
public Username(String username) {
if (!StringUtils.hasText(username)) {
throw new IllegalArgumentException("Invalid username!");
}
this.username = username;
}
/*
* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return username;
}
}

View File

@@ -0,0 +1,155 @@
/*
* Copyright 2015-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://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.
*/
package example.users.web;
import static org.springframework.validation.ValidationUtils.*;
import java.util.Map;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.web.PageableDefault;
import org.springframework.data.web.PageableHandlerMethodArgumentResolver;
import org.springframework.data.web.config.EnableSpringDataWebSupport;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.view.RedirectView;
import example.users.Password;
import example.users.User;
import example.users.UserManagement;
import example.users.Username;
/**
* A sample controller implementation to showcase Spring Data web support:
* <ol>
* <li>Automatic population of a {@link Pageable} instance as controller method argument. This is achieved by the
* automatic activation of {@link EnableSpringDataWebSupport} and in turn its registration of a
* {@link PageableHandlerMethodArgumentResolver}.</li>
* <li>Usage of proxy-backed interfaces to bind request parameters.</li>
* </ol>
*
* @author Oliver Gierke
*/
@Controller
@RequiredArgsConstructor
@RequestMapping("/users")
class UserController {
private final UserManagement userManagement;
/**
* Equis the model with a {@link Page} of {@link User}s. Spring Data automatically populates the {@link Pageable} from
* request data according to the setup of {@link PageableHandlerMethodArgumentResolver}. Note how the defaults can be
* tweaked by using {@link PageableDefault}.
*
* @param pageable will never be {@literal null}.
* @return
*/
@ModelAttribute("users")
public Page<User> users(@PageableDefault(size = 5) Pageable pageable) {
return userManagement.findAll(pageable);
}
/**
* Registers a new {@link User} for the data provided by the given {@link UserForm}. Note, how an interface is used to
* bind request parameters.
*
* @param userForm the request data bound to the {@link UserForm} instance.
* @param binding the result of the binding operation.
* @param model the Spring MVC {@link Model}.
* @return
*/
@RequestMapping(method = RequestMethod.POST)
public Object register(UserForm userForm, BindingResult binding, Model model) {
userForm.validate(binding, userManagement);
if (binding.hasErrors()) {
return "users";
}
userManagement.register(new Username(userForm.getUsername()), Password.raw(userForm.getPassword()));
RedirectView redirectView = new RedirectView("redirect:/users");
redirectView.setPropagateQueryParams(true);
return redirectView;
}
/**
* Populates the {@link Model} with the {@link UserForm} automatically created by Spring Data web components. It will
* create a {@link Map}-backed proxy for the interface.
*
* @param model will never be {@literal null}.
* @param userForm will never be {@literal null}.
* @return
*/
@RequestMapping(method = RequestMethod.GET)
public String listUsers(Model model, UserForm userForm) {
model.addAttribute("userForm", userForm);
return "users";
}
/**
* An interface to represent the form to be used
*
* @author Oliver Gierke
*/
interface UserForm {
String getUsername();
String getPassword();
String getRepeatedPassword();
/**
* Validates the {@link UserForm}.
*
* @param errors
* @param userManagement
*/
default void validate(BindingResult errors, UserManagement userManagement) {
rejectIfEmptyOrWhitespace(errors, "username", "user.username.empty");
rejectIfEmptyOrWhitespace(errors, "password", "user.password.empty");
rejectIfEmptyOrWhitespace(errors, "repeatedPassword", "user.repeatedPassword.empty");
if (!getPassword().equals(getRepeatedPassword())) {
errors.rejectValue("repeatedPassword", "user.password.no-match");
}
try {
userManagement.findByUsername(new Username(getUsername())).ifPresent(
user -> errors.rejectValue("username", "user.username.exists"));
} catch (IllegalArgumentException o_O) {
errors.rejectValue("username", "user.username.invalidFormat");
}
}
}
}

View File

@@ -0,0 +1 @@
spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration,org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration

View File

@@ -0,0 +1,7 @@
user.username.empty = Username is required!
user.username.exists = Username already exists!
user.username.invalidFormat = Invalid Username format!
user.password.empty = Password is required!
user.password.no-match = The given passwords don't match!
user.repeatedPassword.empty = Repeated password is required!

View File

@@ -0,0 +1,28 @@
body {
margin: auto;
width: 50%;
}
form.user-form {
padding: 9px 14px;
border: 1px solid #e1e1e8;
border-radius: 4px;
}
.fieldError {
border: 1px solid #a94442;
}
.errors {
padding: 1em;
margin: 1em 0;
border: 1px solid #eee;
border-left-width: 5px;
border-left-color: #a94442;
border-radius: 5px;
}
.errors li {
list-style-type: none;
margin: 0.5em 0.7em;
}

View File

@@ -0,0 +1,53 @@
<!DOCTYPE html SYSTEM "http://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-spring4-4.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
<title>Users</title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css" />
<link rel="stylesheet" th:href="@{/css/style.css}" type="text/css" />
</head>
<body>
<h1>Users</h1>
<nav>
<ul class="pagination" th:with="total = ${users.totalPages}">
<li th:if="${users.hasPrevious()}">
<a th:href="@{/users(page=${users.previousPageable().pageNumber},size=${users.size})}" aria-label="Previous">
<span aria-hidden="true">&laquo;</span>
</a>
</li>
<li th:each="page : ${#numbers.sequence(0, total - 1)}"><a th:href="@{/users(page=${page},size=${users.size})}" th:text="${page + 1}">1</a></li>
<li th:if="${users.hasNext()}">
<a th:href="@{/users(page=${users.nextPageable().pageNumber},size=${users.size})}" aria-label="Next">
<span aria-hidden="true">&raquo;</span>
</a>
</li>
</ul>
</nav>
<ol>
<li th:each="user, it : ${users}" th:with="index = ${it.count}" th:text="${user.username + ' - ' + user.password}" th:value="${users.number * users.size + index}">Username</li>
</ol>
<form method="post" role="form" class="gb-form" id="form" th:action="@{/users(page=${page},size=${users.size})}" th:object="${userForm}">
<ul th:if="${#fields.hasErrors('*')}" class="errors" >
<li th:each="error : ${#fields.errors('*')}" th:text="${error}" class="text-danger">Input is incorrect</li>
</ul>
<div class="form-group">
<label for="username">Username</label><br />
<input type="text" th:field="*{username}" class="form-control" th:errorclass="fieldError" required="required" />
</div>
<div class="form-group">
<label for="password">Password</label><br />
<input type="password" th:field="*{password}" class="form-control" th:errorclass="fieldError" required="required" />
</div>
<div class="form-group">
<label for="repeatedPassword">Password (repeated)</label><br />
<input type="password" th:field="*{repeatedPassword}" class="form-control" th:errorclass="fieldError" required="required" />
</div>
<input type="submit" class="btn btn-default" value="Register user" />
</form>
</body>
</html>