├── .editorconfig
├── .github
├── dependabot.yml
├── mergify.yml
└── workflows
│ └── gradle.yml
├── .gitignore
├── LICENSE
├── README.md
├── build.gradle
├── docs
└── Spring Framework Presentation.pdf
├── example-00-hello
├── build.gradle
└── src
│ └── main
│ ├── java
│ └── io
│ │ └── sfe
│ │ └── hello
│ │ ├── Main.java
│ │ └── Note.java
│ └── resources
│ └── applicationContext.xml
├── example-01-bean-factory
├── build.gradle
└── src
│ └── main
│ ├── java
│ └── io
│ │ └── sfe
│ │ └── beanfactory
│ │ ├── Main.java
│ │ └── Note.java
│ └── resources
│ └── applicationContext.xml
├── example-02-bean-definition
├── build.gradle
└── src
│ └── main
│ ├── java
│ └── io
│ │ └── sfe
│ │ └── beandefinition
│ │ ├── Main.java
│ │ ├── Note.java
│ │ └── NoteService.java
│ └── resources
│ └── applicationContext.xml
├── example-03-bean-scope
├── build.gradle
└── src
│ └── main
│ ├── java
│ └── io
│ │ └── sfe
│ │ └── beanscope
│ │ ├── Main.java
│ │ └── Note.java
│ └── resources
│ └── applicationContext.xml
├── example-04-bean-lifecycle
├── build.gradle
└── src
│ └── main
│ ├── java
│ └── io
│ │ └── sfe
│ │ └── beanlifecycle
│ │ ├── LifecycleBean.java
│ │ └── Main.java
│ └── resources
│ └── applicationContext.xml
├── example-05-dependency-injection
├── build.gradle
└── src
│ └── main
│ ├── java
│ └── io
│ │ └── sfe
│ │ └── dependencyinjection
│ │ ├── Author.java
│ │ ├── Main.java
│ │ └── Note.java
│ └── resources
│ └── applicationContext.xml
├── example-06-annotation-config
├── build.gradle
└── src
│ └── main
│ └── java
│ └── io
│ └── sfe
│ └── annotationconfig
│ ├── Main.java
│ ├── Note.java
│ ├── NoteRepository.java
│ ├── NoteService.java
│ ├── NoteValidator.java
│ └── config
│ └── AppConfig.java
├── example-07-java-config
├── build.gradle
└── src
│ └── main
│ └── java
│ └── io
│ └── sfe
│ └── javaconfig
│ ├── Main.java
│ ├── Note.java
│ ├── NoteRepository.java
│ ├── NoteService.java
│ ├── config
│ ├── AppConfig.java
│ └── DbConfig.java
│ └── db
│ └── DbConnectionProvider.java
├── example-08-properties
├── build.gradle
└── src
│ └── main
│ ├── java
│ └── io
│ │ └── sfe
│ │ └── properties
│ │ ├── Main.java
│ │ ├── config
│ │ ├── PropertiesAnnotationConfig.java
│ │ └── PropertiesJavaConfig.java
│ │ └── db
│ │ └── DbConnectionProvider.java
│ └── resources
│ └── db.properties
├── example-09-dispatcher-servlet
├── build.gradle
└── src
│ └── main
│ └── java
│ └── io
│ └── sfe
│ └── dispatcher
│ ├── HelloController.java
│ └── config
│ ├── AppInitializer.java
│ └── WebAppConfig.java
├── example-10-spring-mvc
├── build.gradle
└── src
│ └── main
│ ├── java
│ └── io
│ │ └── sfe
│ │ └── springmvc
│ │ ├── Note.java
│ │ ├── NoteController.java
│ │ └── config
│ │ ├── AppInitializer.java
│ │ └── WebAppConfig.java
│ └── webapp
│ └── WEB-INF
│ └── views
│ └── notes.html
├── example-11-spring-boot
├── build.gradle
└── src
│ ├── main
│ ├── java
│ │ └── io
│ │ │ └── sfe
│ │ │ └── springboot
│ │ │ ├── NoteApplication.java
│ │ │ └── note
│ │ │ ├── Note.java
│ │ │ └── NoteController.java
│ └── resources
│ │ ├── application.properties
│ │ └── templates
│ │ └── notes.html
│ └── test
│ └── java
│ └── io
│ └── sfe
│ └── springboot
│ └── NoteApplicationTests.java
├── example-12-boot-security
├── build.gradle
└── src
│ └── main
│ ├── java
│ └── io
│ │ └── sfe
│ │ └── bootsecurity
│ │ └── SecuredApplication.java
│ └── resources
│ ├── application.properties
│ └── templates
│ └── index.html
├── example-13-inmemory-security
├── build.gradle
└── src
│ └── main
│ ├── java
│ └── io
│ │ └── sfe
│ │ └── bootsecurity
│ │ ├── InMemorySecurityApplication.java
│ │ ├── InMemorySecurityConfig.java
│ │ └── IndexController.java
│ └── resources
│ ├── application.properties
│ └── templates
│ └── index.html
├── example-14-jdbc-auth-security
├── build.gradle
└── src
│ └── main
│ ├── java
│ └── io
│ │ └── sfe
│ │ └── jdbcsecurity
│ │ ├── DatabaseConfig.java
│ │ ├── JdbcAuthApplication.java
│ │ ├── PasswordEncoderConfig.java
│ │ └── SecurityConfig.java
│ └── resources
│ ├── application.properties
│ └── templates
│ └── index.html
├── example-15-user-flow-security
├── build.gradle
└── src
│ ├── main
│ ├── java
│ │ └── io
│ │ │ └── sfe
│ │ │ └── userflow
│ │ │ ├── UserFlowApplication.java
│ │ │ ├── config
│ │ │ ├── DatabaseConfig.java
│ │ │ └── SecurityConfig.java
│ │ │ └── user
│ │ │ ├── UserController.java
│ │ │ └── UserService.java
│ └── resources
│ │ ├── application.properties
│ │ └── templates
│ │ └── index.html
│ └── test
│ └── java
│ └── io
│ └── sfe
│ └── userflow
│ └── user
│ ├── UserControllerTest.java
│ └── UserServiceTest.java
├── example-16-custom-auth-provider
├── build.gradle
└── src
│ └── main
│ ├── java
│ └── io
│ │ └── sfe
│ │ └── customauth
│ │ ├── CustomAuthApplication.java
│ │ ├── auth
│ │ └── LoginPasswordAuthenticationProvider.java
│ │ └── config
│ │ ├── CommonSecurityConfig.java
│ │ ├── DatabaseConfig.java
│ │ ├── SecurityConfig.java
│ │ └── WebConfig.java
│ └── resources
│ ├── application.properties
│ └── templates
│ ├── index.html
│ └── login.html
├── example-17-authorization
├── build.gradle
└── src
│ ├── main
│ ├── java
│ │ └── io
│ │ │ └── sfe
│ │ │ └── authorization
│ │ │ ├── AccessCheckController.java
│ │ │ ├── InMemorySecurityConfig.java
│ │ │ └── UserAuthorizationApplication.java
│ └── resources
│ │ └── application.properties
│ └── test
│ └── java
│ └── io
│ └── sfe
│ └── authorization
│ └── AccessCheckControllerTest.java
├── example-18-method-security
├── build.gradle
└── src
│ ├── main
│ └── java
│ │ └── io
│ │ └── sfe
│ │ └── methodsecurity
│ │ ├── AccessCheckController.java
│ │ ├── InMemorySecurityConfig.java
│ │ └── MethodAuthorizationApplication.java
│ └── test
│ └── java
│ └── io
│ └── sfe
│ └── methodsecurity
│ └── AccessCheckControllerTest.java
├── example-19-remember-me
├── build.gradle
└── src
│ └── main
│ ├── java
│ └── io
│ │ └── sfe
│ │ └── rememberme
│ │ ├── RememberMeApplication.java
│ │ └── SecurityConfig.java
│ └── resources
│ ├── application.properties
│ └── templates
│ └── index.html
├── example-20-oauth
├── build.gradle
└── src
│ ├── main
│ ├── java
│ │ └── io
│ │ │ └── sfe
│ │ │ └── oauth
│ │ │ ├── OAuthApplication.java
│ │ │ ├── SecurityConfig.java
│ │ │ └── UserController.java
│ └── resources
│ │ ├── application.properties
│ │ └── templates
│ │ └── index.html
│ └── test
│ └── java
│ └── io
│ └── sfe
│ └── oauth
│ └── UserControllerTest.java
├── example-21-jwt
├── build.gradle
└── src
│ └── main
│ ├── java
│ └── io
│ │ └── sfe
│ │ └── jwt
│ │ ├── AppConfig.java
│ │ ├── JwtApplication.java
│ │ ├── UserController.java
│ │ └── security
│ │ ├── CommonSecurityConfig.java
│ │ ├── SecurityConfig.java
│ │ └── jwt
│ │ ├── JwtAuthenticationFilter.java
│ │ ├── JwtAuthorizationFilter.java
│ │ ├── JwtTokenGenerator.java
│ │ ├── JwtTokenProvider.java
│ │ ├── JwtTokenUtil.java
│ │ └── UserDetailsExtractor.java
│ └── resources
│ └── application.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── notes-app
├── build.gradle
└── src
│ ├── main
│ ├── java
│ │ └── io
│ │ │ └── sfe
│ │ │ └── notesapp
│ │ │ ├── NotesApplication.java
│ │ │ ├── domain
│ │ │ └── note
│ │ │ │ ├── Note.java
│ │ │ │ └── NoteService.java
│ │ │ ├── storage
│ │ │ ├── author
│ │ │ │ ├── AuthorEntity.java
│ │ │ │ └── AuthorRepository.java
│ │ │ └── note
│ │ │ │ ├── NoteEntity.java
│ │ │ │ ├── NoteJdbcTemplateRepository.java
│ │ │ │ └── NoteRepository.java
│ │ │ └── web
│ │ │ ├── IndexController.java
│ │ │ ├── common
│ │ │ ├── ControllerExceptionHandler.java
│ │ │ └── LoggerInterceptor.java
│ │ │ ├── config
│ │ │ └── WebConfig.java
│ │ │ └── note
│ │ │ ├── NoteController.java
│ │ │ └── NoteDto.java
│ └── resources
│ │ ├── application-prod.properties
│ │ ├── application.properties
│ │ ├── schema.sql
│ │ └── templates
│ │ ├── index.html
│ │ └── note
│ │ ├── create-note.html
│ │ ├── note.html
│ │ ├── notes.html
│ │ └── update-note.html
│ └── test
│ ├── java
│ └── io
│ │ └── sfe
│ │ └── notesapp
│ │ ├── NotesApplicationTest.java
│ │ ├── TestPropertySourcesTest.java
│ │ ├── domain
│ │ └── note
│ │ │ ├── NoteServiceContextConfigurationTest.java
│ │ │ ├── NoteServiceIntegrationTest.java
│ │ │ └── NoteServiceTest.java
│ │ ├── storage
│ │ └── note
│ │ │ ├── NoteJdbcTemplateRepositoryTest.java
│ │ │ ├── NoteRepositoryTest.java
│ │ │ └── NoteTableSchemaTest.java
│ │ └── web
│ │ ├── IndexControllerTest.java
│ │ └── note
│ │ ├── NoteControllerIntegrationTest.java
│ │ └── NoteControllerTest.java
│ └── resources
│ └── test.properties
└── settings.gradle
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: gradle
4 | directory: "/"
5 | schedule:
6 | interval: daily
7 | time: "03:00"
8 | open-pull-requests-limit: 10
9 | ignore:
10 | - dependency-name: org.apache.tomcat.embed:tomcat-embed-jasper
11 | versions:
12 | - 10.0.2
13 | - 10.0.4
14 | - dependency-name: org.apache.tomcat.embed:tomcat-embed-core
15 | versions:
16 | - 10.0.2
17 | - 10.0.4
18 | - dependency-name: org.webjars:bootstrap
19 | versions:
20 | - 4.6.0
21 | - dependency-name: org.springframework:spring-webmvc
22 | versions:
23 | - 5.3.4
24 | - dependency-name: org.springframework:spring-web
25 | versions:
26 | - 5.3.4
27 | - dependency-name: org.springframework:spring-context
28 | versions:
29 | - 5.3.4
30 |
--------------------------------------------------------------------------------
/.github/mergify.yml:
--------------------------------------------------------------------------------
1 | pull_request_rules:
2 | - name: Automatic merge ⬇️ for Dependabot pull requests
3 | conditions:
4 | - author~=^dependabot(|-preview)\[bot\]$
5 | - check-success=build
6 | actions:
7 | merge:
8 | method: merge
--------------------------------------------------------------------------------
/.github/workflows/gradle.yml:
--------------------------------------------------------------------------------
1 | name: build
2 |
3 | on: [push]
4 |
5 | jobs:
6 | build:
7 | runs-on: ubuntu-latest
8 |
9 | steps:
10 | - uses: actions/checkout@v1
11 |
12 | - name: Set up JDK 17
13 | uses: actions/setup-java@v2
14 | with:
15 | java-version: '17'
16 | distribution: 'temurin'
17 |
18 | - name: Build with Gradle
19 | run: ./gradlew build
20 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled class file
2 | *.class
3 |
4 | # Package Files #
5 | *.jar
6 | *.war
7 | *.nar
8 | *.ear
9 | *.zip
10 | *.tar.gz
11 | *.rar
12 |
13 | # IntelliJ
14 | out/
15 | .idea/
16 |
17 | ### Gradle template
18 | .gradle
19 | **/build/
20 |
21 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
22 | !gradle-wrapper.jar
23 |
24 | **/secrets.properties
25 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 vrudas
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Spring Framework Examples
7 |
8 |
9 | An educational project with Spring Framework examples. Used for lectures at courses.
10 |
11 |
12 | ## Related links
13 | - [Spring Framework Documentation](https://docs.spring.io/spring-framework/docs/current/reference/html/)
14 | - [Spring Core Technologies](https://docs.spring.io/spring-framework/docs/current/reference/html/core.html)
15 | - [Spring Guides](https://spring.io/guides)
16 | - [Spring Quickstart Guide](https://spring.io/quickstart)
17 | - [Properties with Spring and Spring Boot](https://www.baeldung.com/properties-with-spring)
18 | - [An Intro to the Spring DispatcherServlet](https://www.baeldung.com/spring-dispatcherservlet)
19 | - [Design Pattern - Front Controller Pattern](https://www.tutorialspoint.com/design_pattern/front_controller_pattern.htm)
20 | - [Introduction to Using Thymeleaf in Spring](https://www.baeldung.com/thymeleaf-in-spring-mvc)
21 | - [Servlet Filter and Handler Interceptor](https://medium.com/techno101/servlet-filter-and-handler-interceptor-spring-boot-implementation-b58d397d9dbd)
22 | - [Error Handling for REST with Spring](https://www.baeldung.com/exception-handling-for-rest-with-spring)
23 | - [Spring 5, Embedded Tomcat 8, and Gradle: a Quick Tutorial](https://auth0.com/blog/spring-5-embedded-tomcat-8-gradle-tutorial/)
24 | - [Spring Boot Reference Documentation](https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/)
25 |
26 | ## Spring Security 6 Migration links
27 | - [Migrating to 6.0](https://docs.spring.io/spring-security/reference/migration/index.html)
28 | - [Spring Security without the WebSecurityConfigurerAdapter](https://spring.io/blog/2022/02/21/spring-security-without-the-websecurityconfigureradapter)
29 |
30 | ## Important information
31 | - The module [example-17-authorization](example-17-authorization) has an issue https://github.com/vrudas/spring-framework-examples/issues/101 that was caused because of update to Spring Security 6
32 |
33 | ## Example 21 - JWT Instructions
34 |
35 | Please note that IntelliJ IDEA [HTTP Client](https://blog.jetbrains.com/idea/2020/09/at-your-request-use-the-http-client-in-intellij-idea-for-spring-boot-restful-web-services/) was used to perform requests in code snippets
36 |
37 | Please follow the steps to perform a demo of how to get a JWT token for an existing user:
38 |
39 | - Perform login action
40 | ```HTTP request
41 | POST http://localhost:8080/login?username=user&password=user
42 | Accept: application/json
43 | ```
44 |
45 | - Extract the generated Bearer token from a response header `Authorization: Bearer `
46 | ```
47 | HTTP/1.1 200
48 | Vary: Origin
49 | Vary: Access-Control-Request-Method
50 | Vary: Access-Control-Request-Headers
51 | Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ1c2VyIiwiZXhwIjoxNjc4ODM2OTY1fQ.Afagk8no-r2kUiDOdtjWMT06gYPHkrhCoOSoK5_X6k8BC8Lr6k5rB-9gyoE72-lkd0rx1sEPET-3Uf7KP-7BrQ
52 | X-Content-Type-Options: nosniff
53 | X-XSS-Protection: 0
54 | Cache-Control: no-cache, no-store, max-age=0, must-revalidate
55 | Pragma: no-cache
56 | Expires: 0
57 | X-Frame-Options: DENY
58 | Content-Length: 0
59 | Date: Tue, 14 Mar 2023 23:35:05 GMT
60 | Keep-Alive: timeout=60
61 | Connection: keep-alive
62 |
63 |
64 | ```
65 |
66 | - Use the generated Bearer token to perform the call to an endpoint by providing the `Authorization: Bearer ` header
67 | ```HTTP request
68 | GET http://localhost:8080/users/me
69 | Accept: application/json
70 | Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ1c2VyIiwiZXhwIjoxNjc4ODM1NjE5fQ.cRZ1ob4XZfG5RnU0jl2kdPihc9Ln-BlEOe7hbuwZJWp-UuQSGukI_57pWrBcdaCWPN-8luCF08YWU74tUErOFg
71 | ```
72 |
73 |
74 | Contribution statistic
75 |
76 |
77 |
78 |
79 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | group("io.sfe")
2 |
3 | wrapper {
4 | gradleVersion = "8.6"
5 | distributionType = Wrapper.DistributionType.ALL
6 | }
7 |
8 | subprojects {
9 | apply plugin: 'java'
10 |
11 | group 'io.sfe'
12 |
13 | sourceCompatibility = JavaVersion.VERSION_17
14 |
15 | repositories {
16 | mavenCentral()
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/docs/Spring Framework Presentation.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vrudas/spring-framework-examples/99c111602e503c07535cc8db76acc703f0b591e1/docs/Spring Framework Presentation.pdf
--------------------------------------------------------------------------------
/example-00-hello/build.gradle:
--------------------------------------------------------------------------------
1 | dependencies {
2 | implementation libs.spring.context
3 | }
4 |
--------------------------------------------------------------------------------
/example-00-hello/src/main/java/io/sfe/hello/Main.java:
--------------------------------------------------------------------------------
1 | package io.sfe.hello;
2 |
3 | import org.springframework.context.support.ClassPathXmlApplicationContext;
4 |
5 | public class Main {
6 | public static void main(String[] args) {
7 | var applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
8 |
9 | var noteByName = (Note) applicationContext.getBean("helloWorldNote");
10 | System.out.println("noteByName = " + noteByName);
11 |
12 | var noteByType = applicationContext.getBean(Note.class);
13 | System.out.println("noteByType = " + noteByType);
14 |
15 | var noteByNameAndType = applicationContext.getBean("helloWorldNote", Note.class);
16 | System.out.println("noteByNameAndType = " + noteByNameAndType);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/example-00-hello/src/main/java/io/sfe/hello/Note.java:
--------------------------------------------------------------------------------
1 | package io.sfe.hello;
2 |
3 | import java.util.Objects;
4 |
5 | public class Note {
6 |
7 | private String text;
8 |
9 | public String getText() {
10 | return text;
11 | }
12 |
13 | public void setText(String text) {
14 | this.text = text;
15 | }
16 |
17 | @Override
18 | public boolean equals(Object o) {
19 | if (this == o) return true;
20 | if (o == null || getClass() != o.getClass()) return false;
21 | Note note = (Note) o;
22 | return Objects.equals(text, note.text);
23 | }
24 |
25 | @Override
26 | public int hashCode() {
27 | return Objects.hash(text);
28 | }
29 |
30 | @Override
31 | public String toString() {
32 | return "Note{" +
33 | "text='" + text + '\'' +
34 | '}';
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/example-00-hello/src/main/resources/applicationContext.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/example-01-bean-factory/build.gradle:
--------------------------------------------------------------------------------
1 | dependencies {
2 | implementation libs.spring.context
3 | }
4 |
--------------------------------------------------------------------------------
/example-01-bean-factory/src/main/java/io/sfe/beanfactory/Main.java:
--------------------------------------------------------------------------------
1 | package io.sfe.beanfactory;
2 |
3 | import org.springframework.beans.factory.support.DefaultListableBeanFactory;
4 | import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
5 | import org.springframework.context.support.ClassPathXmlApplicationContext;
6 | import org.springframework.core.io.ClassPathResource;
7 |
8 | public class Main {
9 | public static void main(String[] args) {
10 | var applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
11 |
12 | DefaultListableBeanFactory listableBeanFactory = new DefaultListableBeanFactory();
13 | XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(listableBeanFactory);
14 | beanDefinitionReader.loadBeanDefinitions(new ClassPathResource("applicationContext.xml"));
15 |
16 | var noteFromApplicationContext = applicationContext.getBean("helloWorldNote", Note.class);
17 | var noteFromCustomBeanFactory = listableBeanFactory.getBean("helloWorldNote", Note.class);
18 |
19 | System.out.println("noteFromApplicationContext = " + noteFromApplicationContext);
20 | System.out.println("noteFromCustomBeanFactory = " + noteFromCustomBeanFactory);
21 |
22 | System.out.println("Beans are not the same: " + (noteFromApplicationContext != noteFromCustomBeanFactory));
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/example-01-bean-factory/src/main/java/io/sfe/beanfactory/Note.java:
--------------------------------------------------------------------------------
1 | package io.sfe.beanfactory;
2 |
3 | import java.util.Objects;
4 |
5 | public class Note {
6 |
7 | private String text;
8 |
9 | public String getText() {
10 | return text;
11 | }
12 |
13 | public void setText(String text) {
14 | this.text = text;
15 | }
16 |
17 | @Override
18 | public boolean equals(Object o) {
19 | if (this == o) return true;
20 | if (o == null || getClass() != o.getClass()) return false;
21 | Note note = (Note) o;
22 | return Objects.equals(text, note.text);
23 | }
24 |
25 | @Override
26 | public int hashCode() {
27 | return Objects.hash(text);
28 | }
29 |
30 | @Override
31 | public String toString() {
32 | return "Note{" +
33 | "text='" + text + '\'' +
34 | '}';
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/example-01-bean-factory/src/main/resources/applicationContext.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/example-02-bean-definition/build.gradle:
--------------------------------------------------------------------------------
1 | dependencies {
2 | implementation libs.spring.context
3 | }
4 |
--------------------------------------------------------------------------------
/example-02-bean-definition/src/main/java/io/sfe/beandefinition/Main.java:
--------------------------------------------------------------------------------
1 | package io.sfe.beandefinition;
2 |
3 | import org.springframework.context.support.ClassPathXmlApplicationContext;
4 |
5 | public class Main {
6 | public static void main(String[] args) {
7 | var applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
8 |
9 | // Show example of lazy init case
10 | var noteService = applicationContext.getBean("noteService", NoteService.class);
11 |
12 | System.out.println("noteService = " + noteService);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/example-02-bean-definition/src/main/java/io/sfe/beandefinition/Note.java:
--------------------------------------------------------------------------------
1 | package io.sfe.beandefinition;
2 |
3 | import java.util.Objects;
4 |
5 | public class Note {
6 |
7 | private String text;
8 |
9 | public String getText() {
10 | return text;
11 | }
12 |
13 | public void setText(String text) {
14 | this.text = text;
15 | }
16 |
17 | public void init() {
18 | System.out.println("Init on start");
19 | }
20 |
21 | @Override
22 | public boolean equals(Object o) {
23 | if (this == o) return true;
24 | if (o == null || getClass() != o.getClass()) return false;
25 | Note note = (Note) o;
26 | return Objects.equals(text, note.text);
27 | }
28 |
29 | @Override
30 | public int hashCode() {
31 | return Objects.hash(text);
32 | }
33 |
34 | @Override
35 | public String toString() {
36 | return "Note{" +
37 | "text='" + text + '\'' +
38 | '}';
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/example-02-bean-definition/src/main/java/io/sfe/beandefinition/NoteService.java:
--------------------------------------------------------------------------------
1 | package io.sfe.beandefinition;
2 |
3 | import java.util.List;
4 |
5 | public class NoteService {
6 |
7 | private List notes;
8 |
9 | public List getNotes() {
10 | return notes;
11 | }
12 |
13 | public void setNotes(List notes) {
14 | this.notes = notes;
15 | }
16 |
17 | public void init() {
18 | System.out.println("Lazy init");
19 | }
20 |
21 | @Override
22 | public String toString() {
23 | return "NoteService{" +
24 | "notes=" + notes +
25 | '}';
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/example-02-bean-definition/src/main/resources/applicationContext.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
11 |
12 |
13 |
14 |
19 |
20 |
21 |
22 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/example-03-bean-scope/build.gradle:
--------------------------------------------------------------------------------
1 | dependencies {
2 | implementation libs.spring.context
3 | }
4 |
--------------------------------------------------------------------------------
/example-03-bean-scope/src/main/java/io/sfe/beanscope/Main.java:
--------------------------------------------------------------------------------
1 | package io.sfe.beanscope;
2 |
3 | import org.springframework.context.support.ClassPathXmlApplicationContext;
4 |
5 | public class Main {
6 | public static void main(String[] args) {
7 | var applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
8 |
9 | var noteSingleton = applicationContext.getBean("noteSingleton", Note.class);
10 | noteSingleton.setText("Modified");
11 |
12 | var anotherNoteSingleton = applicationContext.getBean("noteSingleton", Note.class);
13 |
14 | System.out.println("noteSingleton = " + noteSingleton);
15 | System.out.println("anotherNoteSingleton = " + anotherNoteSingleton);
16 | System.out.println();
17 |
18 |
19 | var notePrototype = applicationContext.getBean("notePrototype", Note.class);
20 | notePrototype.setText("Modified");
21 |
22 | var anotherNotePrototype = applicationContext.getBean("notePrototype", Note.class);
23 |
24 | System.out.println("notePrototype = " + notePrototype);
25 | System.out.println("anotherNotePrototype = " + anotherNotePrototype);
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/example-03-bean-scope/src/main/java/io/sfe/beanscope/Note.java:
--------------------------------------------------------------------------------
1 | package io.sfe.beanscope;
2 |
3 | import java.util.Objects;
4 |
5 | public class Note {
6 |
7 | private String text;
8 |
9 | public String getText() {
10 | return text;
11 | }
12 |
13 | public void setText(String text) {
14 | this.text = text;
15 | }
16 |
17 | @Override
18 | public boolean equals(Object o) {
19 | if (this == o) return true;
20 | if (o == null || getClass() != o.getClass()) return false;
21 | Note note = (Note) o;
22 | return Objects.equals(text, note.text);
23 | }
24 |
25 | @Override
26 | public int hashCode() {
27 | return Objects.hash(text);
28 | }
29 |
30 | @Override
31 | public String toString() {
32 | return "Note{" +
33 | "text='" + text + '\'' +
34 | '}';
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/example-03-bean-scope/src/main/resources/applicationContext.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/example-04-bean-lifecycle/build.gradle:
--------------------------------------------------------------------------------
1 | dependencies {
2 | implementation libs.spring.context
3 | implementation libs.javax.annotation
4 | }
5 |
--------------------------------------------------------------------------------
/example-04-bean-lifecycle/src/main/java/io/sfe/beanlifecycle/LifecycleBean.java:
--------------------------------------------------------------------------------
1 | package io.sfe.beanlifecycle;
2 |
3 | import org.springframework.beans.factory.DisposableBean;
4 | import org.springframework.beans.factory.InitializingBean;
5 |
6 | import javax.annotation.PostConstruct;
7 | import javax.annotation.PreDestroy;
8 |
9 | public class LifecycleBean implements InitializingBean, DisposableBean {
10 |
11 | @PostConstruct
12 | public void postConstruct() {
13 | System.out.println("Post Construct stage");
14 | }
15 |
16 | @Override
17 | public void afterPropertiesSet() {
18 | System.out.println("After Properties Set stage");
19 | }
20 |
21 | public void customInit() {
22 | System.out.println("Custom Init stage");
23 | }
24 |
25 | @PreDestroy
26 | public void preDestroy() {
27 | System.out.println("Pre Destroy stage");
28 | }
29 |
30 | @Override
31 | public void destroy() {
32 | System.out.println("Destroy stage");
33 | }
34 |
35 | public void customDestroy() {
36 | System.out.println("Custom Destroy stage");
37 | }
38 |
39 | @Override
40 | public String toString() {
41 | return "LifecycleBean{}";
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/example-04-bean-lifecycle/src/main/java/io/sfe/beanlifecycle/Main.java:
--------------------------------------------------------------------------------
1 | package io.sfe.beanlifecycle;
2 |
3 | import org.springframework.context.support.ClassPathXmlApplicationContext;
4 |
5 | public class Main {
6 | public static void main(String[] args) {
7 | var applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
8 |
9 | var lifecycleBean = applicationContext.getBean("lifecycleBean", LifecycleBean.class);
10 | System.out.println("lifecycleBean = " + lifecycleBean);
11 |
12 | applicationContext.registerShutdownHook();
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/example-04-bean-lifecycle/src/main/resources/applicationContext.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/example-05-dependency-injection/build.gradle:
--------------------------------------------------------------------------------
1 | dependencies {
2 | implementation libs.spring.context
3 | }
4 |
--------------------------------------------------------------------------------
/example-05-dependency-injection/src/main/java/io/sfe/dependencyinjection/Author.java:
--------------------------------------------------------------------------------
1 | package io.sfe.dependencyinjection;
2 |
3 | public record Author(int authorId) {
4 | }
5 |
--------------------------------------------------------------------------------
/example-05-dependency-injection/src/main/java/io/sfe/dependencyinjection/Main.java:
--------------------------------------------------------------------------------
1 | package io.sfe.dependencyinjection;
2 |
3 | import org.springframework.context.support.ClassPathXmlApplicationContext;
4 |
5 | public class Main {
6 | public static void main(String[] args) {
7 | var applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
8 |
9 | Author author = applicationContext.getBean(Author.class);
10 | System.out.println("author = " + author);
11 |
12 | Note setterNote = applicationContext.getBean("setterNote", Note.class);
13 | System.out.println("setterNote = " + setterNote);
14 |
15 | Note constructorNote = applicationContext.getBean("constructorNote", Note.class);
16 | System.out.println("constructorNote = " + constructorNote);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/example-05-dependency-injection/src/main/java/io/sfe/dependencyinjection/Note.java:
--------------------------------------------------------------------------------
1 | package io.sfe.dependencyinjection;
2 |
3 | import java.util.Objects;
4 |
5 | public class Note {
6 |
7 | private Author author;
8 | private String text;
9 |
10 |
11 | public Note() {
12 |
13 | }
14 |
15 | public Note(Author author) {
16 | this.author = author;
17 | }
18 |
19 | public String getText() {
20 | return text;
21 | }
22 |
23 | public void setText(String text) {
24 | this.text = text;
25 | }
26 |
27 | public Author getAuthor() {
28 | return author;
29 | }
30 |
31 | public void setAuthor(Author author) {
32 | this.author = author;
33 | }
34 |
35 | @Override
36 | public boolean equals(Object o) {
37 | if (this == o) return true;
38 | if (o == null || getClass() != o.getClass()) return false;
39 | Note note = (Note) o;
40 | return Objects.equals(text, note.text);
41 | }
42 |
43 | @Override
44 | public int hashCode() {
45 | return Objects.hash(text);
46 | }
47 |
48 | @Override
49 | public String toString() {
50 | return "Note{" +
51 | "author=" + author +
52 | ", text='" + text + '\'' +
53 | '}';
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/example-05-dependency-injection/src/main/resources/applicationContext.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/example-06-annotation-config/build.gradle:
--------------------------------------------------------------------------------
1 | dependencies {
2 | implementation libs.spring.context
3 | implementation libs.javax.annotation
4 | }
5 |
--------------------------------------------------------------------------------
/example-06-annotation-config/src/main/java/io/sfe/annotationconfig/Main.java:
--------------------------------------------------------------------------------
1 | package io.sfe.annotationconfig;
2 |
3 | import org.springframework.context.annotation.AnnotationConfigApplicationContext;
4 |
5 | public class Main {
6 | public static void main(String[] args) {
7 | var applicationContext = new AnnotationConfigApplicationContext("io.sfe.annotationconfig");
8 |
9 | // Example with component scan annotation
10 | // var applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
11 |
12 | var noteService = applicationContext.getBean(NoteService.class);
13 | var noteValidator = applicationContext.getBean(NoteValidator.class);
14 |
15 | Note note = new Note("text");
16 |
17 | noteValidator.validateNote(note);
18 | noteService.saveNote(note);
19 |
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/example-06-annotation-config/src/main/java/io/sfe/annotationconfig/Note.java:
--------------------------------------------------------------------------------
1 | package io.sfe.annotationconfig;
2 |
3 | record Note(String text) {
4 | }
5 |
--------------------------------------------------------------------------------
/example-06-annotation-config/src/main/java/io/sfe/annotationconfig/NoteRepository.java:
--------------------------------------------------------------------------------
1 | package io.sfe.annotationconfig;
2 |
3 | import org.springframework.stereotype.Repository;
4 |
5 | @Repository
6 | public class NoteRepository {
7 |
8 | public void saveNote(Note note) {
9 | System.out.println("Note: " + note + " was saved");
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/example-06-annotation-config/src/main/java/io/sfe/annotationconfig/NoteService.java:
--------------------------------------------------------------------------------
1 | package io.sfe.annotationconfig;
2 |
3 | import org.springframework.stereotype.Service;
4 |
5 | @Service
6 | public class NoteService {
7 |
8 | private final NoteRepository noteRepository;
9 |
10 | // @Autowired // Not required after Spring 5
11 | public NoteService(NoteRepository noteRepository) {
12 | this.noteRepository = noteRepository;
13 | }
14 |
15 | void saveNote(Note note) {
16 | noteRepository.saveNote(note);
17 | }
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/example-06-annotation-config/src/main/java/io/sfe/annotationconfig/NoteValidator.java:
--------------------------------------------------------------------------------
1 | package io.sfe.annotationconfig;
2 |
3 | import org.springframework.stereotype.Component;
4 |
5 | import java.util.Objects;
6 |
7 | @Component
8 | public class NoteValidator {
9 |
10 | void validateNote(Note note) {
11 | if (note == null) {
12 | throw new IllegalArgumentException("Note is null");
13 | }
14 |
15 | String noteText = Objects.requireNonNullElse(note.text(), "");
16 |
17 | if (noteText.isBlank()) {
18 | throw new IllegalArgumentException("Note text is blank");
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/example-06-annotation-config/src/main/java/io/sfe/annotationconfig/config/AppConfig.java:
--------------------------------------------------------------------------------
1 | package io.sfe.annotationconfig.config;
2 |
3 | //@ComponentScan(basePackages = "io.sfe.annotationconfig")
4 | public class AppConfig {
5 | }
6 |
--------------------------------------------------------------------------------
/example-07-java-config/build.gradle:
--------------------------------------------------------------------------------
1 | dependencies {
2 | implementation libs.spring.context
3 | implementation libs.javax.annotation
4 | }
5 |
--------------------------------------------------------------------------------
/example-07-java-config/src/main/java/io/sfe/javaconfig/Main.java:
--------------------------------------------------------------------------------
1 | package io.sfe.javaconfig;
2 |
3 | import io.sfe.javaconfig.config.AppConfig;
4 | import io.sfe.javaconfig.config.DbConfig;
5 | import org.springframework.context.annotation.AnnotationConfigApplicationContext;
6 |
7 | public class Main {
8 | public static void main(String[] args) {
9 | var applicationContext = new AnnotationConfigApplicationContext();
10 | applicationContext.register(DbConfig.class, AppConfig.class);
11 | applicationContext.refresh();
12 |
13 | // var applicationContext = new AnnotationConfigApplicationContext(DbConfig.class, AppConfig.class);
14 |
15 | // Import example
16 | // var applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
17 |
18 | var noteService = applicationContext.getBean(NoteService.class);
19 |
20 | Note note = new Note("text");
21 |
22 | noteService.saveNote(note);
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/example-07-java-config/src/main/java/io/sfe/javaconfig/Note.java:
--------------------------------------------------------------------------------
1 | package io.sfe.javaconfig;
2 |
3 | record Note(String text) {
4 | }
5 |
--------------------------------------------------------------------------------
/example-07-java-config/src/main/java/io/sfe/javaconfig/NoteRepository.java:
--------------------------------------------------------------------------------
1 | package io.sfe.javaconfig;
2 |
3 | import io.sfe.javaconfig.db.DbConnectionProvider;
4 |
5 | public class NoteRepository {
6 |
7 | private final DbConnectionProvider dbConnectionProvider;
8 |
9 | public NoteRepository(DbConnectionProvider dbConnectionProvider) {
10 | this.dbConnectionProvider = dbConnectionProvider;
11 | }
12 |
13 | public void saveNote(Note note) {
14 | dbConnectionProvider.getDbConnection();
15 | System.out.println("Note: " + note + " was saved");
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/example-07-java-config/src/main/java/io/sfe/javaconfig/NoteService.java:
--------------------------------------------------------------------------------
1 | package io.sfe.javaconfig;
2 |
3 | public class NoteService {
4 |
5 | // Example of field injection
6 | private final NoteRepository noteRepository;
7 |
8 | public NoteService(NoteRepository noteRepository) {
9 | this.noteRepository = noteRepository;
10 | }
11 |
12 | void saveNote(Note note) {
13 | noteRepository.saveNote(note);
14 | }
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/example-07-java-config/src/main/java/io/sfe/javaconfig/config/AppConfig.java:
--------------------------------------------------------------------------------
1 | package io.sfe.javaconfig.config;
2 |
3 | import io.sfe.javaconfig.NoteRepository;
4 | import io.sfe.javaconfig.NoteService;
5 | import io.sfe.javaconfig.db.DbConnectionProvider;
6 | import org.springframework.context.annotation.Bean;
7 | import org.springframework.context.annotation.Configuration;
8 |
9 | @Configuration
10 | //@Import(DbConfig.class)
11 | public class AppConfig {
12 |
13 | @Bean
14 | public NoteRepository noteRepository(DbConnectionProvider dbConnectionProvider) {
15 | return new NoteRepository(dbConnectionProvider);
16 | }
17 |
18 | @Bean
19 | public NoteService noteService(NoteRepository noteRepository) {
20 | return new NoteService(noteRepository);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/example-07-java-config/src/main/java/io/sfe/javaconfig/config/DbConfig.java:
--------------------------------------------------------------------------------
1 | package io.sfe.javaconfig.config;
2 |
3 | import io.sfe.javaconfig.db.DbConnectionProvider;
4 | import org.springframework.context.annotation.Bean;
5 | import org.springframework.context.annotation.Configuration;
6 |
7 | @Configuration
8 | public class DbConfig {
9 |
10 | @Bean
11 | public DbConnectionProvider dbConnectionProvider() {
12 | return new DbConnectionProvider();
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/example-07-java-config/src/main/java/io/sfe/javaconfig/db/DbConnectionProvider.java:
--------------------------------------------------------------------------------
1 | package io.sfe.javaconfig.db;
2 |
3 | public class DbConnectionProvider {
4 |
5 | public Object getDbConnection() {
6 | System.out.println("Connection obtained");
7 | return new Object();
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/example-08-properties/build.gradle:
--------------------------------------------------------------------------------
1 | dependencies {
2 | implementation libs.spring.context
3 | }
4 |
--------------------------------------------------------------------------------
/example-08-properties/src/main/java/io/sfe/properties/Main.java:
--------------------------------------------------------------------------------
1 | package io.sfe.properties;
2 |
3 | import io.sfe.properties.db.DbConnectionProvider;
4 | import org.springframework.context.annotation.AnnotationConfigApplicationContext;
5 |
6 | public class Main {
7 | public static void main(String[] args) {
8 | var applicationContext = new AnnotationConfigApplicationContext("io.sfe.properties");
9 |
10 | var dbConnectionProvider = applicationContext.getBean(DbConnectionProvider.class);
11 |
12 | dbConnectionProvider.getDbConnection();
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/example-08-properties/src/main/java/io/sfe/properties/config/PropertiesAnnotationConfig.java:
--------------------------------------------------------------------------------
1 | package io.sfe.properties.config;
2 |
3 | //@Configuration
4 | //@PropertySources(
5 | // @PropertySource("classpath:db.properties")
6 | //)
7 | //@PropertySource("classpath:db.properties")
8 | public class PropertiesAnnotationConfig {
9 | }
10 |
--------------------------------------------------------------------------------
/example-08-properties/src/main/java/io/sfe/properties/config/PropertiesJavaConfig.java:
--------------------------------------------------------------------------------
1 | package io.sfe.properties.config;
2 |
3 | import org.springframework.context.annotation.Bean;
4 | import org.springframework.context.annotation.Configuration;
5 | import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
6 | import org.springframework.core.io.ClassPathResource;
7 |
8 | @Configuration
9 | public class PropertiesJavaConfig {
10 |
11 | @Bean
12 | public static PropertySourcesPlaceholderConfigurer dbProperties() {
13 | var configurer = new PropertySourcesPlaceholderConfigurer();
14 |
15 | ClassPathResource dbProperties = new ClassPathResource("db.properties");
16 |
17 | configurer.setLocations(dbProperties);
18 |
19 | return configurer;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/example-08-properties/src/main/java/io/sfe/properties/db/DbConnectionProvider.java:
--------------------------------------------------------------------------------
1 | package io.sfe.properties.db;
2 |
3 | import org.springframework.beans.factory.annotation.Value;
4 | import org.springframework.stereotype.Component;
5 |
6 | @Component
7 | public class DbConnectionProvider {
8 | private final String url;
9 | private final String user;
10 | private final String password;
11 |
12 | public DbConnectionProvider(
13 | @Value("${db.url}") String url,
14 | @Value("${db.user}") String user,
15 | @Value("${db.password}") String password
16 | ) {
17 | this.url = url;
18 | this.user = user;
19 | this.password = password;
20 | }
21 |
22 | public Object getDbConnection() {
23 | System.out.println("Connection obtained for properties:");
24 | System.out.println("url = " + url);
25 | System.out.println("user = " + user.replaceAll("\\w", "*"));
26 | System.out.println("password = " + password.replaceAll("\\w", "*"));
27 | return new Object();
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/example-08-properties/src/main/resources/db.properties:
--------------------------------------------------------------------------------
1 | db.url=url
2 | db.user=user
3 | db.password=password
4 |
--------------------------------------------------------------------------------
/example-09-dispatcher-servlet/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | repositories {
3 | mavenCentral()
4 | }
5 |
6 | dependencies {
7 | classpath "com.bmuschko:gradle-tomcat-plugin:2.5"
8 | }
9 | }
10 |
11 | apply plugin: "com.bmuschko.tomcat"
12 |
13 | dependencies {
14 | def tomcatVersion = "11.0.7"
15 |
16 | tomcat("org.apache.tomcat.embed:tomcat-embed-core:${tomcatVersion}")
17 | tomcat("org.apache.tomcat.embed:tomcat-embed-jasper:${tomcatVersion}")
18 |
19 | providedCompile(libs.javax.servlet)
20 |
21 | implementation libs.spring.context
22 | implementation libs.spring.web
23 | implementation libs.spring.webmvc
24 | }
25 |
26 | tomcat {
27 | httpPort = 8080
28 | contextPath = "/"
29 |
30 | httpProtocol = "org.apache.coyote.http11.Http11Nio2Protocol"
31 | ajpProtocol = "org.apache.coyote.ajp.AjpNio2Protocol"
32 | }
33 |
--------------------------------------------------------------------------------
/example-09-dispatcher-servlet/src/main/java/io/sfe/dispatcher/HelloController.java:
--------------------------------------------------------------------------------
1 | package io.sfe.dispatcher;
2 |
3 | import org.springframework.stereotype.Controller;
4 | import org.springframework.web.bind.annotation.RequestMapping;
5 | import org.springframework.web.bind.annotation.ResponseBody;
6 |
7 | @Controller
8 | public class HelloController {
9 |
10 | @RequestMapping("/hello")
11 | @ResponseBody
12 | public String hello() {
13 | return "Hello in a Spring MVC";
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/example-09-dispatcher-servlet/src/main/java/io/sfe/dispatcher/config/AppInitializer.java:
--------------------------------------------------------------------------------
1 | package io.sfe.dispatcher.config;
2 |
3 | import org.springframework.web.WebApplicationInitializer;
4 | import org.springframework.web.context.ContextLoaderListener;
5 | import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
6 | import org.springframework.web.servlet.DispatcherServlet;
7 |
8 | import javax.servlet.ServletContext;
9 | import javax.servlet.ServletRegistration;
10 |
11 | public class AppInitializer implements WebApplicationInitializer {
12 |
13 | @Override
14 | public void onStartup(ServletContext servletContext) {
15 | var webApplicationContext = new AnnotationConfigWebApplicationContext();
16 | webApplicationContext.register(WebAppConfig.class);
17 |
18 | // Manage the lifecycle of the root application context
19 | servletContext.addListener(new ContextLoaderListener(webApplicationContext));
20 |
21 | // Register and map the dispatcher servlet
22 | ServletRegistration.Dynamic dispatcher = servletContext
23 | .addServlet(
24 | "dispatcher",
25 | new DispatcherServlet(webApplicationContext)
26 | );
27 |
28 | dispatcher.setLoadOnStartup(1);
29 | dispatcher.addMapping("/");
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/example-09-dispatcher-servlet/src/main/java/io/sfe/dispatcher/config/WebAppConfig.java:
--------------------------------------------------------------------------------
1 | package io.sfe.dispatcher.config;
2 |
3 | import org.springframework.context.annotation.ComponentScan;
4 | import org.springframework.context.annotation.Configuration;
5 | import org.springframework.web.servlet.config.annotation.EnableWebMvc;
6 |
7 | @Configuration
8 | @EnableWebMvc
9 | @ComponentScan(basePackages = {"io.sfe.dispatcher"})
10 | public class WebAppConfig {
11 | }
12 |
--------------------------------------------------------------------------------
/example-10-spring-mvc/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | repositories {
3 | mavenCentral()
4 | }
5 |
6 | dependencies {
7 | classpath "com.bmuschko:gradle-tomcat-plugin:2.5"
8 | }
9 | }
10 |
11 | apply plugin: "com.bmuschko.tomcat"
12 |
13 | dependencies {
14 | def tomcatVersion = "11.0.7"
15 |
16 | tomcat("org.apache.tomcat.embed:tomcat-embed-core:${tomcatVersion}")
17 | tomcat("org.apache.tomcat.embed:tomcat-embed-jasper:${tomcatVersion}")
18 |
19 | providedCompile(libs.javax.servlet)
20 |
21 | implementation libs.spring.context
22 | implementation libs.spring.web
23 | implementation libs.spring.webmvc
24 |
25 | implementation "org.thymeleaf:thymeleaf:3.1.3.RELEASE"
26 | implementation "org.thymeleaf:thymeleaf-spring5:3.1.3.RELEASE"
27 | }
28 |
29 | tomcat {
30 | httpPort = 8080
31 | contextPath = "/"
32 |
33 | httpProtocol = "org.apache.coyote.http11.Http11Nio2Protocol"
34 | ajpProtocol = "org.apache.coyote.ajp.AjpNio2Protocol"
35 | }
36 |
--------------------------------------------------------------------------------
/example-10-spring-mvc/src/main/java/io/sfe/springmvc/Note.java:
--------------------------------------------------------------------------------
1 | package io.sfe.springmvc;
2 |
3 | record Note(String text) {
4 | }
5 |
--------------------------------------------------------------------------------
/example-10-spring-mvc/src/main/java/io/sfe/springmvc/NoteController.java:
--------------------------------------------------------------------------------
1 | package io.sfe.springmvc;
2 |
3 | import org.springframework.stereotype.Controller;
4 | import org.springframework.web.bind.annotation.RequestMapping;
5 | import org.springframework.web.servlet.ModelAndView;
6 |
7 | import java.time.LocalDateTime;
8 | import java.time.format.DateTimeFormatter;
9 | import java.util.List;
10 |
11 | @Controller
12 | public class NoteController {
13 |
14 | @RequestMapping("/notes")
15 | public ModelAndView allNotes(ModelAndView modelAndView) {
16 | modelAndView.setViewName("notes");
17 |
18 | LocalDateTime now = LocalDateTime.now();
19 | String formattedDateTime = DateTimeFormatter.ISO_DATE_TIME.format(now);
20 |
21 | modelAndView.addObject("dateTime", formattedDateTime);
22 | modelAndView.addObject(
23 | "notes",
24 | List.of(
25 | new Note("note1"),
26 | new Note("note2"),
27 | new Note("note3")
28 | )
29 | );
30 |
31 | return modelAndView;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/example-10-spring-mvc/src/main/java/io/sfe/springmvc/config/AppInitializer.java:
--------------------------------------------------------------------------------
1 | package io.sfe.springmvc.config;
2 |
3 | import org.springframework.web.WebApplicationInitializer;
4 | import org.springframework.web.context.ContextLoaderListener;
5 | import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
6 | import org.springframework.web.servlet.DispatcherServlet;
7 |
8 | import javax.servlet.ServletContext;
9 | import javax.servlet.ServletRegistration;
10 |
11 | public class AppInitializer implements WebApplicationInitializer {
12 |
13 | @Override
14 | public void onStartup(ServletContext servletContext) {
15 | var webApplicationContext = new AnnotationConfigWebApplicationContext();
16 | webApplicationContext.register(WebAppConfig.class);
17 |
18 | servletContext.addListener(new ContextLoaderListener(webApplicationContext));
19 |
20 | ServletRegistration.Dynamic dispatcher = servletContext
21 | .addServlet(
22 | "dispatcher",
23 | new DispatcherServlet(webApplicationContext)
24 | );
25 |
26 | dispatcher.setLoadOnStartup(1);
27 | dispatcher.addMapping("/");
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/example-10-spring-mvc/src/main/java/io/sfe/springmvc/config/WebAppConfig.java:
--------------------------------------------------------------------------------
1 | package io.sfe.springmvc.config;
2 |
3 | import org.springframework.context.ApplicationContext;
4 | import org.springframework.context.ApplicationContextAware;
5 | import org.springframework.context.annotation.Bean;
6 | import org.springframework.context.annotation.ComponentScan;
7 | import org.springframework.context.annotation.Configuration;
8 | import org.springframework.context.annotation.Description;
9 | import org.springframework.web.servlet.ViewResolver;
10 | import org.springframework.web.servlet.config.annotation.EnableWebMvc;
11 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
12 | import org.thymeleaf.spring5.ISpringTemplateEngine;
13 | import org.thymeleaf.spring5.SpringTemplateEngine;
14 | import org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver;
15 | import org.thymeleaf.spring5.view.ThymeleafViewResolver;
16 | import org.thymeleaf.templatemode.TemplateMode;
17 | import org.thymeleaf.templateresolver.ITemplateResolver;
18 |
19 | @Configuration
20 | @EnableWebMvc
21 | @ComponentScan(basePackages = {"io.sfe.springmvc"})
22 | public class WebAppConfig implements WebMvcConfigurer, ApplicationContextAware {
23 |
24 | private ApplicationContext applicationContext;
25 |
26 | @Override
27 | public void setApplicationContext(ApplicationContext applicationContext) {
28 | this.applicationContext = applicationContext;
29 | }
30 |
31 | @Bean
32 | @Description("Thymeleaf Template Resolver")
33 | public ITemplateResolver templateResolver() {
34 | var templateResolver = new SpringResourceTemplateResolver();
35 | templateResolver.setApplicationContext(applicationContext);
36 | templateResolver.setPrefix("/WEB-INF/views/");
37 | templateResolver.setSuffix(".html");
38 | templateResolver.setTemplateMode(TemplateMode.HTML);
39 |
40 | templateResolver.setCacheable(false);
41 |
42 | return templateResolver;
43 | }
44 |
45 | @Bean
46 | @Description("Thymeleaf Template Engine")
47 | public ISpringTemplateEngine templateEngine(ITemplateResolver templateResolver) {
48 | var templateEngine = new SpringTemplateEngine();
49 | templateEngine.setTemplateResolver(templateResolver);
50 | templateEngine.setEnableSpringELCompiler(true);
51 | return templateEngine;
52 | }
53 |
54 | @Bean
55 | @Description("Thymeleaf View Resolver")
56 | public ViewResolver viewResolver(ISpringTemplateEngine templateEngine) {
57 | var viewResolver = new ThymeleafViewResolver();
58 | viewResolver.setTemplateEngine(templateEngine);
59 | viewResolver.setOrder(1);
60 | return viewResolver;
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/example-10-spring-mvc/src/main/webapp/WEB-INF/views/notes.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Calendar
6 |
7 |
8 | Current time:
9 |
10 | 2011-12-03T10:15:30
11 |
12 |
13 |
14 |
15 | Note index |
16 | Note text |
17 |
18 |
19 |
20 |
21 | 0 |
22 | text |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/example-11-spring-boot/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'org.springframework.boot' version '3.3.5'
3 | id 'io.spring.dependency-management' version '1.1.7'
4 | }
5 |
6 | dependencies {
7 | implementation 'org.springframework.boot:spring-boot-starter-web'
8 | implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
9 | testImplementation 'org.springframework.boot:spring-boot-starter-test'
10 | }
11 |
12 | test {
13 | useJUnitPlatform()
14 | }
15 |
--------------------------------------------------------------------------------
/example-11-spring-boot/src/main/java/io/sfe/springboot/NoteApplication.java:
--------------------------------------------------------------------------------
1 | package io.sfe.springboot;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | @SpringBootApplication
7 | public class NoteApplication {
8 |
9 | public static void main(String[] args) {
10 | SpringApplication.run(NoteApplication.class, args);
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/example-11-spring-boot/src/main/java/io/sfe/springboot/note/Note.java:
--------------------------------------------------------------------------------
1 | package io.sfe.springboot.note;
2 |
3 | record Note(String text) {
4 | }
5 |
--------------------------------------------------------------------------------
/example-11-spring-boot/src/main/java/io/sfe/springboot/note/NoteController.java:
--------------------------------------------------------------------------------
1 | package io.sfe.springboot.note;
2 |
3 | import org.springframework.stereotype.Controller;
4 | import org.springframework.ui.Model;
5 | import org.springframework.web.bind.annotation.RequestMapping;
6 |
7 | import java.time.LocalDateTime;
8 | import java.time.format.DateTimeFormatter;
9 | import java.util.List;
10 |
11 | @Controller
12 | public class NoteController {
13 |
14 | @RequestMapping("/notes")
15 | public String allNotes(Model model) {
16 | LocalDateTime now = LocalDateTime.now();
17 | String formattedDateTime = DateTimeFormatter.ISO_DATE_TIME.format(now);
18 |
19 | model.addAttribute("dateTime", formattedDateTime);
20 | model.addAttribute(
21 | "notes",
22 | List.of(
23 | new Note("note1"),
24 | new Note("note2"),
25 | new Note("note3")
26 | )
27 | );
28 |
29 | return "notes";
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/example-11-spring-boot/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/example-11-spring-boot/src/main/resources/templates/notes.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Calendar
6 |
7 |
8 | Current time:
9 | 2011-12-03T10:15:30
10 |
11 |
12 |
13 |
14 | Note index |
15 | Note text |
16 |
17 |
18 | 0 |
19 | text |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/example-11-spring-boot/src/test/java/io/sfe/springboot/NoteApplicationTests.java:
--------------------------------------------------------------------------------
1 | package io.sfe.springboot;
2 |
3 | import org.junit.jupiter.api.Test;
4 | import org.springframework.boot.test.context.SpringBootTest;
5 |
6 | @SpringBootTest
7 | class NoteApplicationTests {
8 |
9 | @Test
10 | void contextLoads() {
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/example-12-boot-security/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'org.springframework.boot' version '3.3.5'
3 | id 'io.spring.dependency-management' version '1.1.7'
4 | }
5 |
6 | dependencies {
7 | implementation 'org.springframework.boot:spring-boot-starter-web'
8 | implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
9 | implementation 'org.springframework.boot:spring-boot-starter-security'
10 |
11 | compileOnly("org.springframework.boot:spring-boot-devtools")
12 |
13 | implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6'
14 |
15 | testImplementation 'org.springframework.boot:spring-boot-starter-test'
16 | }
17 |
18 | test {
19 | useJUnitPlatform()
20 | }
21 |
--------------------------------------------------------------------------------
/example-12-boot-security/src/main/java/io/sfe/bootsecurity/SecuredApplication.java:
--------------------------------------------------------------------------------
1 | package io.sfe.bootsecurity;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | @SpringBootApplication
7 | public class SecuredApplication {
8 |
9 | public static void main(String[] args) {
10 | SpringApplication.run(SecuredApplication.class, args);
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/example-12-boot-security/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | #spring.security.user.name=user
2 | #spring.security.user.password=password
3 | #spring.security.user.roles=USER
4 |
--------------------------------------------------------------------------------
/example-12-boot-security/src/main/resources/templates/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Security Example
6 |
7 |
8 | Logged user: Bob
9 | Roles: [ROLE_USER, ROLE_ADMIN]
10 |
11 |
12 |
--------------------------------------------------------------------------------
/example-13-inmemory-security/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'org.springframework.boot' version '3.3.5'
3 | id 'io.spring.dependency-management' version '1.1.7'
4 | }
5 |
6 | dependencies {
7 | implementation 'org.springframework.boot:spring-boot-starter-web'
8 | implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
9 | implementation 'org.springframework.boot:spring-boot-starter-security'
10 |
11 | compileOnly("org.springframework.boot:spring-boot-devtools")
12 |
13 | implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6'
14 |
15 | testImplementation 'org.springframework.boot:spring-boot-starter-test'
16 | }
17 |
18 | test {
19 | useJUnitPlatform()
20 | }
21 |
--------------------------------------------------------------------------------
/example-13-inmemory-security/src/main/java/io/sfe/bootsecurity/InMemorySecurityApplication.java:
--------------------------------------------------------------------------------
1 | package io.sfe.bootsecurity;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | @SpringBootApplication
7 | public class InMemorySecurityApplication {
8 |
9 | public static void main(String[] args) {
10 | SpringApplication.run(InMemorySecurityApplication.class, args);
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/example-13-inmemory-security/src/main/java/io/sfe/bootsecurity/InMemorySecurityConfig.java:
--------------------------------------------------------------------------------
1 | package io.sfe.bootsecurity;
2 |
3 | import org.springframework.context.annotation.Bean;
4 | import org.springframework.context.annotation.Configuration;
5 | import org.springframework.security.config.annotation.web.builders.HttpSecurity;
6 | import org.springframework.security.core.userdetails.User;
7 | import org.springframework.security.crypto.factory.PasswordEncoderFactories;
8 | import org.springframework.security.crypto.password.PasswordEncoder;
9 | import org.springframework.security.provisioning.InMemoryUserDetailsManager;
10 | import org.springframework.security.web.SecurityFilterChain;
11 |
12 | @Configuration
13 | public class InMemorySecurityConfig {
14 |
15 | @Bean
16 | public PasswordEncoder passwordEncoder() {
17 | return PasswordEncoderFactories.createDelegatingPasswordEncoder();
18 | }
19 |
20 | @Bean
21 | public InMemoryUserDetailsManager inMemoryUserDetailsManager() {
22 | var user = User.withUsername("user")
23 | .password("{bcrypt}$2a$10$GlpFG1Ml3U9AvkOu0D1B9ufnoquX5xqCR/NHaMfBZliYgPa8/e5sK") //user
24 | .roles("USER")
25 | .build();
26 |
27 | var admin = User.withUsername("admin")
28 | .password("{bcrypt}$2a$10$ku.DZ5JqOy/dgFgAZkwcSuiaMMCmOt8pVmerZDM5lTWO44MHGCMcC") //admin
29 | .roles("ADMIN")
30 | .build();
31 |
32 | return new InMemoryUserDetailsManager(user, admin);
33 | }
34 |
35 | /**
36 | * HTTP Basic Example
37 | */
38 | @Bean
39 | protected SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
40 | return http.authorizeHttpRequests()
41 | .requestMatchers("/").permitAll()
42 | .anyRequest().authenticated()
43 | .and()
44 | .httpBasic()
45 | .and().build();
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/example-13-inmemory-security/src/main/java/io/sfe/bootsecurity/IndexController.java:
--------------------------------------------------------------------------------
1 | package io.sfe.bootsecurity;
2 |
3 | import org.springframework.stereotype.Controller;
4 | import org.springframework.web.bind.annotation.GetMapping;
5 |
6 | @Controller
7 | public class IndexController {
8 |
9 | @GetMapping("/index")
10 | public String index() {
11 | return "index";
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/example-13-inmemory-security/src/main/resources/application.properties:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vrudas/spring-framework-examples/99c111602e503c07535cc8db76acc703f0b591e1/example-13-inmemory-security/src/main/resources/application.properties
--------------------------------------------------------------------------------
/example-13-inmemory-security/src/main/resources/templates/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Security Example
6 |
7 |
8 | Logged user: Bob
9 |
10 |
11 |
--------------------------------------------------------------------------------
/example-14-jdbc-auth-security/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'org.springframework.boot' version '3.3.5'
3 | id 'io.spring.dependency-management' version '1.1.7'
4 | }
5 |
6 | dependencies {
7 | implementation 'org.springframework.boot:spring-boot-starter-web'
8 | implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
9 | implementation 'org.springframework.boot:spring-boot-starter-security'
10 | implementation 'org.springframework.boot:spring-boot-starter-data-jdbc'
11 |
12 | runtimeOnly 'com.h2database:h2'
13 | compileOnly("org.springframework.boot:spring-boot-devtools")
14 |
15 | implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6'
16 |
17 | testImplementation 'org.springframework.boot:spring-boot-starter-test'
18 | }
19 |
20 | test {
21 | useJUnitPlatform()
22 | }
23 |
--------------------------------------------------------------------------------
/example-14-jdbc-auth-security/src/main/java/io/sfe/jdbcsecurity/DatabaseConfig.java:
--------------------------------------------------------------------------------
1 | package io.sfe.jdbcsecurity;
2 |
3 | import org.springframework.context.annotation.Bean;
4 | import org.springframework.context.annotation.Configuration;
5 | import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
6 | import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
7 | import org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl;
8 |
9 | import javax.sql.DataSource;
10 |
11 | @Configuration
12 | public class DatabaseConfig {
13 |
14 | @Bean
15 | public DataSource dataSource() {
16 | return new EmbeddedDatabaseBuilder()
17 | .setType(EmbeddedDatabaseType.H2)
18 | .addScript(JdbcDaoImpl.DEFAULT_USER_SCHEMA_DDL_LOCATION)
19 | .build();
20 | }
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/example-14-jdbc-auth-security/src/main/java/io/sfe/jdbcsecurity/JdbcAuthApplication.java:
--------------------------------------------------------------------------------
1 | package io.sfe.jdbcsecurity;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | @SpringBootApplication
7 | public class JdbcAuthApplication {
8 |
9 | public static void main(String[] args) {
10 | SpringApplication.run(JdbcAuthApplication.class, args);
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/example-14-jdbc-auth-security/src/main/java/io/sfe/jdbcsecurity/PasswordEncoderConfig.java:
--------------------------------------------------------------------------------
1 | package io.sfe.jdbcsecurity;
2 |
3 | import org.springframework.context.annotation.Bean;
4 | import org.springframework.context.annotation.Configuration;
5 | import org.springframework.security.crypto.factory.PasswordEncoderFactories;
6 | import org.springframework.security.crypto.password.PasswordEncoder;
7 |
8 | @Configuration
9 | public class PasswordEncoderConfig {
10 |
11 | @Bean
12 | public PasswordEncoder passwordEncoder() {
13 | return PasswordEncoderFactories.createDelegatingPasswordEncoder();
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/example-14-jdbc-auth-security/src/main/java/io/sfe/jdbcsecurity/SecurityConfig.java:
--------------------------------------------------------------------------------
1 | package io.sfe.jdbcsecurity;
2 |
3 | import org.springframework.context.annotation.Bean;
4 | import org.springframework.context.annotation.Configuration;
5 | import org.springframework.security.authentication.AuthenticationManager;
6 | import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
7 | import org.springframework.security.config.annotation.web.builders.HttpSecurity;
8 |
9 | import javax.sql.DataSource;
10 |
11 | @Configuration
12 | public class SecurityConfig {
13 |
14 | @Bean
15 | public AuthenticationManager authenticationManager(
16 | HttpSecurity http,
17 | DataSource dataSource
18 | ) throws Exception {
19 | var authenticationManagerBuilder = http.getSharedObject(AuthenticationManagerBuilder.class);
20 |
21 | authenticationManagerBuilder
22 | .inMemoryAuthentication()
23 | .withUser("admin")
24 | .password("{bcrypt}$2a$10$rkWfnHrSpo0JyNBH4tHRDOeuZACtCU5v4sCQpleWl4P41YuYqQMjC") //admin
25 | .roles("ADMIN");
26 |
27 | authenticationManagerBuilder
28 | .jdbcAuthentication()
29 | .dataSource(dataSource)
30 | .withUser("user")
31 | .password("{bcrypt}$2a$10$GlpFG1Ml3U9AvkOu0D1B9ufnoquX5xqCR/NHaMfBZliYgPa8/e5sK")
32 | .roles("USER");
33 |
34 | return authenticationManagerBuilder.build();
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/example-14-jdbc-auth-security/src/main/resources/application.properties:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vrudas/spring-framework-examples/99c111602e503c07535cc8db76acc703f0b591e1/example-14-jdbc-auth-security/src/main/resources/application.properties
--------------------------------------------------------------------------------
/example-14-jdbc-auth-security/src/main/resources/templates/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Security Example
6 |
7 |
8 | Logged user: Bob
9 | Roles: [ROLE_USER, ROLE_ADMIN]
10 |
11 |
12 |
--------------------------------------------------------------------------------
/example-15-user-flow-security/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'org.springframework.boot' version '3.3.5'
3 | id 'io.spring.dependency-management' version '1.1.7'
4 | }
5 |
6 | dependencies {
7 | implementation 'org.springframework.boot:spring-boot-starter-web'
8 | implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
9 | implementation 'org.springframework.boot:spring-boot-starter-security'
10 | implementation 'org.springframework.boot:spring-boot-starter-data-jdbc'
11 |
12 | runtimeOnly 'com.h2database:h2'
13 | compileOnly("org.springframework.boot:spring-boot-devtools")
14 |
15 | implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6'
16 |
17 | testImplementation 'org.springframework.boot:spring-boot-starter-test'
18 | testImplementation 'org.springframework.security:spring-security-test'
19 | }
20 |
21 | test {
22 | useJUnitPlatform()
23 | }
24 |
--------------------------------------------------------------------------------
/example-15-user-flow-security/src/main/java/io/sfe/userflow/UserFlowApplication.java:
--------------------------------------------------------------------------------
1 | package io.sfe.userflow;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | @SpringBootApplication
7 | public class UserFlowApplication {
8 |
9 | public static void main(String[] args) {
10 | SpringApplication.run(UserFlowApplication.class, args);
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/example-15-user-flow-security/src/main/java/io/sfe/userflow/config/DatabaseConfig.java:
--------------------------------------------------------------------------------
1 | package io.sfe.userflow.config;
2 |
3 | import org.springframework.context.annotation.Bean;
4 | import org.springframework.context.annotation.Configuration;
5 | import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
6 | import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
7 | import org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl;
8 |
9 | import javax.sql.DataSource;
10 |
11 | @Configuration
12 | public class DatabaseConfig {
13 |
14 | @Bean
15 | public DataSource dataSource() {
16 | return new EmbeddedDatabaseBuilder()
17 | .setType(EmbeddedDatabaseType.H2)
18 | .addScript(JdbcDaoImpl.DEFAULT_USER_SCHEMA_DDL_LOCATION)
19 | .build();
20 | }
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/example-15-user-flow-security/src/main/java/io/sfe/userflow/config/SecurityConfig.java:
--------------------------------------------------------------------------------
1 | package io.sfe.userflow.config;
2 |
3 | import org.springframework.context.annotation.Bean;
4 | import org.springframework.context.annotation.Configuration;
5 | import org.springframework.security.core.userdetails.User;
6 | import org.springframework.security.crypto.factory.PasswordEncoderFactories;
7 | import org.springframework.security.crypto.password.PasswordEncoder;
8 | import org.springframework.security.provisioning.JdbcUserDetailsManager;
9 | import org.springframework.security.provisioning.UserDetailsManager;
10 |
11 | import javax.sql.DataSource;
12 |
13 | @Configuration
14 | public class SecurityConfig {
15 |
16 | @Bean
17 | public PasswordEncoder passwordEncoder() {
18 | return PasswordEncoderFactories.createDelegatingPasswordEncoder();
19 | }
20 |
21 | @Bean
22 | public UserDetailsManager users(
23 | DataSource dataSource
24 | ) {
25 | var admin = User.withUsername("admin")
26 | .password("{bcrypt}$2a$10$kF9qWGfBqKqqO9PuG/XLZuuPq601zbtV3F4v8.mYVX0ilBsvbjjpW")
27 | .roles("ADMIN")
28 | .build();
29 |
30 | JdbcUserDetailsManager users = new JdbcUserDetailsManager(dataSource);
31 | users.createUser(admin);
32 |
33 | return users;
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/example-15-user-flow-security/src/main/java/io/sfe/userflow/user/UserController.java:
--------------------------------------------------------------------------------
1 | package io.sfe.userflow.user;
2 |
3 | import org.springframework.security.core.annotation.AuthenticationPrincipal;
4 | import org.springframework.security.core.userdetails.UserDetails;
5 | import org.springframework.web.bind.annotation.GetMapping;
6 | import org.springframework.web.bind.annotation.RequestMapping;
7 | import org.springframework.web.bind.annotation.RestController;
8 |
9 | @RestController
10 | @RequestMapping("/users")
11 | public class UserController {
12 |
13 | @GetMapping("/me")
14 | public UserDetails currentUser(@AuthenticationPrincipal UserDetails userDetails) {
15 | return userDetails;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/example-15-user-flow-security/src/main/java/io/sfe/userflow/user/UserService.java:
--------------------------------------------------------------------------------
1 | package io.sfe.userflow.user;
2 |
3 | import org.springframework.security.core.userdetails.User;
4 | import org.springframework.security.core.userdetails.UserDetails;
5 | import org.springframework.security.crypto.password.PasswordEncoder;
6 | import org.springframework.security.provisioning.UserDetailsManager;
7 | import org.springframework.stereotype.Service;
8 |
9 | @Service
10 | public class UserService {
11 |
12 | private final UserDetailsManager userDetailsManager;
13 | private final PasswordEncoder passwordEncoder;
14 |
15 | public UserService(
16 | UserDetailsManager userDetailsManager,
17 | PasswordEncoder passwordEncoder
18 | ) {
19 | this.userDetailsManager = userDetailsManager;
20 | this.passwordEncoder = passwordEncoder;
21 | }
22 |
23 | void createUser(UserDetails user) {
24 | var encodedPassword = passwordEncoder.encode(user.getPassword());
25 |
26 | var userWithEncodedPassword = User.withUserDetails(user)
27 | .password(encodedPassword)
28 | .build();
29 |
30 | userDetailsManager.createUser(userWithEncodedPassword);
31 | }
32 |
33 | void deleteUser(String username) {
34 | userDetailsManager.deleteUser(username);
35 | }
36 |
37 | void changePassword(String oldPassword, String newPassword) {
38 | var encodedNewPassword = passwordEncoder.encode(newPassword);
39 | userDetailsManager.changePassword(oldPassword, encodedNewPassword);
40 | }
41 |
42 | boolean userExists(String username) {
43 | return userDetailsManager.userExists(username);
44 | }
45 |
46 | UserDetails loadUserByUsername(String username) {
47 | return userDetailsManager.loadUserByUsername(username);
48 | }
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/example-15-user-flow-security/src/main/resources/application.properties:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vrudas/spring-framework-examples/99c111602e503c07535cc8db76acc703f0b591e1/example-15-user-flow-security/src/main/resources/application.properties
--------------------------------------------------------------------------------
/example-15-user-flow-security/src/main/resources/templates/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Security Example
6 |
7 |
8 | Logged user: Bob
9 | Roles: [ROLE_USER, ROLE_ADMIN]
10 |
11 |
12 |
--------------------------------------------------------------------------------
/example-15-user-flow-security/src/test/java/io/sfe/userflow/user/UserControllerTest.java:
--------------------------------------------------------------------------------
1 | package io.sfe.userflow.user;
2 |
3 | import org.junit.jupiter.api.Test;
4 | import org.springframework.beans.factory.annotation.Autowired;
5 | import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
6 | import org.springframework.http.MediaType;
7 | import org.springframework.security.test.context.support.WithMockUser;
8 | import org.springframework.test.web.servlet.MockMvc;
9 |
10 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
11 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
12 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
13 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
14 |
15 | @WebMvcTest
16 | class UserControllerTest {
17 |
18 | @Autowired
19 | private MockMvc mockMvc;
20 |
21 | @Test
22 | @WithMockUser(
23 | username = "user",
24 | password = "pswd",
25 | roles = "USER"
26 | )
27 | void current_user_was_returned() throws Exception {
28 | mockMvc.perform(get("/users/me"))
29 | .andExpect(status().isOk())
30 | .andExpect(content().contentType(MediaType.APPLICATION_JSON))
31 | .andExpect(jsonPath("$").isMap())
32 | .andExpect(jsonPath("$.username").value("user"))
33 | .andExpect(jsonPath("$.password").value("pswd"))
34 | .andExpect(jsonPath("$.authorities").isArray())
35 | .andExpect(jsonPath("$.authorities[0]").isMap())
36 | .andExpect(jsonPath("$.authorities[0].authority").value("ROLE_USER"))
37 | ;
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/example-15-user-flow-security/src/test/java/io/sfe/userflow/user/UserServiceTest.java:
--------------------------------------------------------------------------------
1 | package io.sfe.userflow.user;
2 |
3 | import org.junit.jupiter.api.BeforeEach;
4 | import org.junit.jupiter.api.Test;
5 | import org.springframework.beans.factory.annotation.Autowired;
6 | import org.springframework.boot.test.context.SpringBootTest;
7 | import org.springframework.boot.test.mock.mockito.SpyBean;
8 | import org.springframework.security.core.userdetails.User;
9 | import org.springframework.security.core.userdetails.UserDetails;
10 | import org.springframework.security.crypto.password.PasswordEncoder;
11 | import org.springframework.security.test.context.support.WithMockUser;
12 |
13 | import static org.assertj.core.api.Assertions.assertThat;
14 | import static org.mockito.ArgumentMatchers.anyString;
15 | import static org.mockito.Mockito.doReturn;
16 |
17 | @SpringBootTest
18 | class UserServiceTest {
19 |
20 | @SpyBean
21 | private PasswordEncoder passwordEncoder;
22 |
23 | @Autowired
24 | private UserService userService;
25 |
26 | @BeforeEach
27 | void setUp() {
28 | doReturn("encoded_password")
29 | .when(passwordEncoder).encode(anyString());
30 | }
31 |
32 | @Test
33 | void check_that_admin_user_exist_by_default() {
34 | boolean adminUserExist = userService.userExists("admin");
35 |
36 | assertThat(adminUserExist).isTrue();
37 | }
38 |
39 | @Test
40 | void user_was_created() {
41 | userService.createUser(
42 | User.builder()
43 | .username("user")
44 | .password("user")
45 | .roles("USER")
46 | .build()
47 | );
48 |
49 | boolean userExists = userService.userExists("user");
50 |
51 | assertThat(userExists).isTrue();
52 | }
53 |
54 | @Test
55 | void user_was_found() {
56 | userService.createUser(
57 | User.builder()
58 | .username("user_found")
59 | .password("user_found")
60 | .roles("USER")
61 | .build()
62 | );
63 |
64 | UserDetails user = userService.loadUserByUsername("user_found");
65 |
66 | assertThat(user).isNotNull();
67 | assertThat(user).extracting(UserDetails::getUsername).isEqualTo("user_found");
68 | assertThat(user).extracting(UserDetails::getPassword).isEqualTo("encoded_password");
69 | }
70 |
71 | @Test
72 | @WithMockUser(
73 | username = "user_with_password",
74 | password = "PASSWORD"
75 | )
76 | void user_password_was_changed() {
77 | userService.createUser(
78 | User.withUsername("user_with_password")
79 | .password("password")
80 | .roles("USER")
81 | .build()
82 | );
83 |
84 | userService.changePassword("PASSWORD", "password");
85 |
86 | UserDetails user = userService.loadUserByUsername("user_with_password");
87 |
88 | assertThat(user).extracting(UserDetails::getPassword).isEqualTo("encoded_password");
89 | }
90 |
91 | @Test
92 | void user_was_deleted() {
93 | userService.createUser(
94 | User.withUsername("user_to_delete")
95 | .password("")
96 | .roles("USER")
97 | .build()
98 | );
99 |
100 | assertThat(userService.userExists("user_to_delete")).isTrue();
101 |
102 | userService.deleteUser("user_to_delete");
103 |
104 | assertThat(userService.userExists("user_to_delete")).isFalse();
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/example-16-custom-auth-provider/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'org.springframework.boot' version '3.3.5'
3 | id 'io.spring.dependency-management' version '1.1.7'
4 | }
5 |
6 | dependencies {
7 | implementation 'org.springframework.boot:spring-boot-starter-web'
8 | implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
9 | implementation 'org.springframework.boot:spring-boot-starter-security'
10 | implementation 'org.springframework.boot:spring-boot-starter-data-jdbc'
11 |
12 | runtimeOnly 'com.h2database:h2'
13 | compileOnly("org.springframework.boot:spring-boot-devtools")
14 |
15 | implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6'
16 |
17 | testImplementation 'org.springframework.boot:spring-boot-starter-test'
18 | testImplementation 'org.springframework.security:spring-security-test'
19 | }
20 |
21 | test {
22 | useJUnitPlatform()
23 | }
24 |
--------------------------------------------------------------------------------
/example-16-custom-auth-provider/src/main/java/io/sfe/customauth/CustomAuthApplication.java:
--------------------------------------------------------------------------------
1 | package io.sfe.customauth;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | @SpringBootApplication
7 | public class CustomAuthApplication {
8 |
9 | public static void main(String[] args) {
10 | SpringApplication.run(CustomAuthApplication.class, args);
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/example-16-custom-auth-provider/src/main/java/io/sfe/customauth/auth/LoginPasswordAuthenticationProvider.java:
--------------------------------------------------------------------------------
1 | package io.sfe.customauth.auth;
2 |
3 | import org.springframework.context.annotation.Primary;
4 | import org.springframework.security.authentication.AuthenticationProvider;
5 | import org.springframework.security.authentication.BadCredentialsException;
6 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
7 | import org.springframework.security.core.Authentication;
8 | import org.springframework.security.core.AuthenticationException;
9 | import org.springframework.security.core.userdetails.UserDetails;
10 | import org.springframework.security.crypto.password.PasswordEncoder;
11 | import org.springframework.security.provisioning.UserDetailsManager;
12 | import org.springframework.stereotype.Component;
13 |
14 | import static java.util.Collections.emptyList;
15 |
16 | @Primary
17 | @Component
18 | public class LoginPasswordAuthenticationProvider implements AuthenticationProvider {
19 |
20 | private final UserDetailsManager userDetailsManager;
21 | private final PasswordEncoder passwordEncoder;
22 |
23 | public LoginPasswordAuthenticationProvider(
24 | UserDetailsManager userDetailsManager,
25 | PasswordEncoder passwordEncoder
26 | ) {
27 | this.userDetailsManager = userDetailsManager;
28 | this.passwordEncoder = passwordEncoder;
29 | }
30 |
31 | @Override
32 | public Authentication authenticate(Authentication authentication) throws AuthenticationException {
33 | String username = authentication.getName();
34 | String password = authentication.getCredentials().toString();
35 |
36 | UserDetails user = userDetailsManager.loadUserByUsername(username);
37 |
38 | if (user != null && passwordEncoder.matches(password, user.getPassword())) {
39 | return new UsernamePasswordAuthenticationToken(user, null, emptyList());
40 | } else {
41 | throw new BadCredentialsException("Bad credentials");
42 | }
43 | }
44 |
45 | @Override
46 | public boolean supports(Class> authentication) {
47 | return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/example-16-custom-auth-provider/src/main/java/io/sfe/customauth/config/CommonSecurityConfig.java:
--------------------------------------------------------------------------------
1 | package io.sfe.customauth.config;
2 |
3 | import org.springframework.context.annotation.Bean;
4 | import org.springframework.context.annotation.Configuration;
5 | import org.springframework.security.core.userdetails.User;
6 | import org.springframework.security.crypto.factory.PasswordEncoderFactories;
7 | import org.springframework.security.crypto.password.PasswordEncoder;
8 | import org.springframework.security.provisioning.JdbcUserDetailsManager;
9 | import org.springframework.security.provisioning.UserDetailsManager;
10 |
11 | import javax.sql.DataSource;
12 |
13 | @Configuration
14 | public class CommonSecurityConfig {
15 |
16 | /**
17 | * This bean can be replaced by a custom implementation (often case)
18 | */
19 | @Bean
20 | public UserDetailsManager userDetailsService(DataSource dataSource) {
21 | var admin = User.withUsername("admin")
22 | .password("$2a$10$kF9qWGfBqKqqO9PuG/XLZuuPq601zbtV3F4v8.mYVX0ilBsvbjjpW")
23 | .roles("ADMIN")
24 | .build();
25 |
26 | var users = new JdbcUserDetailsManager(dataSource);
27 | users.createUser(admin);
28 |
29 | return users;
30 | }
31 |
32 | @Bean
33 | public PasswordEncoder passwordEncoder() {
34 | return PasswordEncoderFactories.createDelegatingPasswordEncoder();
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/example-16-custom-auth-provider/src/main/java/io/sfe/customauth/config/DatabaseConfig.java:
--------------------------------------------------------------------------------
1 | package io.sfe.customauth.config;
2 |
3 | import org.springframework.context.annotation.Bean;
4 | import org.springframework.context.annotation.Configuration;
5 | import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
6 | import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
7 | import org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl;
8 |
9 | import javax.sql.DataSource;
10 |
11 | @Configuration
12 | public class DatabaseConfig {
13 |
14 | @Bean
15 | public DataSource dataSource() {
16 | return new EmbeddedDatabaseBuilder()
17 | .setType(EmbeddedDatabaseType.H2)
18 | .addScript(JdbcDaoImpl.DEFAULT_USER_SCHEMA_DDL_LOCATION)
19 | .build();
20 | }
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/example-16-custom-auth-provider/src/main/java/io/sfe/customauth/config/SecurityConfig.java:
--------------------------------------------------------------------------------
1 | package io.sfe.customauth.config;
2 |
3 | import io.sfe.customauth.auth.LoginPasswordAuthenticationProvider;
4 | import org.springframework.beans.factory.annotation.Autowired;
5 | import org.springframework.context.annotation.Bean;
6 | import org.springframework.context.annotation.Configuration;
7 | import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
8 | import org.springframework.security.config.annotation.web.builders.HttpSecurity;
9 | import org.springframework.security.web.SecurityFilterChain;
10 |
11 | @Configuration
12 | public class SecurityConfig {
13 |
14 | @Autowired
15 | public void configureAuthenticationManager(
16 | AuthenticationManagerBuilder auth,
17 | LoginPasswordAuthenticationProvider loginPasswordAuthenticationProvider
18 | ) {
19 | auth.authenticationProvider(loginPasswordAuthenticationProvider);
20 | }
21 |
22 | @Bean
23 | public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
24 | http
25 | .authorizeHttpRequests()
26 | .anyRequest().authenticated()
27 | .and()
28 | .formLogin()
29 | .loginPage("/login")
30 | .usernameParameter("username")
31 | .passwordParameter("password")
32 | .permitAll()
33 | .and()
34 | .logout()
35 | .logoutUrl("/logout")
36 | .logoutSuccessUrl("/login?logout")
37 | .permitAll();
38 |
39 | return http.build();
40 | }
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/example-16-custom-auth-provider/src/main/java/io/sfe/customauth/config/WebConfig.java:
--------------------------------------------------------------------------------
1 | package io.sfe.customauth.config;
2 |
3 | import org.springframework.context.annotation.Configuration;
4 | import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
5 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
6 |
7 | @Configuration
8 | public class WebConfig implements WebMvcConfigurer {
9 |
10 | @Override
11 | public void addViewControllers(ViewControllerRegistry registry) {
12 | registry.addViewController("/login");
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/example-16-custom-auth-provider/src/main/resources/application.properties:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vrudas/spring-framework-examples/99c111602e503c07535cc8db76acc703f0b591e1/example-16-custom-auth-provider/src/main/resources/application.properties
--------------------------------------------------------------------------------
/example-16-custom-auth-provider/src/main/resources/templates/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Security Example
6 |
7 |
8 | Logged user: Bob
9 | Roles: [ROLE_USER, ROLE_ADMIN]
10 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/example-16-custom-auth-provider/src/main/resources/templates/login.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Please Login
4 |
5 |
6 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/example-17-authorization/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'org.springframework.boot' version '3.3.5'
3 | id 'io.spring.dependency-management' version '1.1.7'
4 | }
5 |
6 | dependencies {
7 | implementation 'org.springframework.boot:spring-boot-starter-web'
8 | implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
9 | implementation 'org.springframework.boot:spring-boot-starter-security'
10 | implementation 'org.springframework.boot:spring-boot-starter-data-jdbc'
11 |
12 | runtimeOnly 'com.h2database:h2'
13 | compileOnly("org.springframework.boot:spring-boot-devtools")
14 |
15 | implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6'
16 |
17 | testImplementation 'org.springframework.boot:spring-boot-starter-test'
18 | testImplementation 'org.springframework.security:spring-security-test'
19 | }
20 |
21 | test {
22 | useJUnitPlatform()
23 | }
24 |
--------------------------------------------------------------------------------
/example-17-authorization/src/main/java/io/sfe/authorization/AccessCheckController.java:
--------------------------------------------------------------------------------
1 | package io.sfe.authorization;
2 |
3 | import org.springframework.security.core.annotation.AuthenticationPrincipal;
4 | import org.springframework.security.core.userdetails.UserDetails;
5 | import org.springframework.web.bind.annotation.GetMapping;
6 | import org.springframework.web.bind.annotation.RestController;
7 |
8 | @RestController
9 | public class AccessCheckController {
10 |
11 | @GetMapping("/admin/system-info")
12 | public String showSystemInfo() {
13 | return "systemInfo";
14 | }
15 |
16 | @GetMapping("/name")
17 | public String showUserName(@AuthenticationPrincipal UserDetails userDetails) {
18 | return userDetails.getUsername();
19 | }
20 |
21 | @GetMapping("/read-user")
22 | public UserDetails readUser(@AuthenticationPrincipal UserDetails userDetails) {
23 | return userDetails;
24 | }
25 |
26 | @GetMapping("/delete-user")
27 | public UserDetails deleteUser(@AuthenticationPrincipal UserDetails userDetails) {
28 | return userDetails;
29 | }
30 |
31 |
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/example-17-authorization/src/main/java/io/sfe/authorization/InMemorySecurityConfig.java:
--------------------------------------------------------------------------------
1 | package io.sfe.authorization;
2 |
3 | import org.springframework.beans.factory.annotation.Autowired;
4 | import org.springframework.context.annotation.Bean;
5 | import org.springframework.context.annotation.Configuration;
6 | import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
7 | import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl;
8 | import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
9 | import org.springframework.security.config.annotation.web.builders.HttpSecurity;
10 | import org.springframework.security.core.authority.SimpleGrantedAuthority;
11 | import org.springframework.security.core.userdetails.User;
12 | import org.springframework.security.crypto.factory.PasswordEncoderFactories;
13 | import org.springframework.security.crypto.password.PasswordEncoder;
14 | import org.springframework.security.web.SecurityFilterChain;
15 | import org.springframework.security.web.access.expression.WebExpressionAuthorizationManager;
16 |
17 | @Configuration
18 | public class InMemorySecurityConfig {
19 |
20 | @Bean
21 | public PasswordEncoder passwordEncoder() {
22 | return PasswordEncoderFactories.createDelegatingPasswordEncoder();
23 | }
24 |
25 | @Autowired
26 | void configureAuthenticationManager(
27 | AuthenticationManagerBuilder auth
28 | ) throws Exception {
29 | auth.inMemoryAuthentication()
30 | .withUser(
31 | User.builder()
32 | .username("user")
33 | .password("{bcrypt}$2a$10$GlpFG1Ml3U9AvkOu0D1B9ufnoquX5xqCR/NHaMfBZliYgPa8/e5sK")
34 | .roles("USER")
35 | .authorities(new SimpleGrantedAuthority("READ_USERS"))
36 | .build()
37 | );
38 |
39 | auth.inMemoryAuthentication()
40 | .withUser("admin")
41 | .password("{bcrypt}$2a$10$kF9qWGfBqKqqO9PuG/XLZuuPq601zbtV3F4v8.mYVX0ilBsvbjjpW")
42 | .authorities("DELETE_USERS")
43 | .roles("USER", "ADMIN");
44 | }
45 |
46 | @Bean
47 | public SecurityFilterChain securityFilterChain(
48 | HttpSecurity http
49 | ) throws Exception {
50 | http
51 | .authorizeHttpRequests(authRegistry -> authRegistry
52 | .requestMatchers("/login").permitAll()
53 | .requestMatchers("/read-user/**").hasAnyAuthority("READ_USERS")
54 | .requestMatchers("/admin/**").hasRole("ADMIN")
55 | .requestMatchers("/delete-user/**").access(
56 | new WebExpressionAuthorizationManager("hasRole('ADMIN') and hasAuthority('DELETE_USERS')")
57 | )
58 | .anyRequest().authenticated()
59 | )
60 | .formLogin();
61 |
62 | return http.build();
63 | }
64 |
65 | @Bean
66 | public RoleHierarchy roleHierarchy() {
67 | RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
68 |
69 | roleHierarchy.setHierarchy("ROLE_ADMIN > ROLE_USER > ROLE_GUEST");
70 |
71 | return roleHierarchy;
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/example-17-authorization/src/main/java/io/sfe/authorization/UserAuthorizationApplication.java:
--------------------------------------------------------------------------------
1 | package io.sfe.authorization;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | @SpringBootApplication
7 | public class UserAuthorizationApplication {
8 |
9 | public static void main(String[] args) {
10 | SpringApplication.run(UserAuthorizationApplication.class, args);
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/example-17-authorization/src/main/resources/application.properties:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vrudas/spring-framework-examples/99c111602e503c07535cc8db76acc703f0b591e1/example-17-authorization/src/main/resources/application.properties
--------------------------------------------------------------------------------
/example-17-authorization/src/test/java/io/sfe/authorization/AccessCheckControllerTest.java:
--------------------------------------------------------------------------------
1 | package io.sfe.authorization;
2 |
3 | import org.junit.jupiter.api.Disabled;
4 | import org.junit.jupiter.api.Test;
5 | import org.springframework.beans.factory.annotation.Autowired;
6 | import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
7 | import org.springframework.security.test.context.support.WithMockUser;
8 | import org.springframework.test.web.servlet.MockMvc;
9 |
10 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
11 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
12 |
13 | // TODO: 15.03.2023 Fix the tests as a part of https://github.com/vrudas/spring-framework-examples/issues/101
14 | // implementation
15 | @Disabled("Because of https://github.com/vrudas/spring-framework-examples/issues/101")
16 | @WebMvcTest(AccessCheckController.class)
17 | class AccessCheckControllerTest {
18 |
19 | @Autowired
20 | private MockMvc mockMvc;
21 |
22 | @Test
23 | @WithMockUser
24 | void login_page_is_available_for_user() throws Exception {
25 | mockMvc.perform(get("/login"))
26 | .andExpect(status().isOk());
27 | }
28 |
29 | @Test
30 | @WithMockUser(username = "admin", roles = "ADMIN")
31 | void login_page_is_available_for_admin() throws Exception {
32 | mockMvc.perform(get("/login"))
33 | .andExpect(status().isOk());
34 | }
35 |
36 | @Test
37 | @WithMockUser
38 | void admin_endpoint_is_not_available_for_user() throws Exception {
39 | mockMvc.perform(get("/admin/system-info"))
40 | .andExpect(status().isForbidden());
41 | }
42 |
43 | @Test
44 | @WithMockUser(username = "admin", roles = "ADMIN")
45 | void admin_endpoint_is_available_for_admin() throws Exception {
46 | mockMvc.perform(get("/admin/system-info"))
47 | .andExpect(status().isOk());
48 | }
49 |
50 |
51 | @Test
52 | @WithMockUser
53 | void user_name_endpoint_is_available_for_user() throws Exception {
54 | mockMvc.perform(get("/name"))
55 | .andExpect(status().isOk());
56 | }
57 |
58 | @Test
59 | @WithMockUser(username = "admin", roles = "ADMIN")
60 | void user_name_endpoint_is_available_for_admin() throws Exception {
61 | mockMvc.perform(get("/name"))
62 | .andExpect(status().isOk());
63 | }
64 |
65 | @Test
66 | @WithMockUser
67 | void read_user_endpoint_is_not_available_for_user_without_authority_but_with_role_USER() throws Exception {
68 | mockMvc.perform(get("/read-user"))
69 | .andExpect(status().isForbidden());
70 | }
71 |
72 | @Test
73 | @WithMockUser(authorities = "READ_USERS")
74 | void read_user_endpoint_is_available_for_user_with_authority() throws Exception {
75 | mockMvc.perform(get("/read-user"))
76 | .andExpect(status().isOk());
77 | }
78 |
79 | @Test
80 | @WithMockUser(username = "admin", roles = "ADMIN")
81 | void read_user_endpoint_is_not_available_for_admin_without_authority() throws Exception {
82 | mockMvc.perform(get("/read-user"))
83 | .andExpect(status().isForbidden());
84 | }
85 |
86 | @Test
87 | @WithMockUser(username = "admin", authorities = {"ROLE_ADMIN", "READ_USERS"})
88 | void read_user_endpoint_is_available_for_admin_with_authority() throws Exception {
89 | mockMvc.perform(get("/read-user"))
90 | .andExpect(status().isOk());
91 | }
92 |
93 | @Test
94 | @WithMockUser
95 | void delete_user_endpoint_is_not_available_for_user() throws Exception {
96 | mockMvc.perform(get("/read-user"))
97 | .andExpect(status().isForbidden());
98 | }
99 |
100 | @Test
101 | @WithMockUser(username = "admin", authorities = {"ROLE_ADMIN"})
102 | void delete_user_endpoint_is_not_available_for_admin_without_authority() throws Exception {
103 | mockMvc.perform(get("/delete-user"))
104 | .andExpect(status().isForbidden());
105 | }
106 |
107 | @Test
108 | @WithMockUser(username = "admin", authorities = {"ROLE_ADMIN", "DELETE_USERS"})
109 | void delete_user_endpoint_is_available_for_admin_with_authority() throws Exception {
110 | mockMvc.perform(get("/delete-user"))
111 | .andExpect(status().isOk());
112 | }
113 |
114 | }
115 |
--------------------------------------------------------------------------------
/example-18-method-security/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'org.springframework.boot' version '3.3.5'
3 | id 'io.spring.dependency-management' version '1.1.7'
4 | }
5 |
6 | dependencies {
7 | implementation 'org.springframework.boot:spring-boot-starter-web'
8 | implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
9 | implementation 'org.springframework.boot:spring-boot-starter-security'
10 | implementation 'org.springframework.boot:spring-boot-starter-data-jdbc'
11 |
12 | runtimeOnly 'com.h2database:h2'
13 | compileOnly("org.springframework.boot:spring-boot-devtools")
14 |
15 | implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6'
16 |
17 | testImplementation 'org.springframework.boot:spring-boot-starter-test'
18 | testImplementation 'org.springframework.security:spring-security-test'
19 | }
20 |
21 | test {
22 | useJUnitPlatform()
23 | }
24 |
--------------------------------------------------------------------------------
/example-18-method-security/src/main/java/io/sfe/methodsecurity/AccessCheckController.java:
--------------------------------------------------------------------------------
1 | package io.sfe.methodsecurity;
2 |
3 | import org.springframework.security.access.prepost.PreAuthorize;
4 | import org.springframework.security.core.annotation.AuthenticationPrincipal;
5 | import org.springframework.security.core.userdetails.UserDetails;
6 | import org.springframework.web.bind.annotation.GetMapping;
7 | import org.springframework.web.bind.annotation.RestController;
8 |
9 | @RestController
10 | public class AccessCheckController {
11 |
12 | @GetMapping("/admin/system-info")
13 | @PreAuthorize("hasRole('ADMIN')")
14 | public String showSystemInfo() {
15 | return "systemInfo";
16 | }
17 |
18 | @GetMapping("/name")
19 | @PreAuthorize("permitAll()")
20 | public String showUserName(@AuthenticationPrincipal UserDetails userDetails) {
21 | return userDetails.getUsername();
22 | }
23 |
24 | @GetMapping("/read-user")
25 | @PreAuthorize("hasAnyAuthority('READ_USERS')")
26 | public UserDetails readUser(@AuthenticationPrincipal UserDetails userDetails) {
27 | return userDetails;
28 | }
29 |
30 | @GetMapping("/delete-user")
31 | @PreAuthorize("hasRole('ADMIN') and hasAuthority('DELETE_USERS')")
32 | public UserDetails deleteUser(@AuthenticationPrincipal UserDetails userDetails) {
33 | return userDetails;
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/example-18-method-security/src/main/java/io/sfe/methodsecurity/InMemorySecurityConfig.java:
--------------------------------------------------------------------------------
1 | package io.sfe.methodsecurity;
2 |
3 | import org.springframework.context.annotation.Bean;
4 | import org.springframework.context.annotation.Configuration;
5 | import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
6 | import org.springframework.security.config.annotation.web.builders.HttpSecurity;
7 | import org.springframework.security.core.authority.SimpleGrantedAuthority;
8 | import org.springframework.security.core.userdetails.User;
9 | import org.springframework.security.provisioning.InMemoryUserDetailsManager;
10 | import org.springframework.security.web.SecurityFilterChain;
11 |
12 | @Configuration
13 | @EnableMethodSecurity
14 | public class InMemorySecurityConfig {
15 |
16 | @Bean
17 | public InMemoryUserDetailsManager inMemoryUserDetailsManager() {
18 | var user = User.withUsername("user")
19 | .password("{bcrypt}$2a$10$GlpFG1Ml3U9AvkOu0D1B9ufnoquX5xqCR/NHaMfBZliYgPa8/e5sK") //user
20 | .roles("USER")
21 | .authorities(new SimpleGrantedAuthority("READ_USERS"), new SimpleGrantedAuthority("ROLE_USER"))
22 | .build();
23 |
24 | var admin = User.withUsername("admin")
25 | .password("{bcrypt}$2a$10$ku.DZ5JqOy/dgFgAZkwcSuiaMMCmOt8pVmerZDM5lTWO44MHGCMcC") //admin
26 | .roles("USER", "ADMIN")
27 | .authorities("DELETE_USERS", "ROLE_ADMIN")
28 | .build();
29 |
30 | return new InMemoryUserDetailsManager(user, admin);
31 | }
32 |
33 | @Bean
34 | public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
35 | http.authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
36 | .formLogin()
37 | ;
38 |
39 | return http.build();
40 | }
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/example-18-method-security/src/main/java/io/sfe/methodsecurity/MethodAuthorizationApplication.java:
--------------------------------------------------------------------------------
1 | package io.sfe.methodsecurity;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | @SpringBootApplication
7 | public class MethodAuthorizationApplication {
8 |
9 | public static void main(String[] args) {
10 | SpringApplication.run(MethodAuthorizationApplication.class, args);
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/example-18-method-security/src/test/java/io/sfe/methodsecurity/AccessCheckControllerTest.java:
--------------------------------------------------------------------------------
1 | package io.sfe.methodsecurity;
2 |
3 | import org.junit.jupiter.api.Test;
4 | import org.springframework.beans.factory.annotation.Autowired;
5 | import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
6 | import org.springframework.context.annotation.Import;
7 | import org.springframework.security.test.context.support.WithMockUser;
8 | import org.springframework.test.web.servlet.MockMvc;
9 |
10 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
11 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
12 |
13 | @Import(InMemorySecurityConfig.class)
14 | @WebMvcTest(AccessCheckController.class)
15 | class AccessCheckControllerTest {
16 |
17 | @Autowired
18 | private MockMvc mockMvc;
19 |
20 | @Test
21 | @WithMockUser
22 | void login_page_is_available_for_user() throws Exception {
23 | mockMvc.perform(get("/login"))
24 | .andExpect(status().isOk());
25 | }
26 |
27 | @Test
28 | @WithMockUser(username = "admin", roles = "ADMIN")
29 | void login_page_is_available_for_admin() throws Exception {
30 | mockMvc.perform(get("/login"))
31 | .andExpect(status().isOk());
32 | }
33 |
34 | @Test
35 | @WithMockUser
36 | void admin_endpoint_is_not_available_for_user() throws Exception {
37 | mockMvc.perform(get("/admin/system-info"))
38 | .andExpect(status().isForbidden());
39 | }
40 |
41 | @Test
42 | @WithMockUser(username = "admin", roles = "ADMIN")
43 | void admin_endpoint_is_available_for_admin() throws Exception {
44 | mockMvc.perform(get("/admin/system-info"))
45 | .andExpect(status().isOk());
46 | }
47 |
48 |
49 | @Test
50 | @WithMockUser
51 | void user_name_endpoint_is_available_for_user() throws Exception {
52 | mockMvc.perform(get("/name"))
53 | .andExpect(status().isOk());
54 | }
55 |
56 | @Test
57 | @WithMockUser(username = "admin", roles = "ADMIN")
58 | void user_name_endpoint_is_available_for_admin() throws Exception {
59 | mockMvc.perform(get("/name"))
60 | .andExpect(status().isOk());
61 | }
62 |
63 | @Test
64 | @WithMockUser
65 | void read_user_endpoint_is_not_available_for_user_without_authority_but_with_role_USER() throws Exception {
66 | mockMvc.perform(get("/read-user"))
67 | .andExpect(status().isForbidden());
68 | }
69 |
70 | @Test
71 | @WithMockUser(authorities = "READ_USERS")
72 | void read_user_endpoint_is_available_for_user_with_authority() throws Exception {
73 | mockMvc.perform(get("/read-user"))
74 | .andExpect(status().isOk());
75 | }
76 |
77 | @Test
78 | @WithMockUser(username = "admin", roles = "ADMIN")
79 | void read_user_endpoint_is_not_available_for_admin_without_authority() throws Exception {
80 | mockMvc.perform(get("/read-user"))
81 | .andExpect(status().isForbidden());
82 | }
83 |
84 | @Test
85 | @WithMockUser(username = "admin", authorities = {"ROLE_ADMIN", "READ_USERS"})
86 | void read_user_endpoint_is_available_for_admin_with_authority() throws Exception {
87 | mockMvc.perform(get("/read-user"))
88 | .andExpect(status().isOk());
89 | }
90 |
91 | @Test
92 | @WithMockUser
93 | void delete_user_endpoint_is_not_available_for_user() throws Exception {
94 | mockMvc.perform(get("/read-user"))
95 | .andExpect(status().isForbidden());
96 | }
97 |
98 | @Test
99 | @WithMockUser(username = "admin", authorities = {"ROLE_ADMIN"})
100 | void delete_user_endpoint_is_not_available_for_admin_without_authority() throws Exception {
101 | mockMvc.perform(get("/delete-user"))
102 | .andExpect(status().isForbidden());
103 | }
104 |
105 | @Test
106 | @WithMockUser(username = "admin", authorities = {"ROLE_ADMIN", "DELETE_USERS"})
107 | void delete_user_endpoint_is_available_for_admin_with_authority() throws Exception {
108 | mockMvc.perform(get("/delete-user"))
109 | .andExpect(status().isOk());
110 | }
111 |
112 | }
113 |
--------------------------------------------------------------------------------
/example-19-remember-me/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'org.springframework.boot' version '3.3.5'
3 | id 'io.spring.dependency-management' version '1.1.7'
4 | }
5 |
6 | dependencies {
7 | implementation 'org.springframework.boot:spring-boot-starter-web'
8 | implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
9 | implementation 'org.springframework.boot:spring-boot-starter-security'
10 | implementation 'org.springframework.boot:spring-boot-starter-data-jdbc'
11 |
12 | runtimeOnly 'com.h2database:h2'
13 | compileOnly("org.springframework.boot:spring-boot-devtools")
14 |
15 | implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6'
16 |
17 | testImplementation 'org.springframework.boot:spring-boot-starter-test'
18 | testImplementation 'org.springframework.security:spring-security-test'
19 | }
20 |
21 | test {
22 | useJUnitPlatform()
23 | }
24 |
--------------------------------------------------------------------------------
/example-19-remember-me/src/main/java/io/sfe/rememberme/RememberMeApplication.java:
--------------------------------------------------------------------------------
1 | package io.sfe.rememberme;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | @SpringBootApplication
7 | public class RememberMeApplication {
8 |
9 | public static void main(String[] args) {
10 | SpringApplication.run(RememberMeApplication.class, args);
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/example-19-remember-me/src/main/java/io/sfe/rememberme/SecurityConfig.java:
--------------------------------------------------------------------------------
1 | package io.sfe.rememberme;
2 |
3 | import org.springframework.context.annotation.Bean;
4 | import org.springframework.context.annotation.Configuration;
5 | import org.springframework.security.config.annotation.web.builders.HttpSecurity;
6 | import org.springframework.security.core.userdetails.User;
7 | import org.springframework.security.provisioning.InMemoryUserDetailsManager;
8 | import org.springframework.security.web.SecurityFilterChain;
9 |
10 | @Configuration
11 | public class SecurityConfig {
12 |
13 | @Bean
14 | public InMemoryUserDetailsManager inMemoryUserDetailsManager() {
15 | var user = User.withUsername("user")
16 | .password("{bcrypt}$2a$10$j5p/7VDz5g2PEXHPBw30VugVGLWK9zUA9WMPD0IkUpGBZPwUKHEaG")
17 | .roles("USER")
18 | .build();
19 |
20 | var admin = User.withUsername("admin")
21 | .password("{bcrypt}$2a$10$kF9qWGfBqKqqO9PuG/XLZuuPq601zbtV3F4v8.mYVX0ilBsvbjjpW")
22 | .roles("ADMIN")
23 | .build();
24 |
25 | return new InMemoryUserDetailsManager(user, admin);
26 | }
27 |
28 | @Bean
29 | public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
30 | http.authorizeHttpRequests()
31 | .requestMatchers("/anonymous*").anonymous()
32 | .requestMatchers("/login*").permitAll()
33 | .anyRequest().authenticated()
34 | .and()
35 |
36 | .formLogin()
37 | .and()
38 |
39 | .logout().deleteCookies("JSESSIONID")
40 | .and()
41 |
42 | .rememberMe()
43 | .key("uniqueAndSecret")
44 | .rememberMeCookieName("remember-me")
45 | .rememberMeParameter("remember-me")
46 | .tokenValiditySeconds(15)
47 | // .useSecureCookie(true) // RememberMe only on HTTPS secured
48 | // .tokenRepository(jdbcTokenRepository())
49 | ;
50 |
51 | return http.build();
52 | }
53 |
54 | }
55 |
--------------------------------------------------------------------------------
/example-19-remember-me/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | server.servlet.session.timeout=10
2 |
--------------------------------------------------------------------------------
/example-19-remember-me/src/main/resources/templates/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Security Example
6 |
7 |
8 | Logged user: Bob
9 |
10 |
11 |
--------------------------------------------------------------------------------
/example-20-oauth/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'org.springframework.boot' version '3.3.5'
3 | id 'io.spring.dependency-management' version '1.1.7'
4 | }
5 |
6 | dependencies {
7 | implementation 'org.springframework.boot:spring-boot-starter-web'
8 | implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
9 | implementation 'org.springframework.boot:spring-boot-starter-security'
10 | implementation 'org.springframework.boot:spring-boot-starter-data-jdbc'
11 | implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
12 |
13 | runtimeOnly 'com.h2database:h2'
14 | compileOnly("org.springframework.boot:spring-boot-devtools")
15 |
16 | implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6'
17 |
18 | implementation 'org.webjars:webjars-locator-core'
19 | implementation 'org.webjars:jquery:3.7.1'
20 | implementation 'org.webjars:bootstrap:5.3.6'
21 | implementation 'org.webjars:js-cookie:3.0.1'
22 |
23 | testImplementation 'org.springframework.boot:spring-boot-starter-test'
24 | testImplementation 'org.springframework.security:spring-security-test'
25 | }
26 |
27 | test {
28 | useJUnitPlatform()
29 | }
30 |
--------------------------------------------------------------------------------
/example-20-oauth/src/main/java/io/sfe/oauth/OAuthApplication.java:
--------------------------------------------------------------------------------
1 | package io.sfe.oauth;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 | import org.springframework.context.annotation.PropertySource;
6 |
7 | @SpringBootApplication
8 | @PropertySource("classpath:secrets/secrets.properties")
9 | public class OAuthApplication {
10 |
11 | public static void main(String[] args) {
12 | SpringApplication.run(OAuthApplication.class, args);
13 | }
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/example-20-oauth/src/main/java/io/sfe/oauth/SecurityConfig.java:
--------------------------------------------------------------------------------
1 | package io.sfe.oauth;
2 |
3 | import org.springframework.context.annotation.Bean;
4 | import org.springframework.context.annotation.Configuration;
5 | import org.springframework.security.config.annotation.web.builders.HttpSecurity;
6 | import org.springframework.security.web.SecurityFilterChain;
7 | import org.springframework.security.web.authentication.Http403ForbiddenEntryPoint;
8 | import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
9 |
10 | @Configuration
11 | public class SecurityConfig {
12 |
13 | @Bean
14 | public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
15 | http.authorizeHttpRequests()
16 | .requestMatchers("/", "/error", "/webjars/**").permitAll()
17 | .anyRequest().authenticated()
18 | .and()
19 |
20 | .exceptionHandling()
21 | .authenticationEntryPoint(new Http403ForbiddenEntryPoint())
22 | .and()
23 |
24 | .oauth2Login()
25 | .and()
26 |
27 | .logout()
28 | .logoutSuccessUrl("/").permitAll()
29 | .and()
30 |
31 | .csrf()
32 | .ignoringRequestMatchers("/login", "/logout")
33 | .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
34 | ;
35 |
36 | return http.build();
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/example-20-oauth/src/main/java/io/sfe/oauth/UserController.java:
--------------------------------------------------------------------------------
1 | package io.sfe.oauth;
2 |
3 | import org.springframework.security.core.annotation.AuthenticationPrincipal;
4 | import org.springframework.security.oauth2.core.user.OAuth2User;
5 | import org.springframework.web.bind.annotation.GetMapping;
6 | import org.springframework.web.bind.annotation.RequestMapping;
7 | import org.springframework.web.bind.annotation.RestController;
8 |
9 | import java.util.Map;
10 |
11 | @RestController
12 | @RequestMapping("/users")
13 | public class UserController {
14 |
15 | @GetMapping("/me")
16 | public Map currentUser(@AuthenticationPrincipal OAuth2User user) {
17 | return Map.of("name", user.getName());
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/example-20-oauth/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | spring.security.oauth2.client.registration.github.client-id=${sfe.github.client.id}
2 | spring.security.oauth2.client.registration.github.client-secret=${sfe.github.client.secret}
3 |
--------------------------------------------------------------------------------
/example-20-oauth/src/main/resources/templates/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | OAuth App
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
OAuth App
15 |
16 |
19 |
20 | Logged in as:
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/example-20-oauth/src/test/java/io/sfe/oauth/UserControllerTest.java:
--------------------------------------------------------------------------------
1 | package io.sfe.oauth;
2 |
3 | import org.junit.jupiter.api.Disabled;
4 | import org.junit.jupiter.api.Test;
5 | import org.springframework.beans.factory.annotation.Autowired;
6 | import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
7 | import org.springframework.security.core.context.SecurityContextImpl;
8 | import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
9 | import org.springframework.security.oauth2.core.user.DefaultOAuth2User;
10 | import org.springframework.security.oauth2.core.user.OAuth2UserAuthority;
11 | import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
12 | import org.springframework.test.web.servlet.MockMvc;
13 |
14 | import java.util.Map;
15 | import java.util.Set;
16 |
17 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
18 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
19 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
20 |
21 | @Disabled("Because require real OAuth secrets under resources/secrets/secrets.properties")
22 | @WebMvcTest(UserController.class)
23 | class UserControllerTest {
24 |
25 | @Autowired
26 | private MockMvc mockMvc;
27 |
28 | @Test
29 | void currentUser() throws Exception {
30 | var principal = buildPrincipal();
31 |
32 | mockMvc.perform(
33 | get("/users/me")
34 | .sessionAttr(
35 | HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY,
36 | new SecurityContextImpl(principal)
37 | )
38 | ).andExpect(status().isOk())
39 | .andExpect(jsonPath("$.name").value("user"));
40 | }
41 |
42 | private OAuth2AuthenticationToken buildPrincipal() {
43 | var attributes = Map.of("display_name", "user");
44 | var authorities = Set.of(new OAuth2UserAuthority(attributes));
45 | var oauth2User = new DefaultOAuth2User(authorities, attributes, "display_name");
46 |
47 | return new OAuth2AuthenticationToken(
48 | oauth2User,
49 | authorities,
50 | "test_client_registration_id"
51 | );
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/example-21-jwt/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'org.springframework.boot' version '3.3.5'
3 | id 'io.spring.dependency-management' version '1.1.7'
4 | }
5 |
6 | dependencies {
7 | implementation 'org.springframework.boot:spring-boot-starter-web'
8 | implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
9 | implementation 'org.springframework.boot:spring-boot-starter-security'
10 | implementation 'org.springframework.boot:spring-boot-starter-data-jdbc'
11 |
12 | runtimeOnly 'com.h2database:h2'
13 | compileOnly("org.springframework.boot:spring-boot-devtools")
14 |
15 | implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6'
16 |
17 | implementation 'org.webjars:webjars-locator-core'
18 | implementation 'org.webjars:jquery:3.7.1'
19 | implementation 'org.webjars:bootstrap:5.3.6'
20 | implementation 'org.webjars:js-cookie:3.0.1'
21 |
22 | implementation 'io.jsonwebtoken:jjwt-api:0.12.6'
23 | runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.12.6'
24 | runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.12.6'
25 |
26 | testImplementation 'org.springframework.boot:spring-boot-starter-test'
27 | testImplementation 'org.springframework.security:spring-security-test'
28 | }
29 |
30 | test {
31 | useJUnitPlatform()
32 | }
33 |
--------------------------------------------------------------------------------
/example-21-jwt/src/main/java/io/sfe/jwt/AppConfig.java:
--------------------------------------------------------------------------------
1 | package io.sfe.jwt;
2 |
3 | import org.springframework.context.annotation.Bean;
4 | import org.springframework.context.annotation.Configuration;
5 |
6 | import java.time.Clock;
7 |
8 | @Configuration
9 | public class AppConfig {
10 |
11 | @Bean
12 | public Clock clock() {
13 | return Clock.systemUTC();
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/example-21-jwt/src/main/java/io/sfe/jwt/JwtApplication.java:
--------------------------------------------------------------------------------
1 | package io.sfe.jwt;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | @SpringBootApplication
7 | public class JwtApplication {
8 |
9 | public static void main(String[] args) {
10 | SpringApplication.run(JwtApplication.class, args);
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/example-21-jwt/src/main/java/io/sfe/jwt/UserController.java:
--------------------------------------------------------------------------------
1 | package io.sfe.jwt;
2 |
3 | import org.springframework.security.core.annotation.AuthenticationPrincipal;
4 | import org.springframework.web.bind.annotation.GetMapping;
5 | import org.springframework.web.bind.annotation.RequestMapping;
6 | import org.springframework.web.bind.annotation.RestController;
7 |
8 | @RestController
9 | @RequestMapping("/users")
10 | public class UserController {
11 |
12 | @GetMapping("/me")
13 | public String currentUser(@AuthenticationPrincipal String username) {
14 | return username;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/example-21-jwt/src/main/java/io/sfe/jwt/security/CommonSecurityConfig.java:
--------------------------------------------------------------------------------
1 | package io.sfe.jwt.security;
2 |
3 | import org.springframework.context.annotation.Bean;
4 | import org.springframework.context.annotation.Configuration;
5 | import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
6 | import org.springframework.security.crypto.factory.PasswordEncoderFactories;
7 | import org.springframework.security.crypto.password.PasswordEncoder;
8 | import org.springframework.security.provisioning.UserDetailsManager;
9 |
10 | @Configuration
11 | public class CommonSecurityConfig {
12 |
13 | @Bean
14 | public PasswordEncoder passwordEncoder() {
15 | return PasswordEncoderFactories.createDelegatingPasswordEncoder();
16 | }
17 |
18 | @Bean
19 | public UserDetailsManager userDetailsService(
20 | AuthenticationManagerBuilder authenticationManagerBuilder
21 | ) throws Exception {
22 | return authenticationManagerBuilder
23 | .inMemoryAuthentication()
24 | .withUser("user")
25 | .password("{bcrypt}$2a$10$GlpFG1Ml3U9AvkOu0D1B9ufnoquX5xqCR/NHaMfBZliYgPa8/e5sK")
26 | .roles("USER")
27 | .and()
28 | .withUser("admin")
29 | .password("{bcrypt}$2a$10$ku.DZ5JqOy/dgFgAZkwcSuiaMMCmOt8pVmerZDM5lTWO44MHGCMcC")
30 | .roles("ADMIN")
31 | .and()
32 | .getUserDetailsService();
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/example-21-jwt/src/main/java/io/sfe/jwt/security/SecurityConfig.java:
--------------------------------------------------------------------------------
1 | package io.sfe.jwt.security;
2 |
3 | import io.sfe.jwt.security.jwt.JwtAuthenticationFilter;
4 | import io.sfe.jwt.security.jwt.JwtAuthorizationFilter;
5 | import io.sfe.jwt.security.jwt.JwtTokenGenerator;
6 | import io.sfe.jwt.security.jwt.JwtTokenProvider;
7 | import io.sfe.jwt.security.jwt.UserDetailsExtractor;
8 | import org.springframework.beans.factory.annotation.Autowired;
9 | import org.springframework.context.annotation.Bean;
10 | import org.springframework.context.annotation.Configuration;
11 | import org.springframework.security.authentication.AuthenticationManager;
12 | import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
13 | import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
14 | import org.springframework.security.config.annotation.web.builders.HttpSecurity;
15 | import org.springframework.security.config.http.SessionCreationPolicy;
16 | import org.springframework.security.web.SecurityFilterChain;
17 |
18 | @Configuration
19 | public class SecurityConfig {
20 |
21 | private final JwtTokenGenerator jwtTokenGenerator;
22 | private final UserDetailsExtractor userDetailsExtractor;
23 |
24 | public SecurityConfig(
25 | JwtTokenGenerator jwtTokenGenerator,
26 | UserDetailsExtractor userDetailsExtractor
27 | ) {
28 | this.jwtTokenGenerator = jwtTokenGenerator;
29 | this.userDetailsExtractor = userDetailsExtractor;
30 | }
31 |
32 | @Autowired
33 | void configureAuthenticationManager(
34 | AuthenticationManagerBuilder auth,
35 | JwtTokenProvider jwtTokenProvider
36 | ) {
37 | auth.authenticationProvider(jwtTokenProvider);
38 | }
39 |
40 | @Bean
41 | public AuthenticationManager authenticationManager(
42 | AuthenticationConfiguration authenticationConfiguration
43 | ) throws Exception {
44 | return authenticationConfiguration.getAuthenticationManager();
45 | }
46 |
47 | @Bean
48 | public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
49 | var authenticationManager = authenticationManager(http.getSharedObject(AuthenticationConfiguration.class));
50 |
51 | http
52 | .cors()
53 | .and()
54 | .csrf().disable()
55 |
56 | .authorizeHttpRequests()
57 | .requestMatchers("/login").permitAll()
58 | .anyRequest().authenticated()
59 | .and()
60 | .addFilter(new JwtAuthenticationFilter(authenticationManager, jwtTokenGenerator))
61 | .addFilter(new JwtAuthorizationFilter(authenticationManager, userDetailsExtractor))
62 | // this disables session creation on Spring Security
63 | .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
64 |
65 | return http.build();
66 | }
67 |
68 | }
69 |
--------------------------------------------------------------------------------
/example-21-jwt/src/main/java/io/sfe/jwt/security/jwt/JwtAuthenticationFilter.java:
--------------------------------------------------------------------------------
1 | package io.sfe.jwt.security.jwt;
2 |
3 | import jakarta.servlet.FilterChain;
4 | import jakarta.servlet.http.HttpServletRequest;
5 | import jakarta.servlet.http.HttpServletResponse;
6 | import org.springframework.http.HttpHeaders;
7 | import org.springframework.security.authentication.AuthenticationManager;
8 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
9 | import org.springframework.security.core.Authentication;
10 | import org.springframework.security.core.AuthenticationException;
11 | import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
12 |
13 | import static java.util.Objects.requireNonNullElse;
14 |
15 | public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
16 |
17 | private static final String BEARER_TOKEN_PREFIX = "Bearer ";
18 |
19 | private final JwtTokenGenerator jwtTokenGenerator;
20 |
21 | public JwtAuthenticationFilter(
22 | AuthenticationManager authenticationManager,
23 | JwtTokenGenerator jwtTokenGenerator
24 | ) {
25 | super(authenticationManager);
26 | this.jwtTokenGenerator = jwtTokenGenerator;
27 | }
28 |
29 | @Override
30 | public Authentication attemptAuthentication(
31 | HttpServletRequest request,
32 | HttpServletResponse response
33 | ) throws AuthenticationException {
34 | String username = requireNonNullElse(obtainUsername(request), "").strip();
35 | String password = requireNonNullElse(obtainPassword(request), "").strip();
36 |
37 | var authRequest = new UsernamePasswordAuthenticationToken(username, password);
38 |
39 | return this.getAuthenticationManager().authenticate(authRequest);
40 | }
41 |
42 | @Override
43 | protected void successfulAuthentication(
44 | HttpServletRequest request,
45 | HttpServletResponse response,
46 | FilterChain chain,
47 | Authentication authResult
48 | ) {
49 | String token = jwtTokenGenerator.generateToken(authResult.getName());
50 | response.addHeader(HttpHeaders.AUTHORIZATION, BEARER_TOKEN_PREFIX + token);
51 | }
52 |
53 | }
54 |
--------------------------------------------------------------------------------
/example-21-jwt/src/main/java/io/sfe/jwt/security/jwt/JwtAuthorizationFilter.java:
--------------------------------------------------------------------------------
1 | package io.sfe.jwt.security.jwt;
2 |
3 | import jakarta.servlet.FilterChain;
4 | import jakarta.servlet.ServletException;
5 | import jakarta.servlet.http.HttpServletRequest;
6 | import jakarta.servlet.http.HttpServletResponse;
7 | import org.springframework.http.HttpHeaders;
8 | import org.springframework.security.authentication.AuthenticationManager;
9 | import org.springframework.security.authentication.BadCredentialsException;
10 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
11 | import org.springframework.security.core.AuthenticationException;
12 | import org.springframework.security.core.context.SecurityContextHolder;
13 | import org.springframework.security.core.userdetails.UserDetails;
14 | import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
15 |
16 | import java.io.IOException;
17 |
18 | public class JwtAuthorizationFilter extends BasicAuthenticationFilter {
19 |
20 | private static final String BEARER_TOKEN_PREFIX = "Bearer ";
21 | private static final String BEARER_TOKEN_NOT_FOUND_MESSAGE =
22 | "Did not process authentication request since failed to find "
23 | + BEARER_TOKEN_PREFIX + "token " + HttpHeaders.AUTHORIZATION + " header";
24 |
25 | private static final Runnable EXPIRED_OR_INVALID_JWT_TOKEN_ACTION = () -> {
26 | throw new BadCredentialsException("Expired or invalid JWT token");
27 | };
28 |
29 | private final UserDetailsExtractor userDetailsExtractor;
30 |
31 | public JwtAuthorizationFilter(
32 | AuthenticationManager authenticationManager,
33 | UserDetailsExtractor userDetailsExtractor
34 | ) {
35 | super(authenticationManager);
36 | this.userDetailsExtractor = userDetailsExtractor;
37 | }
38 |
39 | @Override
40 | protected void doFilterInternal(
41 | HttpServletRequest request,
42 | HttpServletResponse response,
43 | FilterChain filterChain
44 | ) throws ServletException, IOException {
45 | var token = resolveToken(request);
46 |
47 | if (token == null) {
48 | logger.warn(BEARER_TOKEN_NOT_FOUND_MESSAGE);
49 | filterChain.doFilter(request, response);
50 | return;
51 | }
52 |
53 | try {
54 | userDetailsExtractor.extractFromToken(token)
55 | .map(this::createAuthentication)
56 | .ifPresentOrElse(this::authenticate, EXPIRED_OR_INVALID_JWT_TOKEN_ACTION);
57 | } catch (AuthenticationException e) {
58 | //this is very important, since it guarantees the user is not authenticated at all
59 | SecurityContextHolder.clearContext();
60 | response.sendError(HttpServletResponse.SC_UNAUTHORIZED, e.getMessage());
61 | return;
62 | }
63 |
64 | filterChain.doFilter(request, response);
65 | }
66 |
67 | public String resolveToken(HttpServletRequest request) {
68 | String bearerToken = request.getHeader(HttpHeaders.AUTHORIZATION);
69 | if (bearerToken != null && bearerToken.startsWith(BEARER_TOKEN_PREFIX)) {
70 | return bearerToken.substring(BEARER_TOKEN_PREFIX.length());
71 | }
72 | return null;
73 | }
74 |
75 | private UsernamePasswordAuthenticationToken createAuthentication(UserDetails userDetails) {
76 | return new UsernamePasswordAuthenticationToken(
77 | userDetails.getUsername(),
78 | userDetails.getPassword(),
79 | userDetails.getAuthorities()
80 | );
81 | }
82 |
83 | private void authenticate(UsernamePasswordAuthenticationToken authentication) {
84 | SecurityContextHolder.getContext().setAuthentication(authentication);
85 | }
86 |
87 | }
88 |
--------------------------------------------------------------------------------
/example-21-jwt/src/main/java/io/sfe/jwt/security/jwt/JwtTokenGenerator.java:
--------------------------------------------------------------------------------
1 | package io.sfe.jwt.security.jwt;
2 |
3 | import io.jsonwebtoken.Jwts;
4 | import io.jsonwebtoken.security.Keys;
5 | import org.springframework.beans.factory.annotation.Value;
6 | import org.springframework.stereotype.Component;
7 |
8 | import java.time.Clock;
9 | import java.util.Date;
10 |
11 | import static io.sfe.jwt.security.jwt.JwtTokenUtil.encodeSecretKey;
12 |
13 | @Component
14 | public class JwtTokenGenerator {
15 |
16 | private final String secretKey;
17 | private final long tokenExpireMilliseconds;
18 | private final Clock clock;
19 |
20 | public JwtTokenGenerator(
21 | @Value("${security.jwt.token.secret-key}") String secretKey,
22 | @Value("${security.jwt.token.expire-milliseconds}") long tokenExpireMilliseconds,
23 | Clock clock
24 | ) {
25 | this.secretKey = secretKey;
26 | this.tokenExpireMilliseconds = tokenExpireMilliseconds;
27 | this.clock = clock;
28 | }
29 |
30 | String generateToken(String username) {
31 | Date expireDate = new Date(clock.millis() + tokenExpireMilliseconds);
32 |
33 | return Jwts.builder()
34 | .setSubject(username)
35 | .setExpiration(expireDate)
36 | .signWith(Keys.hmacShaKeyFor(encodeSecretKey(secretKey)))
37 | .compact();
38 | }
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/example-21-jwt/src/main/java/io/sfe/jwt/security/jwt/JwtTokenProvider.java:
--------------------------------------------------------------------------------
1 | package io.sfe.jwt.security.jwt;
2 |
3 | import org.springframework.context.annotation.Primary;
4 | import org.springframework.security.authentication.AuthenticationProvider;
5 | import org.springframework.security.authentication.BadCredentialsException;
6 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
7 | import org.springframework.security.core.Authentication;
8 | import org.springframework.security.core.AuthenticationException;
9 | import org.springframework.security.core.userdetails.UserDetails;
10 | import org.springframework.security.crypto.password.PasswordEncoder;
11 | import org.springframework.security.provisioning.UserDetailsManager;
12 | import org.springframework.stereotype.Component;
13 |
14 | @Primary
15 | @Component
16 | public class JwtTokenProvider implements AuthenticationProvider {
17 |
18 | private final UserDetailsManager userDetailsManager;
19 | private final PasswordEncoder passwordEncoder;
20 |
21 | public JwtTokenProvider(
22 | UserDetailsManager userDetailsManager,
23 | PasswordEncoder passwordEncoder
24 | ) {
25 | this.userDetailsManager = userDetailsManager;
26 | this.passwordEncoder = passwordEncoder;
27 | }
28 |
29 | @Override
30 | public Authentication authenticate(Authentication authenticationRequest) throws AuthenticationException {
31 | String username = authenticationRequest.getName();
32 | String password = authenticationRequest.getCredentials().toString();
33 |
34 | UserDetails user = userDetailsManager.loadUserByUsername(username);
35 |
36 | if (user != null && passwordEncoder.matches(password, user.getPassword())) {
37 | return new UsernamePasswordAuthenticationToken(user, user.getPassword(), user.getAuthorities());
38 | } else {
39 | throw new BadCredentialsException("Bad credentials");
40 | }
41 | }
42 |
43 | @Override
44 | public boolean supports(Class> authentication) {
45 | return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/example-21-jwt/src/main/java/io/sfe/jwt/security/jwt/JwtTokenUtil.java:
--------------------------------------------------------------------------------
1 | package io.sfe.jwt.security.jwt;
2 |
3 | import java.nio.charset.StandardCharsets;
4 | import java.util.Base64;
5 |
6 | public class JwtTokenUtil {
7 |
8 | private JwtTokenUtil() {}
9 |
10 | static byte[] encodeSecretKey(String secretKey) {
11 | return Base64.getEncoder()
12 | .encodeToString(secretKey.getBytes())
13 | .getBytes(StandardCharsets.UTF_8);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/example-21-jwt/src/main/java/io/sfe/jwt/security/jwt/UserDetailsExtractor.java:
--------------------------------------------------------------------------------
1 | package io.sfe.jwt.security.jwt;
2 |
3 | import io.jsonwebtoken.Claims;
4 | import io.jsonwebtoken.Jws;
5 | import io.jsonwebtoken.Jwts;
6 | import io.jsonwebtoken.security.Keys;
7 | import org.springframework.beans.factory.annotation.Value;
8 | import org.springframework.security.authentication.CredentialsExpiredException;
9 | import org.springframework.security.core.userdetails.UserDetails;
10 | import org.springframework.security.core.userdetails.UserDetailsService;
11 | import org.springframework.stereotype.Component;
12 |
13 | import java.util.Optional;
14 |
15 | import static io.sfe.jwt.security.jwt.JwtTokenUtil.encodeSecretKey;
16 |
17 | @Component
18 | public class UserDetailsExtractor {
19 |
20 | private final String secretKey;
21 | private final UserDetailsService userDetailsService;
22 |
23 | public UserDetailsExtractor(
24 | @Value("${security.jwt.token.secret-key}") String secretKey,
25 | UserDetailsService userDetailsService
26 | ) {
27 | this.secretKey = secretKey;
28 | this.userDetailsService = userDetailsService;
29 | }
30 |
31 | Optional extractFromToken(String token) {
32 | try {
33 | var claimsJws = parseToken(token);
34 | var username = claimsJws.getPayload().getSubject();
35 |
36 | return Optional.ofNullable(userDetailsService.loadUserByUsername(username));
37 | } catch (Exception e) {
38 | throw new CredentialsExpiredException("Expired or invalid JWT token");
39 | }
40 | }
41 |
42 | private Jws parseToken(String token) {
43 | return Jwts.parser()
44 | .verifyWith(Keys.hmacShaKeyFor(encodeSecretKey(secretKey)))
45 | .build()
46 | .parseSignedClaims(token);
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/example-21-jwt/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | security.jwt.token.secret-key=Hardstyle is an electronic dance genre that emerged in the late 90s in the Netherlands.
2 | security.jwt.token.expire-milliseconds=60000
3 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vrudas/spring-framework-examples/99c111602e503c07535cc8db76acc703f0b591e1/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-all.zip
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | #
4 | # Copyright © 2015-2021 the original authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | #
21 | # Gradle start up script for POSIX generated by Gradle.
22 | #
23 | # Important for running:
24 | #
25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
26 | # noncompliant, but you have some other compliant shell such as ksh or
27 | # bash, then to run this script, type that shell name before the whole
28 | # command line, like:
29 | #
30 | # ksh Gradle
31 | #
32 | # Busybox and similar reduced shells will NOT work, because this script
33 | # requires all of these POSIX shell features:
34 | # * functions;
35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»;
37 | # * compound commands having a testable exit status, especially «case»;
38 | # * various built-in commands including «command», «set», and «ulimit».
39 | #
40 | # Important for patching:
41 | #
42 | # (2) This script targets any POSIX shell, so it avoids extensions provided
43 | # by Bash, Ksh, etc; in particular arrays are avoided.
44 | #
45 | # The "traditional" practice of packing multiple parameters into a
46 | # space-separated string is a well documented source of bugs and security
47 | # problems, so this is (mostly) avoided, by progressively accumulating
48 | # options in "$@", and eventually passing that to Java.
49 | #
50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
52 | # see the in-line comments for details.
53 | #
54 | # There are tweaks for specific operating systems such as AIX, CygWin,
55 | # Darwin, MinGW, and NonStop.
56 | #
57 | # (3) This script is generated from the Groovy template
58 | # https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
59 | # within the Gradle project.
60 | #
61 | # You can find Gradle at https://github.com/gradle/gradle/.
62 | #
63 | ##############################################################################
64 |
65 | # Attempt to set APP_HOME
66 |
67 | # Resolve links: $0 may be a link
68 | app_path=$0
69 |
70 | # Need this for daisy-chained symlinks.
71 | while
72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
73 | [ -h "$app_path" ]
74 | do
75 | ls=$( ls -ld "$app_path" )
76 | link=${ls#*' -> '}
77 | case $link in #(
78 | /*) app_path=$link ;; #(
79 | *) app_path=$APP_HOME$link ;;
80 | esac
81 | done
82 |
83 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
84 |
85 | APP_NAME="Gradle"
86 | APP_BASE_NAME=${0##*/}
87 |
88 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
89 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
90 |
91 | # Use the maximum available, or set MAX_FD != -1 to use that value.
92 | MAX_FD=maximum
93 |
94 | warn () {
95 | echo "$*"
96 | } >&2
97 |
98 | die () {
99 | echo
100 | echo "$*"
101 | echo
102 | exit 1
103 | } >&2
104 |
105 | # OS specific support (must be 'true' or 'false').
106 | cygwin=false
107 | msys=false
108 | darwin=false
109 | nonstop=false
110 | case "$( uname )" in #(
111 | CYGWIN* ) cygwin=true ;; #(
112 | Darwin* ) darwin=true ;; #(
113 | MSYS* | MINGW* ) msys=true ;; #(
114 | NONSTOP* ) nonstop=true ;;
115 | esac
116 |
117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
118 |
119 |
120 | # Determine the Java command to use to start the JVM.
121 | if [ -n "$JAVA_HOME" ] ; then
122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
123 | # IBM's JDK on AIX uses strange locations for the executables
124 | JAVACMD=$JAVA_HOME/jre/sh/java
125 | else
126 | JAVACMD=$JAVA_HOME/bin/java
127 | fi
128 | if [ ! -x "$JAVACMD" ] ; then
129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
130 |
131 | Please set the JAVA_HOME variable in your environment to match the
132 | location of your Java installation."
133 | fi
134 | else
135 | JAVACMD=java
136 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
137 |
138 | Please set the JAVA_HOME variable in your environment to match the
139 | location of your Java installation."
140 | fi
141 |
142 | # Increase the maximum file descriptors if we can.
143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
144 | case $MAX_FD in #(
145 | max*)
146 | MAX_FD=$( ulimit -H -n ) ||
147 | warn "Could not query maximum file descriptor limit"
148 | esac
149 | case $MAX_FD in #(
150 | '' | soft) :;; #(
151 | *)
152 | ulimit -n "$MAX_FD" ||
153 | warn "Could not set maximum file descriptor limit to $MAX_FD"
154 | esac
155 | fi
156 |
157 | # Collect all arguments for the java command, stacking in reverse order:
158 | # * args from the command line
159 | # * the main class name
160 | # * -classpath
161 | # * -D...appname settings
162 | # * --module-path (only if needed)
163 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
164 |
165 | # For Cygwin or MSYS, switch paths to Windows format before running java
166 | if "$cygwin" || "$msys" ; then
167 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
168 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
169 |
170 | JAVACMD=$( cygpath --unix "$JAVACMD" )
171 |
172 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
173 | for arg do
174 | if
175 | case $arg in #(
176 | -*) false ;; # don't mess with options #(
177 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
178 | [ -e "$t" ] ;; #(
179 | *) false ;;
180 | esac
181 | then
182 | arg=$( cygpath --path --ignore --mixed "$arg" )
183 | fi
184 | # Roll the args list around exactly as many times as the number of
185 | # args, so each arg winds up back in the position where it started, but
186 | # possibly modified.
187 | #
188 | # NB: a `for` loop captures its iteration list before it begins, so
189 | # changing the positional parameters here affects neither the number of
190 | # iterations, nor the values presented in `arg`.
191 | shift # remove old arg
192 | set -- "$@" "$arg" # push replacement arg
193 | done
194 | fi
195 |
196 | # Collect all arguments for the java command;
197 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
198 | # shell script including quotes and variable substitutions, so put them in
199 | # double quotes to make sure that they get re-expanded; and
200 | # * put everything else in single quotes, so that it's not re-expanded.
201 |
202 | set -- \
203 | "-Dorg.gradle.appname=$APP_BASE_NAME" \
204 | -classpath "$CLASSPATH" \
205 | org.gradle.wrapper.GradleWrapperMain \
206 | "$@"
207 |
208 | # Use "xargs" to parse quoted args.
209 | #
210 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed.
211 | #
212 | # In Bash we could simply go:
213 | #
214 | # readarray ARGS < <( xargs -n1 <<<"$var" ) &&
215 | # set -- "${ARGS[@]}" "$@"
216 | #
217 | # but POSIX shell has neither arrays nor command substitution, so instead we
218 | # post-process each arg (as a line of input to sed) to backslash-escape any
219 | # character that might be a shell metacharacter, then use eval to reverse
220 | # that process (while maintaining the separation between arguments), and wrap
221 | # the whole thing up as a single "set" statement.
222 | #
223 | # This will of course break if any of these variables contains a newline or
224 | # an unmatched quote.
225 | #
226 |
227 | eval "set -- $(
228 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
229 | xargs -n1 |
230 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
231 | tr '\n' ' '
232 | )" '"$@"'
233 |
234 | exec "$JAVACMD" "$@"
235 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if "%ERRORLEVEL%"=="0" goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84 | exit /b 1
85 |
86 | :mainEnd
87 | if "%OS%"=="Windows_NT" endlocal
88 |
89 | :omega
90 |
--------------------------------------------------------------------------------
/notes-app/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'org.springframework.boot' version '3.3.5'
3 | id 'io.spring.dependency-management' version '1.1.7'
4 | id 'java'
5 | }
6 |
7 | dependencies {
8 | implementation 'org.springframework.boot:spring-boot-starter-web'
9 | implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
10 | implementation 'org.springframework.boot:spring-boot-starter-data-jdbc'
11 |
12 | runtimeOnly 'com.h2database:h2'
13 | compileOnly("org.springframework.boot:spring-boot-devtools")
14 |
15 | testImplementation 'org.springframework.boot:spring-boot-starter-test'
16 | testImplementation 'org.mockito:mockito-core'
17 | testImplementation 'org.assertj:assertj-core'
18 | }
19 |
20 | test {
21 | useJUnitPlatform()
22 | }
23 |
--------------------------------------------------------------------------------
/notes-app/src/main/java/io/sfe/notesapp/NotesApplication.java:
--------------------------------------------------------------------------------
1 | package io.sfe.notesapp;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | @SpringBootApplication
7 | public class NotesApplication {
8 |
9 | public static void main(String[] args) {
10 | SpringApplication.run(NotesApplication.class, args);
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/notes-app/src/main/java/io/sfe/notesapp/domain/note/Note.java:
--------------------------------------------------------------------------------
1 | package io.sfe.notesapp.domain.note;
2 |
3 | public record Note(int id, String text) {
4 | }
5 |
--------------------------------------------------------------------------------
/notes-app/src/main/java/io/sfe/notesapp/domain/note/NoteService.java:
--------------------------------------------------------------------------------
1 | package io.sfe.notesapp.domain.note;
2 |
3 | import io.sfe.notesapp.storage.note.NoteEntity;
4 | import io.sfe.notesapp.storage.note.NoteRepository;
5 | import org.springframework.stereotype.Service;
6 |
7 | import java.util.List;
8 | import java.util.stream.StreamSupport;
9 |
10 | @Service
11 | public class NoteService {
12 |
13 | private final NoteRepository noteRepository;
14 |
15 | public NoteService(NoteRepository noteRepository) {
16 | this.noteRepository = noteRepository;
17 | }
18 |
19 | public Note save(String noteText) {
20 | NoteEntity noteToSave = NoteEntity.of(noteText);
21 | NoteEntity savedNote = noteRepository.save(noteToSave);
22 |
23 | return new Note(savedNote.getId(), savedNote.getText());
24 | }
25 |
26 | public List findAll() {
27 | Iterable allNotes = noteRepository.findAll();
28 |
29 | return StreamSupport.stream(allNotes.spliterator(), false)
30 | .map(noteEntity -> new Note(noteEntity.getId(), noteEntity.getText()))
31 | .toList();
32 | }
33 |
34 | public Note findById(int id) {
35 | return noteRepository.findById(id)
36 | .map(noteEntity -> new Note(noteEntity.getId(), noteEntity.getText()))
37 | .orElseThrow();
38 | }
39 |
40 | public void delete(int id) {
41 | noteRepository.deleteById(id);
42 | }
43 |
44 | public Note updateNote(int noteId, String noteText) {
45 | var noteToUpdate = NoteEntity.of(noteText).withId(noteId);
46 |
47 | NoteEntity updatedNote = noteRepository.save(noteToUpdate);
48 |
49 | return new Note(updatedNote.getId(), updatedNote.getText());
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/notes-app/src/main/java/io/sfe/notesapp/storage/author/AuthorEntity.java:
--------------------------------------------------------------------------------
1 | package io.sfe.notesapp.storage.author;
2 |
3 | import org.springframework.data.annotation.Id;
4 | import org.springframework.data.relational.core.mapping.Table;
5 |
6 | import java.util.Objects;
7 |
8 | @Table("AUTHOR")
9 | public class AuthorEntity {
10 |
11 | @Id
12 | private final Integer id;
13 | private final String name;
14 |
15 | AuthorEntity(Integer id, String name) {
16 | this.id = id;
17 | this.name = name;
18 | }
19 |
20 | public AuthorEntity withId(Integer id) {
21 | return new AuthorEntity(id, this.name);
22 | }
23 |
24 | public static AuthorEntity of(String name) {
25 | return new AuthorEntity(null, name);
26 | }
27 |
28 | public Integer getId() {
29 | return id;
30 | }
31 |
32 | public String getName() {
33 | return name;
34 | }
35 |
36 | @Override
37 | public boolean equals(Object o) {
38 | if (this == o) return true;
39 | if (o == null || getClass() != o.getClass()) return false;
40 | AuthorEntity that = (AuthorEntity) o;
41 | return Objects.equals(id, that.id) && Objects.equals(name, that.name);
42 | }
43 |
44 | @Override
45 | public int hashCode() {
46 | return Objects.hash(id, name);
47 | }
48 |
49 | @Override
50 | public String toString() {
51 | return "AuthorEntity{" +
52 | "id=" + id +
53 | ", name='" + name + '\'' +
54 | '}';
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/notes-app/src/main/java/io/sfe/notesapp/storage/author/AuthorRepository.java:
--------------------------------------------------------------------------------
1 | package io.sfe.notesapp.storage.author;
2 |
3 | import org.springframework.data.repository.CrudRepository;
4 |
5 | public interface AuthorRepository extends CrudRepository {
6 | }
7 |
--------------------------------------------------------------------------------
/notes-app/src/main/java/io/sfe/notesapp/storage/note/NoteEntity.java:
--------------------------------------------------------------------------------
1 | package io.sfe.notesapp.storage.note;
2 |
3 | import org.springframework.data.annotation.Id;
4 | import org.springframework.data.relational.core.mapping.Table;
5 |
6 | import java.util.Objects;
7 |
8 | @Table("NOTE")
9 | public
10 | class NoteEntity {
11 |
12 | @Id
13 | private final Integer id;
14 | private final String text;
15 |
16 | public static NoteEntity of(String text) {
17 | return new NoteEntity(null, text);
18 | }
19 |
20 | NoteEntity(Integer id, String text) {
21 | this.id = id;
22 | this.text = text;
23 | }
24 |
25 | public Integer getId() {
26 | return id;
27 | }
28 |
29 | public String getText() {
30 | return text;
31 | }
32 |
33 | public NoteEntity withId(Integer id) {
34 | return new NoteEntity(id, this.text);
35 | }
36 |
37 | @Override
38 | public boolean equals(Object o) {
39 | if (this == o) return true;
40 | if (o == null || getClass() != o.getClass()) return false;
41 | NoteEntity that = (NoteEntity) o;
42 | return Objects.equals(id, that.id) && Objects.equals(text, that.text);
43 | }
44 |
45 | @Override
46 | public int hashCode() {
47 | return Objects.hash(id, text);
48 | }
49 |
50 | @Override
51 | public String toString() {
52 | return "NoteEntity{" +
53 | "id=" + id +
54 | ", text='" + text + '\'' +
55 | '}';
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/notes-app/src/main/java/io/sfe/notesapp/storage/note/NoteJdbcTemplateRepository.java:
--------------------------------------------------------------------------------
1 | package io.sfe.notesapp.storage.note;
2 |
3 | import org.springframework.jdbc.core.RowMapper;
4 | import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
5 | import org.springframework.jdbc.core.simple.SimpleJdbcInsert;
6 | import org.springframework.stereotype.Component;
7 |
8 | import java.util.List;
9 | import java.util.Map;
10 | import java.util.Optional;
11 |
12 | import static java.util.Collections.emptyMap;
13 | import static java.util.Map.entry;
14 |
15 | @Component
16 | class NoteJdbcTemplateRepository {
17 |
18 | private static final RowMapper NOTE_ENTITY_ROW_MAPPER = (rs, rowNum) -> {
19 | var id = rs.getInt("id");
20 | var text = rs.getString("text");
21 | return NoteEntity.of(text).withId(id);
22 | };
23 |
24 | private final NamedParameterJdbcTemplate namedJdbcTemplate;
25 |
26 | NoteJdbcTemplateRepository(NamedParameterJdbcTemplate namedJdbcTemplate) {
27 | this.namedJdbcTemplate = namedJdbcTemplate;
28 | }
29 |
30 | int save(String noteText) {
31 | return new SimpleJdbcInsert(namedJdbcTemplate.getJdbcTemplate())
32 | .withTableName("note")
33 | .usingGeneratedKeyColumns("id")
34 | .usingColumns("text")
35 | .executeAndReturnKey(Map.of("text", noteText))
36 | .intValue();
37 | }
38 |
39 | Optional findById(int id) {
40 | return namedJdbcTemplate.queryForStream(
41 | "SELECT id, text FROM note WHERE id = :id",
42 | Map.of("id", id),
43 | NOTE_ENTITY_ROW_MAPPER
44 | ).findFirst();
45 | }
46 |
47 | List findAll() {
48 | return namedJdbcTemplate.query(
49 | "SELECT id, text FROM note",
50 | emptyMap(),
51 | NOTE_ENTITY_ROW_MAPPER
52 | );
53 | }
54 |
55 | void delete(int id) {
56 | namedJdbcTemplate.update(
57 | "DELETE FROM note WHERE id = :id",
58 | Map.of("id", id)
59 | );
60 | }
61 |
62 | void updateNote(int noteId, String noteText) {
63 | namedJdbcTemplate.update(
64 | "UPDATE note SET text = :text WHERE id = :id",
65 | Map.ofEntries(
66 | entry("id", noteId),
67 | entry("text", noteText)
68 | )
69 | );
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/notes-app/src/main/java/io/sfe/notesapp/storage/note/NoteRepository.java:
--------------------------------------------------------------------------------
1 | package io.sfe.notesapp.storage.note;
2 |
3 | import org.springframework.data.repository.CrudRepository;
4 | import org.springframework.stereotype.Repository;
5 |
6 | @Repository
7 | public interface NoteRepository extends CrudRepository {
8 |
9 | }
10 |
--------------------------------------------------------------------------------
/notes-app/src/main/java/io/sfe/notesapp/web/IndexController.java:
--------------------------------------------------------------------------------
1 | package io.sfe.notesapp.web;
2 |
3 | import org.springframework.beans.factory.annotation.Value;
4 | import org.springframework.stereotype.Controller;
5 | import org.springframework.ui.Model;
6 | import org.springframework.web.bind.annotation.GetMapping;
7 |
8 | @Controller
9 | public class IndexController {
10 |
11 | private final String applicationName;
12 |
13 | public IndexController(@Value("${spring.application.name}") String applicationName) {
14 | this.applicationName = applicationName;
15 | }
16 |
17 | @GetMapping({"", "/", "/index"})
18 | public String index(Model model) {
19 | model.addAttribute("applicationName", applicationName);
20 | return "index";
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/notes-app/src/main/java/io/sfe/notesapp/web/common/ControllerExceptionHandler.java:
--------------------------------------------------------------------------------
1 | package io.sfe.notesapp.web.common;
2 |
3 | import org.slf4j.Logger;
4 | import org.slf4j.LoggerFactory;
5 | import org.springframework.http.HttpStatus;
6 | import org.springframework.http.ResponseEntity;
7 | import org.springframework.web.bind.annotation.ControllerAdvice;
8 | import org.springframework.web.bind.annotation.ExceptionHandler;
9 | import org.springframework.web.context.request.WebRequest;
10 |
11 | import java.time.LocalDateTime;
12 | import java.util.LinkedHashMap;
13 | import java.util.Map;
14 | import java.util.NoSuchElementException;
15 |
16 | @ControllerAdvice
17 | public class ControllerExceptionHandler {
18 |
19 | private static final Logger logger = LoggerFactory.getLogger(ControllerExceptionHandler.class);
20 |
21 | @ExceptionHandler(NoSuchElementException.class)
22 | public ResponseEntity