├── .gitignore ├── LICENSE ├── README.md ├── spring-boot-react-basic-auth-login-logout ├── all-code.md ├── backend-spring-boot-react-basic-auth-login-logout │ ├── pom.xml │ └── src │ │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── in28minutes │ │ │ │ └── fullstack │ │ │ │ └── springboot │ │ │ │ └── fullstack │ │ │ │ └── basic │ │ │ │ └── authentication │ │ │ │ └── springbootfullstackbasicauthloginlogout │ │ │ │ ├── SpringBootFullStackBasicAuthLoginLogoutApplication.java │ │ │ │ ├── basic │ │ │ │ └── auth │ │ │ │ │ ├── AuthenticationBean.java │ │ │ │ │ ├── BasicAuthenticationController.java │ │ │ │ │ └── SpringSecurityConfigurationBasicAuth.java │ │ │ │ └── course │ │ │ │ ├── Course.java │ │ │ │ ├── CourseResource.java │ │ │ │ └── CoursesHardcodedService.java │ │ └── resources │ │ │ └── application.properties │ │ └── test │ │ └── java │ │ └── com │ │ └── in28minutes │ │ └── fullstack │ │ └── springboot │ │ └── react │ │ └── basic │ │ └── authentication │ │ └── springbootreactbasicauthloginlogout │ │ └── SpringBootReactBasicAuthLoginLogoutApplicationTests.java └── frontend-spring-boot-react-basic-auth-login-logout │ ├── package-lock.json │ ├── package.json │ ├── public │ ├── favicon.ico │ ├── index.html │ └── manifest.json │ └── src │ ├── App.css │ ├── App.js │ ├── App.test.js │ ├── component │ ├── AuthenticatedRoute.jsx │ ├── InstructorApp.jsx │ ├── ListCoursesComponent.jsx │ ├── LoginComponent.jsx │ ├── LogoutComponent.jsx │ └── MenuComponent.jsx │ ├── index.css │ ├── index.js │ ├── logo.svg │ ├── service │ ├── AuthenticationService.js │ └── CourseDataService.js │ └── serviceWorker.js ├── spring-boot-react-cors-cross-origin-csrf ├── all-code.md ├── backend-spring-boot-react-cors-cross-origin-csrf │ ├── pom.xml │ └── src │ │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── in28minutes │ │ │ │ └── fullstack │ │ │ │ └── springboot │ │ │ │ └── rest │ │ │ │ └── api │ │ │ │ └── springbootcorscrossorigincsrf │ │ │ │ ├── SpringBootFullStackCorsCrossOriginCsrfApplication.java │ │ │ │ ├── basic │ │ │ │ └── auth │ │ │ │ │ └── SpringSecurityConfigurationBasicAuth.java │ │ │ │ └── course │ │ │ │ ├── Course.java │ │ │ │ ├── CourseResource.java │ │ │ │ └── CoursesHardcodedService.java │ │ └── resources │ │ │ └── application.properties │ │ └── test │ │ └── java │ │ └── com │ │ └── in28minutes │ │ └── fullstack │ │ └── springboot │ │ └── react │ │ └── rest │ │ └── api │ │ └── springbootreactcorscrossorigincsrf │ │ └── SpringBootReactCorsCrossOriginCsrfApplicationTests.java └── frontend-spring-boot-react-cors-cross-origin-csrf │ ├── package-lock.json │ ├── package.json │ ├── public │ ├── favicon.ico │ ├── index.html │ └── manifest.json │ └── src │ ├── App.css │ ├── App.js │ ├── App.test.js │ ├── component │ ├── ListCoursesComponent.jsx │ └── SecurityAppComponent.jsx │ ├── index.css │ ├── index.js │ ├── logo.svg │ ├── service │ └── CourseDataService.js │ └── serviceWorker.js ├── spring-boot-react-crud-full-stack-with-maven ├── all-code.md ├── backend-spring-boot-react-crud-full-stack-with-maven │ ├── pom.xml │ └── src │ │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── in28minutes │ │ │ │ └── fullstack │ │ │ │ └── springboot │ │ │ │ └── maven │ │ │ │ └── crud │ │ │ │ └── springbootcrudfullstackwithmaven │ │ │ │ ├── SpringBootFullStackCrudFullStackWithMavenApplication.java │ │ │ │ └── course │ │ │ │ ├── Course.java │ │ │ │ ├── CourseResource.java │ │ │ │ └── CoursesHardcodedService.java │ │ └── resources │ │ │ └── application.properties │ │ └── test │ │ └── java │ │ └── com │ │ └── in28minutes │ │ └── fullstack │ │ └── springboot │ │ └── react │ │ └── maven │ │ └── crud │ │ └── springbootreactcrudfullstackwithmaven │ │ └── SpringBootReactCrudFullStackWithMavenApplicationTests.java └── frontend-spring-boot-react-crud-full-stack-with-maven │ ├── package-lock.json │ ├── package.json │ ├── public │ ├── favicon.ico │ ├── index.html │ └── manifest.json │ └── src │ ├── App.css │ ├── App.js │ ├── App.test.js │ ├── component │ ├── CourseComponent.jsx │ ├── InstructorApp.jsx │ └── ListCoursesComponent.jsx │ ├── index.css │ ├── index.js │ ├── logo.svg │ ├── service │ └── CourseDataService.js │ └── serviceWorker.js ├── spring-boot-react-hello-world-with-routing ├── all-code.md ├── backend-spring-boot-react-hello-world-with-routing │ ├── pom.xml │ └── src │ │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── in28minutes │ │ │ │ └── fullstack │ │ │ │ └── springboot │ │ │ │ └── helloworld │ │ │ │ └── springboothelloworldwithrouting │ │ │ │ ├── SpringBootFullStackHelloWorldWithRoutingApplication.java │ │ │ │ └── helloworld │ │ │ │ ├── HelloWorldBean.java │ │ │ │ └── HelloWorldController.java │ │ └── resources │ │ │ └── application.properties │ │ └── test │ │ └── java │ │ └── com │ │ └── in28minutes │ │ └── fullstack │ │ └── springboot │ │ └── react │ │ └── helloworld │ │ └── springbootreacthelloworldwithrouting │ │ └── SpringBootReactHelloWorldWithRoutingApplicationTests.java └── frontend-spring-boot-react-hello-world-with-routing │ ├── package-lock.json │ ├── package.json │ ├── public │ ├── favicon.ico │ ├── index.html │ └── manifest.json │ └── src │ ├── App.css │ ├── App.js │ ├── App.test.js │ ├── component │ ├── HelloWorldApp.js │ ├── HelloWorldBeanComponent.js │ ├── HelloWorldStringComponent.js │ └── MenuComponent.js │ ├── index.css │ ├── index.js │ ├── logo.svg │ ├── service │ └── HelloWorldService.js │ └── serviceWorker.js ├── spring-boot-react-jpa-hibernate-with-h2-full-stack ├── all-code.md ├── backend-spring-boot-react-jpa-hibernate-with-h2-full-stack │ ├── pom.xml │ └── src │ │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── in28minutes │ │ │ │ └── fullstack │ │ │ │ └── springboot │ │ │ │ └── jpa │ │ │ │ └── hibernate │ │ │ │ └── springbootjpahibernatewithh2fullstack │ │ │ │ ├── SpringBootFullStackJpaHibernateWithH2FullStackApplication.java │ │ │ │ └── course │ │ │ │ ├── Course.java │ │ │ │ ├── CourseJpaRepository.java │ │ │ │ └── CourseResource.java │ │ └── resources │ │ │ ├── application.properties │ │ │ └── data.sql │ │ └── test │ │ └── java │ │ └── com │ │ └── in28minutes │ │ └── fullstack │ │ └── springboot │ │ └── react │ │ └── jpa │ │ └── hibernate │ │ └── springbootreactjpahibernatewithh2fullstack │ │ └── SpringBootReactJpaHibernateWithH2FullStackApplicationTests.java └── frontend-spring-boot-react-jpa-hibernate-with-h2-full-stack │ ├── package-lock.json │ ├── package.json │ ├── public │ ├── favicon.ico │ ├── index.html │ └── manifest.json │ └── src │ ├── App.css │ ├── App.js │ ├── App.test.js │ ├── index.css │ ├── index.js │ ├── logo.svg │ └── serviceWorker.js └── spring-boot-react-jwt-auth-login-logout ├── all-code.md ├── backend-spring-boot-react-jwt-auth-login-logout ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── in28minutes │ │ │ └── fullstack │ │ │ └── springboot │ │ │ └── jwt │ │ │ └── basic │ │ │ └── authentication │ │ │ └── springbootjwtauthloginlogout │ │ │ ├── SpringBootFullStackJwtAuthLoginLogoutApplication.java │ │ │ ├── course │ │ │ ├── Course.java │ │ │ ├── CourseResource.java │ │ │ └── CoursesHardcodedService.java │ │ │ └── jwt │ │ │ ├── AuthenticationException.java │ │ │ ├── JWTWebSecurityConfig.java │ │ │ ├── JwtAuthenticationRestController.java │ │ │ ├── JwtInMemoryUserDetailsService.java │ │ │ ├── JwtTokenAuthorizationOncePerRequestFilter.java │ │ │ ├── JwtTokenRequest.java │ │ │ ├── JwtTokenResponse.java │ │ │ ├── JwtTokenUtil.java │ │ │ ├── JwtUnAuthorizedResponseAuthenticationEntryPoint.java │ │ │ └── JwtUserDetails.java │ └── resources │ │ └── application.properties │ └── test │ └── java │ └── com │ └── in28minutes │ └── fullstack │ └── springboot │ └── jwt │ └── basic │ └── authentication │ └── springbootreactjwtauthloginlogout │ └── SpringBootReactJwtAuthLoginLogoutApplicationTests.java └── frontend-spring-boot-react-jwt-auth-login-logout └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # compiled output 2 | /dist 3 | /tmp 4 | /out-tsc 5 | 6 | # dependencies 7 | /node_modules 8 | 9 | # IDEs and editors 10 | /.idea 11 | .project 12 | .classpath 13 | .c9/ 14 | *.launch 15 | .settings/ 16 | *.sublime-workspace 17 | 18 | # IDE - VSCode 19 | .vscode/* 20 | !.vscode/settings.json 21 | !.vscode/tasks.json 22 | !.vscode/launch.json 23 | !.vscode/extensions.json 24 | 25 | # misc 26 | /.sass-cache 27 | /connect.lock 28 | /coverage 29 | /libpeerconnection.log 30 | npm-debug.log 31 | yarn-error.log 32 | testem.log 33 | /typings 34 | 35 | # System Files 36 | .DS_Store 37 | Thumbs.db 38 | 39 | # Compiled class file 40 | *.class 41 | 42 | # Log file 43 | *.log 44 | 45 | # BlueJ files 46 | *.ctxt 47 | 48 | # Mobile Tools for Java (J2ME) 49 | .mtj.tmp/ 50 | 51 | # Package Files # 52 | *.jar 53 | *.war 54 | *.ear 55 | *.tar.gz 56 | *.rar 57 | *.cmd 58 | *.classpath 59 | *.settings 60 | *.project 61 | *.mvn 62 | mvnw 63 | target 64 | *.DS_Store 65 | 66 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 67 | hs_err_pid* 68 | 69 | # Compiled class file 70 | *.class 71 | 72 | # Log file 73 | *.log 74 | 75 | # BlueJ files 76 | *.ctxt 77 | 78 | # Mobile Tools for Java (J2ME) 79 | .mtj.tmp/ 80 | 81 | # Package Files # 82 | *.jar 83 | *.war 84 | *.ear 85 | *.zip 86 | *.tar.gz 87 | *.rar 88 | *.cmd 89 | *.classpath 90 | *.settings 91 | *.project 92 | *.mvn 93 | mvnw 94 | target 95 | node_modules 96 | *.DS_Store 97 | 98 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 99 | hs_err_pid* 100 | bin -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 in28Minutes 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 | # Spring Boot React Full Stack Examples 2 | 3 | All full stack examples with Spring Boot and React for articles on our website http://www.springboottutorial.com 4 | 5 | [![Image](https://www.springboottutorial.com/images/Course-Go-Full-Stack-With-Spring-Boot-and-React.png "Go Full Stack with Spring Boot and React")](https://links.in28minutes.com/in28minutes-React) 6 | 7 | ## Learn from the Top 5 Best Selling Courses 8 | 9 | [![Image](https://www.springboottutorial.com/images/Course-Master-Microservices-with-Spring-Boot-and-Spring-Cloud.png "Master Microservices with Spring Boot and Spring Cloud")](https://links.in28minutes.com/in28minutes-Microservices) 10 | 11 | [![Image](https://www.springboottutorial.com/images/Course-Spring-Framework-Master-Class---Beginner-to-Expert.png "Spring Master Class - Beginner to Expert")](https://links.in28minutes.com/in28minutes-Spring) 12 | 13 | [![Image](https://www.springboottutorial.com/images/Course-DevOps.png "DevOps Course")](https://links.in28minutes.com/DevOps-SBT) 14 | [![Image](https://www.springboottutorial.com/images/Course-go-serverless.png "Go Serverless with AWS Lambda and Azure Functions")](https://links.in28minutes.com/serverless-sbt) 15 | 16 | [![Image](https://www.springboottutorial.com/images/Course-Go-Full-Stack-With-SpringBoot-And-Angular.png "Go Full Stack with Spring Boot and Angular")](https://links.in28minutes.com/in28minutes-angular) 17 | 18 | [![Image](https://www.springboottutorial.com/images/Course-Go-Full-Stack-With-Spring-Boot-and-React.png "Go Full Stack with Spring Boot and React")](https://links.in28minutes.com/in28minutes-React) 19 | 20 | 21 | ## Reskill with the Amazing in28Minutes Learning Paths 22 | 23 | - [Learning Path 01 - Spring and Spring Boot Web Applications and API Developer](https://links.in28minutes.com/in28minutes-LP-01) 24 | - [Learning Path 02 - Full Stack Developer with Spring Boot, React & Angular](https://links.in28minutes.com/in28minutes-LP-02) 25 | - [Learning Path 03 - Cloud Microservices Developer with Docker and Kubernetes](https://links.in28minutes.com/in28minutes-LP-03) 26 | - [Learning Path 04 - Learn Cloud with Spring Boot, AWS, Azure and PCF](https://links.in28minutes.com/in28minutes-LP-04) 27 | - [Learning Path 05 - Learn AWS with Microservices, Docker and Kubernetes](https://links.in28minutes.com/in28minutes-LP-05) 28 | 29 | ### Installing Eclipse & Embedded Maven 30 | - Installation Video : https://www.youtube.com/playlist?list=PLBBog2r6uMCSmMVTW_QmDLyASBvovyAO3 31 | - GIT Repository For Installation : https://github.com/in28minutes/getting-started-in-5-steps 32 | - PDF : https://github.com/in28minutes/SpringIn28Minutes/blob/master/InstallationGuide-JavaEclipseAndMaven_v2.pdf 33 | 34 | ### Running Examples 35 | - Download the zip or clone the Git repository. 36 | - Unzip the zip file (if you downloaded one) 37 | - Open Command Prompt and Change directory (cd) to folder containing pom.xml 38 | - Open Eclipse 39 | - File -> Import -> Existing Maven Project -> Navigate to the folder where you unzipped the zip 40 | - Select the right project 41 | - Choose the Spring Boot Application file (search for @SpringBootApplication) 42 | - Right Click on the file and Run as Java Application 43 | - You are all Set 44 | 45 | ### Troubleshooting 46 | - Refer our TroubleShooting Guide - http://www.in28minutes.com/spring-boot-maven-eclipse-troubleshooting-guide-and-faq 47 | 48 | ### Useful Links 49 | - Find out more about in28Minutes and our approach to creating great learning experience - The in28Minutes Way - http://www.in28minutes.com/the-in28minutes-way 50 | - Facebook : https://www.facebook.com/in28Minutes​ 51 | - Twitter : https://twitter.com/in28Minutes​ 52 | - YouTube : https://www.youtube.com/rithustutorials​ 53 | - Instagram : https://www.instagram.com/in28minutes/ -------------------------------------------------------------------------------- /spring-boot-react-basic-auth-login-logout/backend-spring-boot-react-basic-auth-login-logout/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | org.springframework.boot 8 | spring-boot-starter-parent 9 | 2.1.3.RELEASE 10 | 11 | 12 | com.in28minutes.fullstack.springboot.fullstack.basic.authentication 13 | spring-boot-fullstack-basic-auth-login-logout 14 | 0.0.1-SNAPSHOT 15 | spring-boot-fullstack-basic-auth-login-logout 16 | Demo project for Spring Boot 17 | 18 | 19 | 1.8 20 | 21 | 22 | 23 | 24 | org.springframework.boot 25 | spring-boot-starter-security 26 | 27 | 28 | org.springframework.boot 29 | spring-boot-starter-web 30 | 31 | 32 | 33 | org.springframework.boot 34 | spring-boot-devtools 35 | runtime 36 | 37 | 38 | org.springframework.boot 39 | spring-boot-starter-test 40 | test 41 | 42 | 43 | org.springframework.security 44 | spring-security-test 45 | test 46 | 47 | 48 | 49 | 50 | 51 | 52 | org.springframework.boot 53 | spring-boot-maven-plugin 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /spring-boot-react-basic-auth-login-logout/backend-spring-boot-react-basic-auth-login-logout/src/main/java/com/in28minutes/fullstack/springboot/fullstack/basic/authentication/springbootfullstackbasicauthloginlogout/SpringBootFullStackBasicAuthLoginLogoutApplication.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.fullstack.springboot.fullstack.basic.authentication.springbootfullstackbasicauthloginlogout; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class SpringBootFullStackBasicAuthLoginLogoutApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(SpringBootFullStackBasicAuthLoginLogoutApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /spring-boot-react-basic-auth-login-logout/backend-spring-boot-react-basic-auth-login-logout/src/main/java/com/in28minutes/fullstack/springboot/fullstack/basic/authentication/springbootfullstackbasicauthloginlogout/basic/auth/AuthenticationBean.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.fullstack.springboot.fullstack.basic.authentication.springbootfullstackbasicauthloginlogout.basic.auth; 2 | public class AuthenticationBean { 3 | 4 | private String message; 5 | 6 | public AuthenticationBean(String message) { 7 | this.message = message; 8 | } 9 | 10 | public String getMessage() { 11 | return message; 12 | } 13 | 14 | public void setMessage(String message) { 15 | this.message = message; 16 | } 17 | 18 | @Override 19 | public String toString() { 20 | return String.format("HelloWorldBean [message=%s]", message); 21 | } 22 | 23 | } -------------------------------------------------------------------------------- /spring-boot-react-basic-auth-login-logout/backend-spring-boot-react-basic-auth-login-logout/src/main/java/com/in28minutes/fullstack/springboot/fullstack/basic/authentication/springbootfullstackbasicauthloginlogout/basic/auth/BasicAuthenticationController.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.fullstack.springboot.fullstack.basic.authentication.springbootfullstackbasicauthloginlogout.basic.auth; 2 | import org.springframework.web.bind.annotation.CrossOrigin; 3 | import org.springframework.web.bind.annotation.GetMapping; 4 | import org.springframework.web.bind.annotation.RestController; 5 | 6 | //Controller 7 | @CrossOrigin(origins={ "http://localhost:3000", "http://localhost:4200" }) 8 | @RestController 9 | public class BasicAuthenticationController { 10 | 11 | @GetMapping(path = "/basicauth") 12 | public AuthenticationBean helloWorldBean() { 13 | //throw new RuntimeException("Some Error has Happened! Contact Support at ***-***"); 14 | return new AuthenticationBean("You are authenticated"); 15 | } 16 | } -------------------------------------------------------------------------------- /spring-boot-react-basic-auth-login-logout/backend-spring-boot-react-basic-auth-login-logout/src/main/java/com/in28minutes/fullstack/springboot/fullstack/basic/authentication/springbootfullstackbasicauthloginlogout/basic/auth/SpringSecurityConfigurationBasicAuth.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.fullstack.springboot.fullstack.basic.authentication.springbootfullstackbasicauthloginlogout.basic.auth; 2 | import org.springframework.context.annotation.Configuration; 3 | import org.springframework.http.HttpMethod; 4 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 5 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 6 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 7 | 8 | @Configuration 9 | @EnableWebSecurity 10 | public class SpringSecurityConfigurationBasicAuth extends WebSecurityConfigurerAdapter{ 11 | 12 | @Override 13 | protected void configure(HttpSecurity http) throws Exception { 14 | http 15 | .csrf().disable() 16 | .authorizeRequests() 17 | .antMatchers(HttpMethod.OPTIONS,"/**").permitAll() 18 | .anyRequest().authenticated() 19 | .and() 20 | //.formLogin().and() 21 | .httpBasic(); 22 | } 23 | } -------------------------------------------------------------------------------- /spring-boot-react-basic-auth-login-logout/backend-spring-boot-react-basic-auth-login-logout/src/main/java/com/in28minutes/fullstack/springboot/fullstack/basic/authentication/springbootfullstackbasicauthloginlogout/course/Course.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.fullstack.springboot.fullstack.basic.authentication.springbootfullstackbasicauthloginlogout.course; 2 | 3 | public class Course { 4 | private Long id; 5 | private String username; 6 | private String description; 7 | 8 | public Course() { 9 | 10 | } 11 | 12 | public Course(long id, String username, String description) { 13 | super(); 14 | this.id = id; 15 | this.username = username; 16 | this.description = description; 17 | } 18 | 19 | public Long getId() { 20 | return id; 21 | } 22 | 23 | public void setId(Long id) { 24 | this.id = id; 25 | } 26 | 27 | public String getUsername() { 28 | return username; 29 | } 30 | 31 | public void setUsername(String username) { 32 | this.username = username; 33 | } 34 | 35 | public String getDescription() { 36 | return description; 37 | } 38 | 39 | public void setDescription(String description) { 40 | this.description = description; 41 | } 42 | 43 | @Override 44 | public int hashCode() { 45 | final int prime = 31; 46 | int result = 1; 47 | result = prime * result + ((description == null) ? 0 : description.hashCode()); 48 | result = prime * result + ((id == null) ? 0 : id.hashCode()); 49 | result = prime * result + ((username == null) ? 0 : username.hashCode()); 50 | return result; 51 | } 52 | 53 | @Override 54 | public boolean equals(Object obj) { 55 | if (this == obj) 56 | return true; 57 | if (obj == null) 58 | return false; 59 | if (getClass() != obj.getClass()) 60 | return false; 61 | Course other = (Course) obj; 62 | if (description == null) { 63 | if (other.description != null) 64 | return false; 65 | } else if (!description.equals(other.description)) 66 | return false; 67 | if (id == null) { 68 | if (other.id != null) 69 | return false; 70 | } else if (!id.equals(other.id)) 71 | return false; 72 | if (username == null) { 73 | if (other.username != null) 74 | return false; 75 | } else if (!username.equals(other.username)) 76 | return false; 77 | return true; 78 | } 79 | 80 | } -------------------------------------------------------------------------------- /spring-boot-react-basic-auth-login-logout/backend-spring-boot-react-basic-auth-login-logout/src/main/java/com/in28minutes/fullstack/springboot/fullstack/basic/authentication/springbootfullstackbasicauthloginlogout/course/CourseResource.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.fullstack.springboot.fullstack.basic.authentication.springbootfullstackbasicauthloginlogout.course; 2 | 3 | import java.util.List; 4 | 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.web.bind.annotation.CrossOrigin; 7 | import org.springframework.web.bind.annotation.GetMapping; 8 | import org.springframework.web.bind.annotation.PathVariable; 9 | import org.springframework.web.bind.annotation.RestController; 10 | 11 | @CrossOrigin(origins = { "http://localhost:3000", "http://localhost:4200" }) 12 | @RestController 13 | public class CourseResource { 14 | 15 | @Autowired 16 | private CoursesHardcodedService courseManagementService; 17 | 18 | @GetMapping("/instructors/{username}/courses") 19 | public List getAllCourses(@PathVariable String username) { 20 | return courseManagementService.findAll(); 21 | } 22 | } -------------------------------------------------------------------------------- /spring-boot-react-basic-auth-login-logout/backend-spring-boot-react-basic-auth-login-logout/src/main/java/com/in28minutes/fullstack/springboot/fullstack/basic/authentication/springbootfullstackbasicauthloginlogout/course/CoursesHardcodedService.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.fullstack.springboot.fullstack.basic.authentication.springbootfullstackbasicauthloginlogout.course; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import org.springframework.stereotype.Service; 7 | 8 | @Service 9 | public class CoursesHardcodedService { 10 | 11 | private static List courses = new ArrayList<>(); 12 | private static long idCounter = 0; 13 | 14 | static { 15 | courses.add(new Course(++idCounter, "in28minutes", "Learn Full stack with Spring Boot and Angular")); 16 | courses.add(new Course(++idCounter, "in28minutes", "Learn Full stack with Spring Boot and React")); 17 | courses.add(new Course(++idCounter, "in28minutes", "Master Microservices with Spring Boot and Spring Cloud")); 18 | courses.add(new Course(++idCounter, "in28minutes", 19 | "Deploy Spring Boot Microservices to Cloud with Docker and Kubernetes")); 20 | } 21 | 22 | public List findAll() { 23 | return courses; 24 | } 25 | } -------------------------------------------------------------------------------- /spring-boot-react-basic-auth-login-logout/backend-spring-boot-react-basic-auth-login-logout/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.security.user.name=in28minutes 2 | spring.security.user.password=dummy -------------------------------------------------------------------------------- /spring-boot-react-basic-auth-login-logout/backend-spring-boot-react-basic-auth-login-logout/src/test/java/com/in28minutes/fullstack/springboot/react/basic/authentication/springbootreactbasicauthloginlogout/SpringBootReactBasicAuthLoginLogoutApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.fullstack.springboot.react.basic.authentication.springbootreactbasicauthloginlogout; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | @RunWith(SpringRunner.class) 9 | @SpringBootTest 10 | public class SpringBootReactBasicAuthLoginLogoutApplicationTests { 11 | 12 | @Test 13 | public void contextLoads() { 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /spring-boot-react-basic-auth-login-logout/frontend-spring-boot-react-basic-auth-login-logout/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "spring-boot-react-basic-auth-login-logout", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "axios": "^0.18.0", 7 | "react": "^16.8.5", 8 | "react-dom": "^16.8.5", 9 | "react-router-dom": "^5.0.0", 10 | "react-scripts": "2.1.8" 11 | }, 12 | "scripts": { 13 | "start": "react-scripts start", 14 | "build": "react-scripts build", 15 | "test": "react-scripts test", 16 | "eject": "react-scripts eject" 17 | }, 18 | "eslintConfig": { 19 | "extends": "react-app" 20 | }, 21 | "browserslist": [ 22 | ">0.2%", 23 | "not dead", 24 | "not ie <= 11", 25 | "not op_mini all" 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /spring-boot-react-basic-auth-login-logout/frontend-spring-boot-react-basic-auth-login-logout/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/spring-boot-react-fullstack-examples/99836f3c5c709bbc901a3c762399cded6371db47/spring-boot-react-basic-auth-login-logout/frontend-spring-boot-react-basic-auth-login-logout/public/favicon.ico -------------------------------------------------------------------------------- /spring-boot-react-basic-auth-login-logout/frontend-spring-boot-react-basic-auth-login-logout/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 13 | 14 | 23 | My Full Stack Application with Spring Boot and React 24 | 25 | 26 | 27 | 28 |
29 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /spring-boot-react-basic-auth-login-logout/frontend-spring-boot-react-basic-auth-login-logout/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /spring-boot-react-basic-auth-login-logout/frontend-spring-boot-react-basic-auth-login-logout/src/App.css: -------------------------------------------------------------------------------- 1 | @import url(https://unpkg.com/bootstrap@4.1.0/dist/css/bootstrap.min.css) -------------------------------------------------------------------------------- /spring-boot-react-basic-auth-login-logout/frontend-spring-boot-react-basic-auth-login-logout/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import './App.css'; 3 | import InstructorApp from './component/InstructorApp.jsx'; 4 | 5 | class App extends Component { 6 | render() { 7 | return ( 8 |
9 | 10 |
11 | ); 12 | } 13 | } 14 | 15 | export default App; -------------------------------------------------------------------------------- /spring-boot-react-basic-auth-login-logout/frontend-spring-boot-react-basic-auth-login-logout/src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | it('renders without crashing', () => { 6 | const div = document.createElement('div'); 7 | ReactDOM.render(, div); 8 | ReactDOM.unmountComponentAtNode(div); 9 | }); 10 | -------------------------------------------------------------------------------- /spring-boot-react-basic-auth-login-logout/frontend-spring-boot-react-basic-auth-login-logout/src/component/AuthenticatedRoute.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { Route, Redirect } from 'react-router-dom' 3 | import AuthenticationService from '../service/AuthenticationService'; 4 | 5 | class AuthenticatedRoute extends Component { 6 | render() { 7 | if (AuthenticationService.isUserLoggedIn()) { 8 | return 9 | } else { 10 | return 11 | } 12 | 13 | } 14 | } 15 | 16 | export default AuthenticatedRoute -------------------------------------------------------------------------------- /spring-boot-react-basic-auth-login-logout/frontend-spring-boot-react-basic-auth-login-logout/src/component/InstructorApp.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import ListCoursesComponent from './ListCoursesComponent'; 3 | import { BrowserRouter as Router, Route, Switch } from 'react-router-dom' 4 | import LoginComponent from './LoginComponent'; 5 | import LogoutComponent from './LogoutComponent'; 6 | import MenuComponent from './MenuComponent'; 7 | import AuthenticationService from '../service/AuthenticationService'; 8 | import AuthenticatedRoute from './AuthenticatedRoute'; 9 | 10 | class InstructorApp extends Component { 11 | 12 | 13 | render() { 14 | return ( 15 | <> 16 | 17 | <> 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | ) 29 | } 30 | } 31 | 32 | export default InstructorApp -------------------------------------------------------------------------------- /spring-boot-react-basic-auth-login-logout/frontend-spring-boot-react-basic-auth-login-logout/src/component/ListCoursesComponent.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import CourseDataService from '../service/CourseDataService.js'; 3 | 4 | const INSTRUCTOR = 'in28minutes' 5 | 6 | class ListCoursesComponent extends Component { 7 | constructor(props) { 8 | super(props) 9 | this.state = { 10 | courses: [], 11 | message: null 12 | } 13 | this.refreshCourses = this.refreshCourses.bind(this) 14 | } 15 | 16 | componentDidMount() { 17 | this.refreshCourses(); 18 | } 19 | 20 | refreshCourses() { 21 | CourseDataService.retrieveAllCourses(INSTRUCTOR)//HARDCODED 22 | .then( 23 | response => { 24 | this.setState({ courses: response.data }) 25 | } 26 | ) 27 | } 28 | 29 | 30 | render() { 31 | console.log('render') 32 | return ( 33 |
34 |

All Courses

35 |
36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | { 45 | this.state.courses.map( 46 | course => 47 | 48 | 49 | 50 | 51 | ) 52 | } 53 | 54 |
IdDescription
{course.id}{course.description}
55 |
56 |
57 | ) 58 | } 59 | } 60 | 61 | export default ListCoursesComponent 62 | -------------------------------------------------------------------------------- /spring-boot-react-basic-auth-login-logout/frontend-spring-boot-react-basic-auth-login-logout/src/component/LoginComponent.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import AuthenticationService from '../service/AuthenticationService'; 3 | 4 | class LoginComponent extends Component { 5 | 6 | constructor(props) { 7 | super(props) 8 | 9 | this.state = { 10 | username: 'in28minutes', 11 | password: '', 12 | hasLoginFailed: false, 13 | showSuccessMessage: false 14 | } 15 | 16 | this.handleChange = this.handleChange.bind(this) 17 | this.loginClicked = this.loginClicked.bind(this) 18 | } 19 | 20 | handleChange(event) { 21 | this.setState( 22 | { 23 | [event.target.name] 24 | : event.target.value 25 | } 26 | ) 27 | } 28 | 29 | loginClicked() { 30 | //in28minutes,dummy 31 | // if(this.state.username==='in28minutes' && this.state.password==='dummy'){ 32 | // AuthenticationService.registerSuccessfulLogin(this.state.username,this.state.password) 33 | // this.props.history.push(`/courses`) 34 | // //this.setState({showSuccessMessage:true}) 35 | // //this.setState({hasLoginFailed:false}) 36 | // } 37 | // else { 38 | // this.setState({showSuccessMessage:false}) 39 | // this.setState({hasLoginFailed:true}) 40 | // } 41 | 42 | AuthenticationService 43 | .executeBasicAuthenticationService(this.state.username, this.state.password) 44 | .then(() => { 45 | AuthenticationService.registerSuccessfulLogin(this.state.username, this.state.password) 46 | this.props.history.push(`/courses`) 47 | }).catch(() => { 48 | this.setState({ showSuccessMessage: false }) 49 | this.setState({ hasLoginFailed: true }) 50 | }) 51 | 52 | // AuthenticationService 53 | // .executeJwtAuthenticationService(this.state.username, this.state.password) 54 | // .then((response) => { 55 | // AuthenticationService.registerSuccessfulLoginForJwt(this.state.username, response.data.token) 56 | // this.props.history.push(`/courses`) 57 | // }).catch(() => { 58 | // this.setState({ showSuccessMessage: false }) 59 | // this.setState({ hasLoginFailed: true }) 60 | // }) 61 | 62 | } 63 | 64 | render() { 65 | return ( 66 |
67 |

Login

68 |
69 | {/**/} 70 | {this.state.hasLoginFailed &&
Invalid Credentials
} 71 | {this.state.showSuccessMessage &&
Login Sucessful
} 72 | {/**/} 73 | User Name: 74 | Password: 75 | 76 |
77 |
78 | ) 79 | } 80 | } 81 | 82 | export default LoginComponent -------------------------------------------------------------------------------- /spring-boot-react-basic-auth-login-logout/frontend-spring-boot-react-basic-auth-login-logout/src/component/LogoutComponent.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | 3 | class LogoutComponent extends Component { 4 | render() { 5 | return ( 6 | <> 7 |

You are logged out

8 |
9 | Thank You for Using Our Application. 10 |
11 | 12 | ) 13 | } 14 | } 15 | 16 | export default LogoutComponent -------------------------------------------------------------------------------- /spring-boot-react-basic-auth-login-logout/frontend-spring-boot-react-basic-auth-login-logout/src/component/MenuComponent.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { Link, withRouter } from 'react-router-dom' 3 | import AuthenticationService from '../service/AuthenticationService'; 4 | 5 | class MenuComponent extends Component { 6 | 7 | render() { 8 | const isUserLoggedIn = AuthenticationService.isUserLoggedIn(); 9 | 10 | return ( 11 |
12 | 22 |
23 | ) 24 | } 25 | } 26 | 27 | export default withRouter(MenuComponent) -------------------------------------------------------------------------------- /spring-boot-react-basic-auth-login-logout/frontend-spring-boot-react-basic-auth-login-logout/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", 5 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", 6 | sans-serif; 7 | -webkit-font-smoothing: antialiased; 8 | -moz-osx-font-smoothing: grayscale; 9 | } 10 | 11 | code { 12 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", 13 | monospace; 14 | } 15 | -------------------------------------------------------------------------------- /spring-boot-react-basic-auth-login-logout/frontend-spring-boot-react-basic-auth-login-logout/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | import * as serviceWorker from './serviceWorker'; 6 | 7 | ReactDOM.render(, document.getElementById('root')); 8 | 9 | // If you want your app to work offline and load faster, you can change 10 | // unregister() to register() below. Note this comes with some pitfalls. 11 | // Learn more about service workers: https://bit.ly/CRA-PWA 12 | serviceWorker.unregister(); 13 | -------------------------------------------------------------------------------- /spring-boot-react-basic-auth-login-logout/frontend-spring-boot-react-basic-auth-login-logout/src/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /spring-boot-react-basic-auth-login-logout/frontend-spring-boot-react-basic-auth-login-logout/src/service/AuthenticationService.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | 3 | const API_URL = 'http://localhost:8080' 4 | 5 | export const USER_NAME_SESSION_ATTRIBUTE_NAME = 'authenticatedUser' 6 | 7 | class AuthenticationService { 8 | 9 | executeBasicAuthenticationService(username, password) { 10 | return axios.get(`${API_URL}/basicauth`, 11 | { headers: { authorization: this.createBasicAuthToken(username, password) } }) 12 | } 13 | 14 | executeJwtAuthenticationService(username, password) { 15 | console.log(username); 16 | return axios.post(`${API_URL}/authenticate`, { 17 | username, 18 | password 19 | }) 20 | } 21 | 22 | createBasicAuthToken(username, password) { 23 | return 'Basic ' + window.btoa(username + ":" + password) 24 | } 25 | 26 | registerSuccessfulLogin(username, password) { 27 | //let basicAuthHeader = 'Basic ' + window.btoa(username + ":" + password) 28 | //console.log('registerSuccessfulLogin') 29 | sessionStorage.setItem(USER_NAME_SESSION_ATTRIBUTE_NAME, username) 30 | this.setupAxiosInterceptors(this.createBasicAuthToken(username, password)) 31 | } 32 | 33 | registerSuccessfulLoginForJwt(username, token) { 34 | sessionStorage.setItem(USER_NAME_SESSION_ATTRIBUTE_NAME, username) 35 | this.setupAxiosInterceptors(this.createJWTToken(token)) 36 | } 37 | 38 | createJWTToken(token) { 39 | return 'Bearer ' + token 40 | } 41 | 42 | 43 | logout() { 44 | sessionStorage.removeItem(USER_NAME_SESSION_ATTRIBUTE_NAME); 45 | } 46 | 47 | isUserLoggedIn() { 48 | let user = sessionStorage.getItem(USER_NAME_SESSION_ATTRIBUTE_NAME) 49 | if (user === null) return false 50 | return true 51 | } 52 | 53 | getLoggedInUserName() { 54 | let user = sessionStorage.getItem(USER_NAME_SESSION_ATTRIBUTE_NAME) 55 | if (user === null) return '' 56 | return user 57 | } 58 | 59 | setupAxiosInterceptors(token) { 60 | axios.interceptors.request.use( 61 | (config) => { 62 | if (this.isUserLoggedIn()) { 63 | config.headers.authorization = token 64 | } 65 | return config 66 | } 67 | ) 68 | } 69 | } 70 | 71 | export default new AuthenticationService() -------------------------------------------------------------------------------- /spring-boot-react-basic-auth-login-logout/frontend-spring-boot-react-basic-auth-login-logout/src/service/CourseDataService.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | 3 | const INSTRUCTOR = 'in28minutes' 4 | const PASSWORD = 'dummy' 5 | const COURSE_API_URL = 'http://localhost:8080' 6 | const INSTRUCTOR_API_URL = `${COURSE_API_URL}/instructors/${INSTRUCTOR}` 7 | 8 | class CourseDataService { 9 | 10 | retrieveAllCourses(name) { 11 | //console.log('executed service') 12 | return axios.get(`${INSTRUCTOR_API_URL}/courses`, 13 | //{ headers: { authorization: 'Basic ' + window.btoa(INSTRUCTOR + ":" + PASSWORD) } } 14 | ); 15 | } 16 | } 17 | 18 | export default new CourseDataService() 19 | -------------------------------------------------------------------------------- /spring-boot-react-basic-auth-login-logout/frontend-spring-boot-react-basic-auth-login-logout/src/serviceWorker.js: -------------------------------------------------------------------------------- 1 | // This optional code is used to register a service worker. 2 | // register() is not called by default. 3 | 4 | // This lets the app load faster on subsequent visits in production, and gives 5 | // it offline capabilities. However, it also means that developers (and users) 6 | // will only see deployed updates on subsequent visits to a page, after all the 7 | // existing tabs open on the page have been closed, since previously cached 8 | // resources are updated in the background. 9 | 10 | // To learn more about the benefits of this model and instructions on how to 11 | // opt-in, read https://bit.ly/CRA-PWA 12 | 13 | const isLocalhost = Boolean( 14 | window.location.hostname === 'localhost' || 15 | // [::1] is the IPv6 localhost address. 16 | window.location.hostname === '[::1]' || 17 | // 127.0.0.1/8 is considered localhost for IPv4. 18 | window.location.hostname.match( 19 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ 20 | ) 21 | ); 22 | 23 | export function register(config) { 24 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { 25 | // The URL constructor is available in all browsers that support SW. 26 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href); 27 | if (publicUrl.origin !== window.location.origin) { 28 | // Our service worker won't work if PUBLIC_URL is on a different origin 29 | // from what our page is served on. This might happen if a CDN is used to 30 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374 31 | return; 32 | } 33 | 34 | window.addEventListener('load', () => { 35 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; 36 | 37 | if (isLocalhost) { 38 | // This is running on localhost. Let's check if a service worker still exists or not. 39 | checkValidServiceWorker(swUrl, config); 40 | 41 | // Add some additional logging to localhost, pointing developers to the 42 | // service worker/PWA documentation. 43 | navigator.serviceWorker.ready.then(() => { 44 | console.log( 45 | 'This web app is being served cache-first by a service ' + 46 | 'worker. To learn more, visit https://bit.ly/CRA-PWA' 47 | ); 48 | }); 49 | } else { 50 | // Is not localhost. Just register service worker 51 | registerValidSW(swUrl, config); 52 | } 53 | }); 54 | } 55 | } 56 | 57 | function registerValidSW(swUrl, config) { 58 | navigator.serviceWorker 59 | .register(swUrl) 60 | .then(registration => { 61 | registration.onupdatefound = () => { 62 | const installingWorker = registration.installing; 63 | if (installingWorker == null) { 64 | return; 65 | } 66 | installingWorker.onstatechange = () => { 67 | if (installingWorker.state === 'installed') { 68 | if (navigator.serviceWorker.controller) { 69 | // At this point, the updated precached content has been fetched, 70 | // but the previous service worker will still serve the older 71 | // content until all client tabs are closed. 72 | console.log( 73 | 'New content is available and will be used when all ' + 74 | 'tabs for this page are closed. See https://bit.ly/CRA-PWA.' 75 | ); 76 | 77 | // Execute callback 78 | if (config && config.onUpdate) { 79 | config.onUpdate(registration); 80 | } 81 | } else { 82 | // At this point, everything has been precached. 83 | // It's the perfect time to display a 84 | // "Content is cached for offline use." message. 85 | console.log('Content is cached for offline use.'); 86 | 87 | // Execute callback 88 | if (config && config.onSuccess) { 89 | config.onSuccess(registration); 90 | } 91 | } 92 | } 93 | }; 94 | }; 95 | }) 96 | .catch(error => { 97 | console.error('Error during service worker registration:', error); 98 | }); 99 | } 100 | 101 | function checkValidServiceWorker(swUrl, config) { 102 | // Check if the service worker can be found. If it can't reload the page. 103 | fetch(swUrl) 104 | .then(response => { 105 | // Ensure service worker exists, and that we really are getting a JS file. 106 | const contentType = response.headers.get('content-type'); 107 | if ( 108 | response.status === 404 || 109 | (contentType != null && contentType.indexOf('javascript') === -1) 110 | ) { 111 | // No service worker found. Probably a different app. Reload the page. 112 | navigator.serviceWorker.ready.then(registration => { 113 | registration.unregister().then(() => { 114 | window.location.reload(); 115 | }); 116 | }); 117 | } else { 118 | // Service worker found. Proceed as normal. 119 | registerValidSW(swUrl, config); 120 | } 121 | }) 122 | .catch(() => { 123 | console.log( 124 | 'No internet connection found. App is running in offline mode.' 125 | ); 126 | }); 127 | } 128 | 129 | export function unregister() { 130 | if ('serviceWorker' in navigator) { 131 | navigator.serviceWorker.ready.then(registration => { 132 | registration.unregister(); 133 | }); 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /spring-boot-react-cors-cross-origin-csrf/backend-spring-boot-react-cors-cross-origin-csrf/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | org.springframework.boot 8 | spring-boot-starter-parent 9 | 2.1.3.RELEASE 10 | 11 | 12 | com.in28minutes.fullstack.springboot.react.rest.api 13 | spring-boot-fullstack-cors-cross-origin-csrf 14 | 0.0.1-SNAPSHOT 15 | spring-boot-fullstack-cors-cross-origin-csrf 16 | Demo project for Spring Boot 17 | 18 | 19 | 1.8 20 | 21 | 22 | 23 | 24 | org.springframework.boot 25 | spring-boot-starter-web 26 | 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-security 31 | 32 | 33 | 34 | org.springframework.boot 35 | spring-boot-devtools 36 | runtime 37 | 38 | 39 | org.springframework.boot 40 | spring-boot-starter-test 41 | test 42 | 43 | 44 | 45 | 46 | 47 | 48 | org.springframework.boot 49 | spring-boot-maven-plugin 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /spring-boot-react-cors-cross-origin-csrf/backend-spring-boot-react-cors-cross-origin-csrf/src/main/java/com/in28minutes/fullstack/springboot/rest/api/springbootcorscrossorigincsrf/SpringBootFullStackCorsCrossOriginCsrfApplication.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.fullstack.springboot.rest.api.springbootcorscrossorigincsrf; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class SpringBootFullStackCorsCrossOriginCsrfApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(SpringBootFullStackCorsCrossOriginCsrfApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /spring-boot-react-cors-cross-origin-csrf/backend-spring-boot-react-cors-cross-origin-csrf/src/main/java/com/in28minutes/fullstack/springboot/rest/api/springbootcorscrossorigincsrf/basic/auth/SpringSecurityConfigurationBasicAuth.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.fullstack.springboot.rest.api.springbootcorscrossorigincsrf.basic.auth; 2 | import org.springframework.context.annotation.Configuration; 3 | import org.springframework.http.HttpMethod; 4 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 5 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 6 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 7 | 8 | @Configuration 9 | @EnableWebSecurity 10 | public class SpringSecurityConfigurationBasicAuth extends WebSecurityConfigurerAdapter{ 11 | 12 | @Override 13 | protected void configure(HttpSecurity http) throws Exception { 14 | http 15 | .csrf().disable() 16 | .authorizeRequests() 17 | .antMatchers(HttpMethod.OPTIONS,"/**").permitAll() 18 | .anyRequest().authenticated() 19 | .and() 20 | //.formLogin().and() 21 | .httpBasic(); 22 | } 23 | } -------------------------------------------------------------------------------- /spring-boot-react-cors-cross-origin-csrf/backend-spring-boot-react-cors-cross-origin-csrf/src/main/java/com/in28minutes/fullstack/springboot/rest/api/springbootcorscrossorigincsrf/course/Course.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.fullstack.springboot.rest.api.springbootcorscrossorigincsrf.course; 2 | 3 | public class Course { 4 | private Long id; 5 | private String username; 6 | private String description; 7 | 8 | public Course() { 9 | 10 | } 11 | 12 | public Course(long id, String username, String description) { 13 | super(); 14 | this.id = id; 15 | this.username = username; 16 | this.description = description; 17 | } 18 | 19 | public Long getId() { 20 | return id; 21 | } 22 | 23 | public void setId(Long id) { 24 | this.id = id; 25 | } 26 | 27 | public String getUsername() { 28 | return username; 29 | } 30 | 31 | public void setUsername(String username) { 32 | this.username = username; 33 | } 34 | 35 | public String getDescription() { 36 | return description; 37 | } 38 | 39 | public void setDescription(String description) { 40 | this.description = description; 41 | } 42 | 43 | @Override 44 | public int hashCode() { 45 | final int prime = 31; 46 | int result = 1; 47 | result = prime * result + ((description == null) ? 0 : description.hashCode()); 48 | result = prime * result + ((id == null) ? 0 : id.hashCode()); 49 | result = prime * result + ((username == null) ? 0 : username.hashCode()); 50 | return result; 51 | } 52 | 53 | @Override 54 | public boolean equals(Object obj) { 55 | if (this == obj) 56 | return true; 57 | if (obj == null) 58 | return false; 59 | if (getClass() != obj.getClass()) 60 | return false; 61 | Course other = (Course) obj; 62 | if (description == null) { 63 | if (other.description != null) 64 | return false; 65 | } else if (!description.equals(other.description)) 66 | return false; 67 | if (id == null) { 68 | if (other.id != null) 69 | return false; 70 | } else if (!id.equals(other.id)) 71 | return false; 72 | if (username == null) { 73 | if (other.username != null) 74 | return false; 75 | } else if (!username.equals(other.username)) 76 | return false; 77 | return true; 78 | } 79 | 80 | } -------------------------------------------------------------------------------- /spring-boot-react-cors-cross-origin-csrf/backend-spring-boot-react-cors-cross-origin-csrf/src/main/java/com/in28minutes/fullstack/springboot/rest/api/springbootcorscrossorigincsrf/course/CourseResource.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.fullstack.springboot.rest.api.springbootcorscrossorigincsrf.course; 2 | 3 | import java.util.List; 4 | 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.web.bind.annotation.CrossOrigin; 7 | import org.springframework.web.bind.annotation.GetMapping; 8 | import org.springframework.web.bind.annotation.PathVariable; 9 | import org.springframework.web.bind.annotation.RestController; 10 | 11 | @CrossOrigin(origins = { "http://localhost:3000", "http://localhost:4200" }) 12 | @RestController 13 | public class CourseResource { 14 | 15 | @Autowired 16 | private CoursesHardcodedService courseManagementService; 17 | 18 | @GetMapping("/instructors/{username}/courses") 19 | public List getAllCourses(@PathVariable String username) { 20 | return courseManagementService.findAll(); 21 | } 22 | } -------------------------------------------------------------------------------- /spring-boot-react-cors-cross-origin-csrf/backend-spring-boot-react-cors-cross-origin-csrf/src/main/java/com/in28minutes/fullstack/springboot/rest/api/springbootcorscrossorigincsrf/course/CoursesHardcodedService.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.fullstack.springboot.rest.api.springbootcorscrossorigincsrf.course; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import org.springframework.stereotype.Service; 7 | 8 | @Service 9 | public class CoursesHardcodedService { 10 | 11 | private static List courses = new ArrayList<>(); 12 | private static long idCounter = 0; 13 | 14 | static { 15 | courses.add(new Course(++idCounter, "in28minutes", "Learn Full stack with Spring Boot and Angular")); 16 | courses.add(new Course(++idCounter, "in28minutes", "Learn Full stack with Spring Boot and React")); 17 | courses.add(new Course(++idCounter, "in28minutes", "Master Microservices with Spring Boot and Spring Cloud")); 18 | courses.add(new Course(++idCounter, "in28minutes", 19 | "Deploy Spring Boot Microservices to Cloud with Docker and Kubernetes")); 20 | } 21 | 22 | public List findAll() { 23 | return courses; 24 | } 25 | } -------------------------------------------------------------------------------- /spring-boot-react-cors-cross-origin-csrf/backend-spring-boot-react-cors-cross-origin-csrf/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.security.user.name=in28minutes 2 | spring.security.user.password=dummy 3 | -------------------------------------------------------------------------------- /spring-boot-react-cors-cross-origin-csrf/backend-spring-boot-react-cors-cross-origin-csrf/src/test/java/com/in28minutes/fullstack/springboot/react/rest/api/springbootreactcorscrossorigincsrf/SpringBootReactCorsCrossOriginCsrfApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.fullstack.springboot.react.rest.api.springbootreactcorscrossorigincsrf; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | @RunWith(SpringRunner.class) 9 | @SpringBootTest 10 | public class SpringBootReactCorsCrossOriginCsrfApplicationTests { 11 | 12 | @Test 13 | public void contextLoads() { 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /spring-boot-react-cors-cross-origin-csrf/frontend-spring-boot-react-cors-cross-origin-csrf/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "spring-boot-react-cors-cross-origin-csrf", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "axios": "^0.18.0", 7 | "react": "^16.8.5", 8 | "react-dom": "^16.8.5", 9 | "react-scripts": "2.1.8" 10 | }, 11 | "scripts": { 12 | "start": "react-scripts start", 13 | "build": "react-scripts build", 14 | "test": "react-scripts test", 15 | "eject": "react-scripts eject" 16 | }, 17 | "eslintConfig": { 18 | "extends": "react-app" 19 | }, 20 | "browserslist": [ 21 | ">0.2%", 22 | "not dead", 23 | "not ie <= 11", 24 | "not op_mini all" 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /spring-boot-react-cors-cross-origin-csrf/frontend-spring-boot-react-cors-cross-origin-csrf/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/spring-boot-react-fullstack-examples/99836f3c5c709bbc901a3c762399cded6371db47/spring-boot-react-cors-cross-origin-csrf/frontend-spring-boot-react-cors-cross-origin-csrf/public/favicon.ico -------------------------------------------------------------------------------- /spring-boot-react-cors-cross-origin-csrf/frontend-spring-boot-react-cors-cross-origin-csrf/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | 15 | 16 | 25 | React App 26 | 27 | 28 | 29 |
30 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /spring-boot-react-cors-cross-origin-csrf/frontend-spring-boot-react-cors-cross-origin-csrf/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /spring-boot-react-cors-cross-origin-csrf/frontend-spring-boot-react-cors-cross-origin-csrf/src/App.css: -------------------------------------------------------------------------------- 1 | @import url(https://unpkg.com/bootstrap@4.1.0/dist/css/bootstrap.min.css) -------------------------------------------------------------------------------- /spring-boot-react-cors-cross-origin-csrf/frontend-spring-boot-react-cors-cross-origin-csrf/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import './App.css'; 3 | import SecurityApp from './component/SecurityAppComponent'; 4 | 5 | class App extends Component { 6 | render() { 7 | return ( 8 |
9 | 10 |
11 | ); 12 | } 13 | } 14 | 15 | export default App; 16 | -------------------------------------------------------------------------------- /spring-boot-react-cors-cross-origin-csrf/frontend-spring-boot-react-cors-cross-origin-csrf/src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | it('renders without crashing', () => { 6 | const div = document.createElement('div'); 7 | ReactDOM.render(, div); 8 | ReactDOM.unmountComponentAtNode(div); 9 | }); 10 | -------------------------------------------------------------------------------- /spring-boot-react-cors-cross-origin-csrf/frontend-spring-boot-react-cors-cross-origin-csrf/src/component/ListCoursesComponent.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import CourseDataService from '../service/CourseDataService.js'; 3 | 4 | const INSTRUCTOR = 'in28minutes' 5 | 6 | class ListCoursesComponent extends Component { 7 | constructor(props) { 8 | super(props) 9 | this.state = { 10 | courses: [], 11 | message: null 12 | } 13 | this.refreshCourses = this.refreshCourses.bind(this) 14 | } 15 | 16 | componentDidMount() { 17 | this.refreshCourses(); 18 | } 19 | 20 | refreshCourses() { 21 | CourseDataService.retrieveAllCourses(INSTRUCTOR)//HARDCODED 22 | .then( 23 | response => { 24 | this.setState({ courses: response.data }) 25 | } 26 | ) 27 | } 28 | 29 | 30 | render() { 31 | return ( 32 |
33 |

All Courses

34 |
35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | { 44 | this.state.courses.map( 45 | course => 46 | 47 | 48 | 49 | 50 | ) 51 | } 52 | 53 |
IdDescription
{course.id}{course.description}
54 |
55 |
56 | ) 57 | } 58 | } 59 | 60 | export default ListCoursesComponent -------------------------------------------------------------------------------- /spring-boot-react-cors-cross-origin-csrf/frontend-spring-boot-react-cors-cross-origin-csrf/src/component/SecurityAppComponent.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import ListCoursesComponent from './ListCoursesComponent'; 3 | 4 | class SecurityApp extends Component { 5 | 6 | render() { 7 | return ( 8 | <> 9 |

CORS (CROSS ORIGIN) - PRE FLIGHT Requests

10 |
11 | 12 |
13 | 14 | ); 15 | } 16 | } 17 | 18 | export default SecurityApp -------------------------------------------------------------------------------- /spring-boot-react-cors-cross-origin-csrf/frontend-spring-boot-react-cors-cross-origin-csrf/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", 5 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", 6 | sans-serif; 7 | -webkit-font-smoothing: antialiased; 8 | -moz-osx-font-smoothing: grayscale; 9 | } 10 | 11 | code { 12 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", 13 | monospace; 14 | } 15 | -------------------------------------------------------------------------------- /spring-boot-react-cors-cross-origin-csrf/frontend-spring-boot-react-cors-cross-origin-csrf/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | import * as serviceWorker from './serviceWorker'; 6 | 7 | ReactDOM.render(, document.getElementById('root')); 8 | 9 | // If you want your app to work offline and load faster, you can change 10 | // unregister() to register() below. Note this comes with some pitfalls. 11 | // Learn more about service workers: https://bit.ly/CRA-PWA 12 | serviceWorker.unregister(); 13 | -------------------------------------------------------------------------------- /spring-boot-react-cors-cross-origin-csrf/frontend-spring-boot-react-cors-cross-origin-csrf/src/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /spring-boot-react-cors-cross-origin-csrf/frontend-spring-boot-react-cors-cross-origin-csrf/src/service/CourseDataService.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | 3 | const INSTRUCTOR = 'in28minutes' 4 | const PASSWORD = 'dummy' 5 | const COURSE_API_URL = 'http://localhost:8080' 6 | const INSTRUCTOR_API_URL = `${COURSE_API_URL}/instructors/${INSTRUCTOR}` 7 | 8 | class CourseDataService { 9 | 10 | retrieveAllCourses(name) { 11 | return axios.get(`${INSTRUCTOR_API_URL}/courses`, 12 | { headers: { authorization: 'Basic ' + window.btoa(INSTRUCTOR + ":" + PASSWORD) } } 13 | ); 14 | } 15 | } 16 | 17 | export default new CourseDataService() -------------------------------------------------------------------------------- /spring-boot-react-cors-cross-origin-csrf/frontend-spring-boot-react-cors-cross-origin-csrf/src/serviceWorker.js: -------------------------------------------------------------------------------- 1 | // This optional code is used to register a service worker. 2 | // register() is not called by default. 3 | 4 | // This lets the app load faster on subsequent visits in production, and gives 5 | // it offline capabilities. However, it also means that developers (and users) 6 | // will only see deployed updates on subsequent visits to a page, after all the 7 | // existing tabs open on the page have been closed, since previously cached 8 | // resources are updated in the background. 9 | 10 | // To learn more about the benefits of this model and instructions on how to 11 | // opt-in, read https://bit.ly/CRA-PWA 12 | 13 | const isLocalhost = Boolean( 14 | window.location.hostname === 'localhost' || 15 | // [::1] is the IPv6 localhost address. 16 | window.location.hostname === '[::1]' || 17 | // 127.0.0.1/8 is considered localhost for IPv4. 18 | window.location.hostname.match( 19 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ 20 | ) 21 | ); 22 | 23 | export function register(config) { 24 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { 25 | // The URL constructor is available in all browsers that support SW. 26 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href); 27 | if (publicUrl.origin !== window.location.origin) { 28 | // Our service worker won't work if PUBLIC_URL is on a different origin 29 | // from what our page is served on. This might happen if a CDN is used to 30 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374 31 | return; 32 | } 33 | 34 | window.addEventListener('load', () => { 35 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; 36 | 37 | if (isLocalhost) { 38 | // This is running on localhost. Let's check if a service worker still exists or not. 39 | checkValidServiceWorker(swUrl, config); 40 | 41 | // Add some additional logging to localhost, pointing developers to the 42 | // service worker/PWA documentation. 43 | navigator.serviceWorker.ready.then(() => { 44 | console.log( 45 | 'This web app is being served cache-first by a service ' + 46 | 'worker. To learn more, visit https://bit.ly/CRA-PWA' 47 | ); 48 | }); 49 | } else { 50 | // Is not localhost. Just register service worker 51 | registerValidSW(swUrl, config); 52 | } 53 | }); 54 | } 55 | } 56 | 57 | function registerValidSW(swUrl, config) { 58 | navigator.serviceWorker 59 | .register(swUrl) 60 | .then(registration => { 61 | registration.onupdatefound = () => { 62 | const installingWorker = registration.installing; 63 | if (installingWorker == null) { 64 | return; 65 | } 66 | installingWorker.onstatechange = () => { 67 | if (installingWorker.state === 'installed') { 68 | if (navigator.serviceWorker.controller) { 69 | // At this point, the updated precached content has been fetched, 70 | // but the previous service worker will still serve the older 71 | // content until all client tabs are closed. 72 | console.log( 73 | 'New content is available and will be used when all ' + 74 | 'tabs for this page are closed. See https://bit.ly/CRA-PWA.' 75 | ); 76 | 77 | // Execute callback 78 | if (config && config.onUpdate) { 79 | config.onUpdate(registration); 80 | } 81 | } else { 82 | // At this point, everything has been precached. 83 | // It's the perfect time to display a 84 | // "Content is cached for offline use." message. 85 | console.log('Content is cached for offline use.'); 86 | 87 | // Execute callback 88 | if (config && config.onSuccess) { 89 | config.onSuccess(registration); 90 | } 91 | } 92 | } 93 | }; 94 | }; 95 | }) 96 | .catch(error => { 97 | console.error('Error during service worker registration:', error); 98 | }); 99 | } 100 | 101 | function checkValidServiceWorker(swUrl, config) { 102 | // Check if the service worker can be found. If it can't reload the page. 103 | fetch(swUrl) 104 | .then(response => { 105 | // Ensure service worker exists, and that we really are getting a JS file. 106 | const contentType = response.headers.get('content-type'); 107 | if ( 108 | response.status === 404 || 109 | (contentType != null && contentType.indexOf('javascript') === -1) 110 | ) { 111 | // No service worker found. Probably a different app. Reload the page. 112 | navigator.serviceWorker.ready.then(registration => { 113 | registration.unregister().then(() => { 114 | window.location.reload(); 115 | }); 116 | }); 117 | } else { 118 | // Service worker found. Proceed as normal. 119 | registerValidSW(swUrl, config); 120 | } 121 | }) 122 | .catch(() => { 123 | console.log( 124 | 'No internet connection found. App is running in offline mode.' 125 | ); 126 | }); 127 | } 128 | 129 | export function unregister() { 130 | if ('serviceWorker' in navigator) { 131 | navigator.serviceWorker.ready.then(registration => { 132 | registration.unregister(); 133 | }); 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /spring-boot-react-crud-full-stack-with-maven/backend-spring-boot-react-crud-full-stack-with-maven/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | org.springframework.boot 8 | spring-boot-starter-parent 9 | 2.1.3.RELEASE 10 | 11 | 12 | com.in28minutes.fullstack.springboot.maven.crud 13 | spring-boot-fullstack-crud-full-stack-with-maven 14 | 0.0.1-SNAPSHOT 15 | spring-boot-fullstack-crud-full-stack-with-maven 16 | Demo project for Spring Boot 17 | 18 | 19 | 1.8 20 | 21 | 22 | 23 | 24 | org.springframework.boot 25 | spring-boot-starter-web 26 | 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-devtools 31 | runtime 32 | 33 | 34 | org.springframework.boot 35 | spring-boot-starter-test 36 | test 37 | 38 | 39 | 40 | 41 | 42 | 43 | org.springframework.boot 44 | spring-boot-maven-plugin 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /spring-boot-react-crud-full-stack-with-maven/backend-spring-boot-react-crud-full-stack-with-maven/src/main/java/com/in28minutes/fullstack/springboot/maven/crud/springbootcrudfullstackwithmaven/SpringBootFullStackCrudFullStackWithMavenApplication.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.fullstack.springboot.maven.crud.springbootcrudfullstackwithmaven; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class SpringBootFullStackCrudFullStackWithMavenApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(SpringBootFullStackCrudFullStackWithMavenApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /spring-boot-react-crud-full-stack-with-maven/backend-spring-boot-react-crud-full-stack-with-maven/src/main/java/com/in28minutes/fullstack/springboot/maven/crud/springbootcrudfullstackwithmaven/course/Course.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.fullstack.springboot.maven.crud.springbootcrudfullstackwithmaven.course; 2 | 3 | public class Course { 4 | private Long id; 5 | private String username; 6 | private String description; 7 | 8 | public Course() { 9 | 10 | } 11 | 12 | public Course(long id, String username, String description) { 13 | super(); 14 | this.id = id; 15 | this.username = username; 16 | this.description = description; 17 | } 18 | 19 | public Long getId() { 20 | return id; 21 | } 22 | 23 | public void setId(Long id) { 24 | this.id = id; 25 | } 26 | 27 | public String getUsername() { 28 | return username; 29 | } 30 | 31 | public void setUsername(String username) { 32 | this.username = username; 33 | } 34 | 35 | public String getDescription() { 36 | return description; 37 | } 38 | 39 | public void setDescription(String description) { 40 | this.description = description; 41 | } 42 | 43 | @Override 44 | public int hashCode() { 45 | final int prime = 31; 46 | int result = 1; 47 | result = prime * result + ((description == null) ? 0 : description.hashCode()); 48 | result = prime * result + ((id == null) ? 0 : id.hashCode()); 49 | result = prime * result + ((username == null) ? 0 : username.hashCode()); 50 | return result; 51 | } 52 | 53 | @Override 54 | public boolean equals(Object obj) { 55 | if (this == obj) 56 | return true; 57 | if (obj == null) 58 | return false; 59 | if (getClass() != obj.getClass()) 60 | return false; 61 | Course other = (Course) obj; 62 | if (description == null) { 63 | if (other.description != null) 64 | return false; 65 | } else if (!description.equals(other.description)) 66 | return false; 67 | if (id == null) { 68 | if (other.id != null) 69 | return false; 70 | } else if (!id.equals(other.id)) 71 | return false; 72 | if (username == null) { 73 | if (other.username != null) 74 | return false; 75 | } else if (!username.equals(other.username)) 76 | return false; 77 | return true; 78 | } 79 | 80 | } -------------------------------------------------------------------------------- /spring-boot-react-crud-full-stack-with-maven/backend-spring-boot-react-crud-full-stack-with-maven/src/main/java/com/in28minutes/fullstack/springboot/maven/crud/springbootcrudfullstackwithmaven/course/CourseResource.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.fullstack.springboot.maven.crud.springbootcrudfullstackwithmaven.course; 2 | 3 | import java.net.URI; 4 | import java.util.List; 5 | 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.http.HttpStatus; 8 | import org.springframework.http.ResponseEntity; 9 | import org.springframework.web.bind.annotation.CrossOrigin; 10 | import org.springframework.web.bind.annotation.DeleteMapping; 11 | import org.springframework.web.bind.annotation.GetMapping; 12 | import org.springframework.web.bind.annotation.PathVariable; 13 | import org.springframework.web.bind.annotation.PostMapping; 14 | import org.springframework.web.bind.annotation.PutMapping; 15 | import org.springframework.web.bind.annotation.RequestBody; 16 | import org.springframework.web.bind.annotation.RestController; 17 | import org.springframework.web.servlet.support.ServletUriComponentsBuilder; 18 | 19 | @CrossOrigin(origins = { "http://localhost:3000", "http://localhost:4200" }) 20 | @RestController 21 | public class CourseResource { 22 | 23 | @Autowired 24 | private CoursesHardcodedService courseManagementService; 25 | 26 | @GetMapping("/instructors/{username}/courses") 27 | public List getAllCourses(@PathVariable String username) { 28 | return courseManagementService.findAll(); 29 | } 30 | 31 | @GetMapping("/instructors/{username}/courses/{id}") 32 | public Course getCourse(@PathVariable String username, @PathVariable long id) { 33 | return courseManagementService.findById(id); 34 | } 35 | 36 | @DeleteMapping("/instructors/{username}/courses/{id}") 37 | public ResponseEntity deleteCourse(@PathVariable String username, @PathVariable long id) { 38 | 39 | Course course = courseManagementService.deleteById(id); 40 | 41 | if (course != null) { 42 | return ResponseEntity.noContent().build(); 43 | } 44 | 45 | return ResponseEntity.notFound().build(); 46 | } 47 | 48 | @PutMapping("/instructors/{username}/courses/{id}") 49 | public ResponseEntity updateCourse(@PathVariable String username, @PathVariable long id, 50 | @RequestBody Course course) { 51 | 52 | Course courseUpdated = courseManagementService.save(course); 53 | 54 | return new ResponseEntity(course, HttpStatus.OK); 55 | } 56 | 57 | @PostMapping("/instructors/{username}/courses") 58 | public ResponseEntity createCourse(@PathVariable String username, @RequestBody Course course) { 59 | 60 | Course createdCourse = courseManagementService.save(course); 61 | 62 | // Location 63 | // Get current resource url 64 | /// {id} 65 | URI uri = ServletUriComponentsBuilder.fromCurrentRequest().path("/{id}").buildAndExpand(createdCourse.getId()) 66 | .toUri(); 67 | 68 | return ResponseEntity.created(uri).build(); 69 | } 70 | 71 | } -------------------------------------------------------------------------------- /spring-boot-react-crud-full-stack-with-maven/backend-spring-boot-react-crud-full-stack-with-maven/src/main/java/com/in28minutes/fullstack/springboot/maven/crud/springbootcrudfullstackwithmaven/course/CoursesHardcodedService.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.fullstack.springboot.maven.crud.springbootcrudfullstackwithmaven.course; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import org.springframework.stereotype.Service; 7 | 8 | @Service 9 | public class CoursesHardcodedService { 10 | 11 | private static List courses = new ArrayList<>(); 12 | private static long idCounter = 0; 13 | 14 | static { 15 | courses.add(new Course(++idCounter, "in28minutes", "Learn Full stack with Spring Boot and Angular")); 16 | courses.add(new Course(++idCounter, "in28minutes", "Learn Full stack with Spring Boot and React")); 17 | courses.add(new Course(++idCounter, "in28minutes", "Master Microservices with Spring Boot and Spring Cloud")); 18 | courses.add(new Course(++idCounter, "in28minutes", 19 | "Deploy Spring Boot Microservices to Cloud with Docker and Kubernetes")); 20 | } 21 | 22 | public List findAll() { 23 | return courses; 24 | } 25 | 26 | public Course save(Course course) { 27 | if (course.getId() == -1 || course.getId() == 0) { 28 | course.setId(++idCounter); 29 | courses.add(course); 30 | } else { 31 | deleteById(course.getId()); 32 | courses.add(course); 33 | } 34 | return course; 35 | } 36 | 37 | public Course deleteById(long id) { 38 | Course course = findById(id); 39 | 40 | if (course == null) 41 | return null; 42 | 43 | if (courses.remove(course)) { 44 | return course; 45 | } 46 | 47 | return null; 48 | } 49 | 50 | public Course findById(long id) { 51 | for (Course course : courses) { 52 | if (course.getId() == id) { 53 | return course; 54 | } 55 | } 56 | 57 | return null; 58 | } 59 | } -------------------------------------------------------------------------------- /spring-boot-react-crud-full-stack-with-maven/backend-spring-boot-react-crud-full-stack-with-maven/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /spring-boot-react-crud-full-stack-with-maven/backend-spring-boot-react-crud-full-stack-with-maven/src/test/java/com/in28minutes/fullstack/springboot/react/maven/crud/springbootreactcrudfullstackwithmaven/SpringBootReactCrudFullStackWithMavenApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.fullstack.springboot.react.maven.crud.springbootreactcrudfullstackwithmaven; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | @RunWith(SpringRunner.class) 9 | @SpringBootTest 10 | public class SpringBootReactCrudFullStackWithMavenApplicationTests { 11 | 12 | @Test 13 | public void contextLoads() { 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /spring-boot-react-crud-full-stack-with-maven/frontend-spring-boot-react-crud-full-stack-with-maven/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "spring-boot-react-crud-full-stack-with-maven", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "axios": "^0.18.0", 7 | "formik": "^1.5.1", 8 | "react": "^16.8.5", 9 | "react-dom": "^16.8.5", 10 | "react-router-dom": "^5.0.0", 11 | "react-scripts": "2.1.8" 12 | }, 13 | "scripts": { 14 | "start": "react-scripts start", 15 | "build": "react-scripts build", 16 | "test": "react-scripts test", 17 | "eject": "react-scripts eject" 18 | }, 19 | "eslintConfig": { 20 | "extends": "react-app" 21 | }, 22 | "browserslist": [ 23 | ">0.2%", 24 | "not dead", 25 | "not ie <= 11", 26 | "not op_mini all" 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /spring-boot-react-crud-full-stack-with-maven/frontend-spring-boot-react-crud-full-stack-with-maven/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/spring-boot-react-fullstack-examples/99836f3c5c709bbc901a3c762399cded6371db47/spring-boot-react-crud-full-stack-with-maven/frontend-spring-boot-react-crud-full-stack-with-maven/public/favicon.ico -------------------------------------------------------------------------------- /spring-boot-react-crud-full-stack-with-maven/frontend-spring-boot-react-crud-full-stack-with-maven/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 13 | 14 | 23 | My Full Stack Application with Spring Boot and React 24 | 25 | 26 | 27 | 28 |
29 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /spring-boot-react-crud-full-stack-with-maven/frontend-spring-boot-react-crud-full-stack-with-maven/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /spring-boot-react-crud-full-stack-with-maven/frontend-spring-boot-react-crud-full-stack-with-maven/src/App.css: -------------------------------------------------------------------------------- 1 | @import url(https://unpkg.com/bootstrap@4.1.0/dist/css/bootstrap.min.css) -------------------------------------------------------------------------------- /spring-boot-react-crud-full-stack-with-maven/frontend-spring-boot-react-crud-full-stack-with-maven/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import './App.css'; 3 | import InstructorApp from './component/InstructorApp'; 4 | 5 | class App extends Component { 6 | render() { 7 | return ( 8 |
9 | 10 |
11 | ); 12 | } 13 | } 14 | 15 | export default App; 16 | -------------------------------------------------------------------------------- /spring-boot-react-crud-full-stack-with-maven/frontend-spring-boot-react-crud-full-stack-with-maven/src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | it('renders without crashing', () => { 6 | const div = document.createElement('div'); 7 | ReactDOM.render(, div); 8 | ReactDOM.unmountComponentAtNode(div); 9 | }); 10 | -------------------------------------------------------------------------------- /spring-boot-react-crud-full-stack-with-maven/frontend-spring-boot-react-crud-full-stack-with-maven/src/component/CourseComponent.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { Formik, Form, Field, ErrorMessage } from 'formik'; 3 | import CourseDataService from '../service/CourseDataService'; 4 | 5 | const INSTRUCTOR = 'in28minutes' 6 | 7 | class CourseComponent extends Component { 8 | constructor(props) { 9 | super(props) 10 | 11 | this.state = { 12 | id: this.props.match.params.id, 13 | description: '' 14 | } 15 | 16 | this.onSubmit = this.onSubmit.bind(this) 17 | this.validate = this.validate.bind(this) 18 | 19 | } 20 | 21 | componentDidMount() { 22 | 23 | console.log(this.state.id) 24 | 25 | // eslint-disable-next-line 26 | if (this.state.id == -1) { 27 | return 28 | } 29 | 30 | CourseDataService.retrieveCourse(INSTRUCTOR, this.state.id) 31 | .then(response => this.setState({ 32 | description: response.data.description 33 | })) 34 | } 35 | 36 | validate(values) { 37 | let errors = {} 38 | if (!values.description) { 39 | errors.description = 'Enter a Description' 40 | } else if (values.description.length < 5) { 41 | errors.description = 'Enter atleast 5 Characters in Description' 42 | } 43 | 44 | return errors 45 | 46 | } 47 | 48 | onSubmit(values) { 49 | let username = INSTRUCTOR 50 | 51 | let course = { 52 | id: this.state.id, 53 | description: values.description, 54 | targetDate: values.targetDate 55 | } 56 | 57 | if (this.state.id === -1) { 58 | CourseDataService.createCourse(username, course) 59 | .then(() => this.props.history.push('/courses')) 60 | } else { 61 | CourseDataService.updateCourse(username, this.state.id, course) 62 | .then(() => this.props.history.push('/courses')) 63 | } 64 | 65 | console.log(values); 66 | } 67 | 68 | render() { 69 | 70 | let { description, id } = this.state 71 | 72 | return ( 73 |
74 |

Course

75 |
76 | 84 | { 85 | (props) => ( 86 |
87 | 89 |
90 | 91 | 92 |
93 |
94 | 95 | 96 |
97 | 98 | 99 | ) 100 | } 101 |
102 | 103 |
104 |
105 | ) 106 | } 107 | } 108 | 109 | export default CourseComponent -------------------------------------------------------------------------------- /spring-boot-react-crud-full-stack-with-maven/frontend-spring-boot-react-crud-full-stack-with-maven/src/component/InstructorApp.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import ListCoursesComponent from './ListCoursesComponent'; 3 | import { BrowserRouter as Router, Route, Switch } from 'react-router-dom' 4 | import CourseComponent from './CourseComponent'; 5 | 6 | class InstructorApp extends Component { 7 | render() { 8 | return ( 9 | 10 | <> 11 |

Instructor Application

12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | ) 20 | } 21 | } 22 | 23 | export default InstructorApp -------------------------------------------------------------------------------- /spring-boot-react-crud-full-stack-with-maven/frontend-spring-boot-react-crud-full-stack-with-maven/src/component/ListCoursesComponent.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import CourseDataService from '../service/CourseDataService'; 3 | 4 | const INSTRUCTOR = 'in28minutes' 5 | 6 | class ListCoursesComponent extends Component { 7 | constructor(props) { 8 | super(props) 9 | this.state = { 10 | courses: [], 11 | message: null 12 | } 13 | this.deleteCourseClicked = this.deleteCourseClicked.bind(this) 14 | this.updateCourseClicked = this.updateCourseClicked.bind(this) 15 | this.addCourseClicked = this.addCourseClicked.bind(this) 16 | this.refreshCourses = this.refreshCourses.bind(this) 17 | } 18 | 19 | componentDidMount() { 20 | this.refreshCourses(); 21 | } 22 | 23 | refreshCourses() { 24 | CourseDataService.retrieveAllCourses(INSTRUCTOR)//HARDCODED 25 | .then( 26 | response => { 27 | //console.log(response); 28 | this.setState({ courses: response.data }) 29 | } 30 | ) 31 | } 32 | 33 | deleteCourseClicked(id) { 34 | CourseDataService.deleteCourse(INSTRUCTOR, id) 35 | .then( 36 | response => { 37 | this.setState({ message: `Delete of course ${id} Successful` }) 38 | this.refreshCourses() 39 | } 40 | ) 41 | 42 | } 43 | 44 | addCourseClicked() { 45 | this.props.history.push(`/courses/-1`) 46 | } 47 | 48 | updateCourseClicked(id) { 49 | console.log('update ' + id) 50 | this.props.history.push(`/courses/${id}`) 51 | } 52 | 53 | render() { 54 | console.log('render') 55 | return ( 56 |
57 |

All Courses

58 | {this.state.message &&
{this.state.message}
} 59 |
60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | { 71 | this.state.courses.map( 72 | course => 73 | 74 | 75 | 76 | 77 | 78 | 79 | ) 80 | } 81 | 82 |
IdDescriptionUpdateDelete
{course.id}{course.description}
83 |
84 | 85 |
86 |
87 |
88 | ) 89 | } 90 | } 91 | 92 | export default ListCoursesComponent -------------------------------------------------------------------------------- /spring-boot-react-crud-full-stack-with-maven/frontend-spring-boot-react-crud-full-stack-with-maven/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", 5 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", 6 | sans-serif; 7 | -webkit-font-smoothing: antialiased; 8 | -moz-osx-font-smoothing: grayscale; 9 | } 10 | 11 | code { 12 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", 13 | monospace; 14 | } 15 | -------------------------------------------------------------------------------- /spring-boot-react-crud-full-stack-with-maven/frontend-spring-boot-react-crud-full-stack-with-maven/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | import * as serviceWorker from './serviceWorker'; 6 | 7 | ReactDOM.render(, document.getElementById('root')); 8 | 9 | // If you want your app to work offline and load faster, you can change 10 | // unregister() to register() below. Note this comes with some pitfalls. 11 | // Learn more about service workers: https://bit.ly/CRA-PWA 12 | serviceWorker.unregister(); 13 | -------------------------------------------------------------------------------- /spring-boot-react-crud-full-stack-with-maven/frontend-spring-boot-react-crud-full-stack-with-maven/src/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /spring-boot-react-crud-full-stack-with-maven/frontend-spring-boot-react-crud-full-stack-with-maven/src/service/CourseDataService.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | 3 | const INSTRUCTOR = 'in28minutes' 4 | const COURSE_API_URL = 'http://localhost:8080' 5 | const INSTRUCTOR_API_URL = `${COURSE_API_URL}/instructors/${INSTRUCTOR}` 6 | 7 | class CourseDataService { 8 | 9 | retrieveAllCourses(name) { 10 | //console.log('executed service') 11 | return axios.get(`${INSTRUCTOR_API_URL}/courses`); 12 | } 13 | 14 | retrieveCourse(name, id) { 15 | //console.log('executed service') 16 | return axios.get(`${INSTRUCTOR_API_URL}/courses/${id}`); 17 | } 18 | 19 | deleteCourse(name, id) { 20 | //console.log('executed service') 21 | return axios.delete(`${INSTRUCTOR_API_URL}/courses/${id}`); 22 | } 23 | 24 | updateCourse(name, id, course) { 25 | //console.log('executed service') 26 | return axios.put(`${INSTRUCTOR_API_URL}/courses/${id}`, course); 27 | } 28 | 29 | createCourse(name, course) { 30 | //console.log('executed service') 31 | return axios.post(`${INSTRUCTOR_API_URL}/courses/`, course); 32 | } 33 | } 34 | 35 | export default new CourseDataService() -------------------------------------------------------------------------------- /spring-boot-react-crud-full-stack-with-maven/frontend-spring-boot-react-crud-full-stack-with-maven/src/serviceWorker.js: -------------------------------------------------------------------------------- 1 | // This optional code is used to register a service worker. 2 | // register() is not called by default. 3 | 4 | // This lets the app load faster on subsequent visits in production, and gives 5 | // it offline capabilities. However, it also means that developers (and users) 6 | // will only see deployed updates on subsequent visits to a page, after all the 7 | // existing tabs open on the page have been closed, since previously cached 8 | // resources are updated in the background. 9 | 10 | // To learn more about the benefits of this model and instructions on how to 11 | // opt-in, read https://bit.ly/CRA-PWA 12 | 13 | const isLocalhost = Boolean( 14 | window.location.hostname === 'localhost' || 15 | // [::1] is the IPv6 localhost address. 16 | window.location.hostname === '[::1]' || 17 | // 127.0.0.1/8 is considered localhost for IPv4. 18 | window.location.hostname.match( 19 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ 20 | ) 21 | ); 22 | 23 | export function register(config) { 24 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { 25 | // The URL constructor is available in all browsers that support SW. 26 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href); 27 | if (publicUrl.origin !== window.location.origin) { 28 | // Our service worker won't work if PUBLIC_URL is on a different origin 29 | // from what our page is served on. This might happen if a CDN is used to 30 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374 31 | return; 32 | } 33 | 34 | window.addEventListener('load', () => { 35 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; 36 | 37 | if (isLocalhost) { 38 | // This is running on localhost. Let's check if a service worker still exists or not. 39 | checkValidServiceWorker(swUrl, config); 40 | 41 | // Add some additional logging to localhost, pointing developers to the 42 | // service worker/PWA documentation. 43 | navigator.serviceWorker.ready.then(() => { 44 | console.log( 45 | 'This web app is being served cache-first by a service ' + 46 | 'worker. To learn more, visit https://bit.ly/CRA-PWA' 47 | ); 48 | }); 49 | } else { 50 | // Is not localhost. Just register service worker 51 | registerValidSW(swUrl, config); 52 | } 53 | }); 54 | } 55 | } 56 | 57 | function registerValidSW(swUrl, config) { 58 | navigator.serviceWorker 59 | .register(swUrl) 60 | .then(registration => { 61 | registration.onupdatefound = () => { 62 | const installingWorker = registration.installing; 63 | if (installingWorker == null) { 64 | return; 65 | } 66 | installingWorker.onstatechange = () => { 67 | if (installingWorker.state === 'installed') { 68 | if (navigator.serviceWorker.controller) { 69 | // At this point, the updated precached content has been fetched, 70 | // but the previous service worker will still serve the older 71 | // content until all client tabs are closed. 72 | console.log( 73 | 'New content is available and will be used when all ' + 74 | 'tabs for this page are closed. See https://bit.ly/CRA-PWA.' 75 | ); 76 | 77 | // Execute callback 78 | if (config && config.onUpdate) { 79 | config.onUpdate(registration); 80 | } 81 | } else { 82 | // At this point, everything has been precached. 83 | // It's the perfect time to display a 84 | // "Content is cached for offline use." message. 85 | console.log('Content is cached for offline use.'); 86 | 87 | // Execute callback 88 | if (config && config.onSuccess) { 89 | config.onSuccess(registration); 90 | } 91 | } 92 | } 93 | }; 94 | }; 95 | }) 96 | .catch(error => { 97 | console.error('Error during service worker registration:', error); 98 | }); 99 | } 100 | 101 | function checkValidServiceWorker(swUrl, config) { 102 | // Check if the service worker can be found. If it can't reload the page. 103 | fetch(swUrl) 104 | .then(response => { 105 | // Ensure service worker exists, and that we really are getting a JS file. 106 | const contentType = response.headers.get('content-type'); 107 | if ( 108 | response.status === 404 || 109 | (contentType != null && contentType.indexOf('javascript') === -1) 110 | ) { 111 | // No service worker found. Probably a different app. Reload the page. 112 | navigator.serviceWorker.ready.then(registration => { 113 | registration.unregister().then(() => { 114 | window.location.reload(); 115 | }); 116 | }); 117 | } else { 118 | // Service worker found. Proceed as normal. 119 | registerValidSW(swUrl, config); 120 | } 121 | }) 122 | .catch(() => { 123 | console.log( 124 | 'No internet connection found. App is running in offline mode.' 125 | ); 126 | }); 127 | } 128 | 129 | export function unregister() { 130 | if ('serviceWorker' in navigator) { 131 | navigator.serviceWorker.ready.then(registration => { 132 | registration.unregister(); 133 | }); 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /spring-boot-react-hello-world-with-routing/all-code.md: -------------------------------------------------------------------------------- 1 | 4 | 5 | ## Complete Code Example 6 | 7 | 8 | ### /frontend-spring-boot-react-hello-world-with-routing/public/index.html 9 | 10 | ```html 11 | 12 | 13 | 14 | 15 | 16 | 20 | 21 | 25 | 26 | 35 | React App 36 | 37 | 38 | 39 |
40 | 50 | 51 | 52 | ``` 53 | --- 54 | 55 | ### /frontend-spring-boot-react-hello-world-with-routing/public/manifest.json 56 | 57 | ```json 58 | { 59 | "short_name": "React App", 60 | "name": "Create React App Sample", 61 | "icons": [ 62 | { 63 | "src": "favicon.ico", 64 | "sizes": "64x64 32x32 24x24 16x16", 65 | "type": "image/x-icon" 66 | } 67 | ], 68 | "start_url": ".", 69 | "display": "standalone", 70 | "theme_color": "#000000", 71 | "background_color": "#ffffff" 72 | } 73 | ``` 74 | --- 75 | 76 | ### /frontend-spring-boot-react-hello-world-with-routing/src/App.css 77 | 78 | ```css 79 | @import url(https://unpkg.com/bootstrap@4.1.0/dist/css/bootstrap.min.css) 80 | ``` 81 | --- 82 | 83 | ### /frontend-spring-boot-react-hello-world-with-routing/src/index.js 84 | 85 | ```js 86 | import React from 'react'; 87 | import ReactDOM from 'react-dom'; 88 | import './index.css'; 89 | import App from './App'; 90 | import * as serviceWorker from './serviceWorker'; 91 | 92 | ReactDOM.render(, document.getElementById('root')); 93 | 94 | // If you want your app to work offline and load faster, you can change 95 | // unregister() to register() below. Note this comes with some pitfalls. 96 | // Learn more about service workers: https://bit.ly/CRA-PWA 97 | serviceWorker.unregister(); 98 | ``` 99 | --- 100 | 101 | ### /frontend-spring-boot-react-hello-world-with-routing/src/component/MenuComponent.js 102 | 103 | ```js 104 | import React, { Component } from 'react'; 105 | import { Link } from 'react-router-dom' 106 | 107 | class MenuComponent extends Component { 108 | componentDidMount() { 109 | 110 | } 111 | render() { 112 | return ( 113 |
114 | 121 |
122 | ) 123 | 124 | } 125 | 126 | } 127 | 128 | export default MenuComponent 129 | ``` 130 | --- 131 | 132 | ### /frontend-spring-boot-react-hello-world-with-routing/src/component/HelloWorldBeanComponent.js 133 | 134 | ```js 135 | import React, { Component } from 'react'; 136 | import HelloWorldService from '../service/HelloWorldService'; 137 | 138 | class HelloWorldBeanComponent extends Component { 139 | constructor(props) { 140 | super(props) 141 | this.state = { 142 | welcomeMessage: '' 143 | } 144 | } 145 | 146 | componentDidMount() { 147 | HelloWorldService.executeHelloWorldBeanService() 148 | .then(response => this.setState({ welcomeMessage: response.data.message })) 149 | .catch(this.setState({ welcomeMessage: 'Error Processing Request' })) 150 | } 151 | 152 | render() { 153 | return (<> 154 |

Hello World String Component

155 |
156 | {this.state.welcomeMessage} 157 |
158 |
159 | 160 |
161 | 162 | ) 163 | } 164 | 165 | gotoStringComponent = () => { 166 | this.props.history.push('/hello-world-string') 167 | } 168 | } 169 | 170 | export default HelloWorldBeanComponent 171 | ``` 172 | --- 173 | 174 | ### /frontend-spring-boot-react-hello-world-with-routing/src/component/HelloWorldApp.js 175 | 176 | ```js 177 | import React, { Component } from 'react'; 178 | import { BrowserRouter as Router, Route, Switch } from 'react-router-dom' 179 | import MenuComponent from './MenuComponent' 180 | import HelloWorldBeanComponent from './HelloWorldBeanComponent'; 181 | import HelloWorldStringComponent from './HelloWorldStringComponent'; 182 | 183 | class HelloWorldApp extends Component { 184 | render() { 185 | return ( 186 | <> 187 | 188 | <> 189 | 190 |
191 | 192 | 193 | 194 | 195 | 196 |
197 | 198 |
199 | 200 | ) 201 | } 202 | } 203 | 204 | export default HelloWorldApp; 205 | ``` 206 | --- 207 | 208 | ### /frontend-spring-boot-react-hello-world-with-routing/src/component/HelloWorldStringComponent.js 209 | 210 | ```js 211 | import React, { Component } from 'react'; 212 | import HelloWorldService from '../service/HelloWorldService'; 213 | 214 | class HelloWorldStringComponent extends Component { 215 | constructor(props) { 216 | super(props) 217 | this.state = { 218 | welcomeMessage: '' 219 | } 220 | } 221 | 222 | componentDidMount() { 223 | HelloWorldService.executeHelloWorldService() 224 | .then(response => this.setState({ welcomeMessage: response.data })) 225 | .catch(this.setState({ welcomeMessage: 'Error Processing Request' })) 226 | } 227 | 228 | render() { 229 | return (<> 230 |

Hello World String Component

231 |
232 | {this.state.welcomeMessage} 233 |
234 |
235 | 236 |
237 | 238 | ) 239 | } 240 | 241 | gotoBeanComponent = () => { 242 | this.props.history.push('/hello-world-bean') 243 | } 244 | } 245 | 246 | export default HelloWorldStringComponent 247 | ``` 248 | --- 249 | 250 | ### /frontend-spring-boot-react-hello-world-with-routing/src/index.css 251 | 252 | ```css 253 | body { 254 | margin: 0; 255 | padding: 0; 256 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", 257 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", 258 | sans-serif; 259 | -webkit-font-smoothing: antialiased; 260 | -moz-osx-font-smoothing: grayscale; 261 | } 262 | 263 | code { 264 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", 265 | monospace; 266 | } 267 | ``` 268 | --- 269 | 270 | ### /frontend-spring-boot-react-hello-world-with-routing/src/App.test.js 271 | 272 | ```js 273 | import React from 'react'; 274 | import ReactDOM from 'react-dom'; 275 | import App from './App'; 276 | 277 | it('renders without crashing', () => { 278 | const div = document.createElement('div'); 279 | ReactDOM.render(, div); 280 | ReactDOM.unmountComponentAtNode(div); 281 | }); 282 | ``` 283 | --- 284 | 285 | ### /frontend-spring-boot-react-hello-world-with-routing/src/serviceWorker.js 286 | 287 | ```js 288 | // This optional code is used to register a service worker. 289 | // register() is not called by default. 290 | 291 | // This lets the app load faster on subsequent visits in production, and gives 292 | // it offline capabilities. However, it also means that developers (and users) 293 | // will only see deployed updates on subsequent visits to a page, after all the 294 | // existing tabs open on the page have been closed, since previously cached 295 | // resources are updated in the background. 296 | 297 | // To learn more about the benefits of this model and instructions on how to 298 | // opt-in, read https://bit.ly/CRA-PWA 299 | 300 | const isLocalhost = Boolean( 301 | window.location.hostname === 'localhost' || 302 | // [::1] is the IPv6 localhost address. 303 | window.location.hostname === '[::1]' || 304 | // 127.0.0.1/8 is considered localhost for IPv4. 305 | window.location.hostname.match( 306 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ 307 | ) 308 | ); 309 | 310 | export function register(config) { 311 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { 312 | // The URL constructor is available in all browsers that support SW. 313 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href); 314 | if (publicUrl.origin !== window.location.origin) { 315 | // Our service worker won't work if PUBLIC_URL is on a different origin 316 | // from what our page is served on. This might happen if a CDN is used to 317 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374 318 | return; 319 | } 320 | 321 | window.addEventListener('load', () => { 322 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; 323 | 324 | if (isLocalhost) { 325 | // This is running on localhost. Let's check if a service worker still exists or not. 326 | checkValidServiceWorker(swUrl, config); 327 | 328 | // Add some additional logging to localhost, pointing developers to the 329 | // service worker/PWA documentation. 330 | navigator.serviceWorker.ready.then(() => { 331 | console.log( 332 | 'This web app is being served cache-first by a service ' + 333 | 'worker. To learn more, visit https://bit.ly/CRA-PWA' 334 | ); 335 | }); 336 | } else { 337 | // Is not localhost. Just register service worker 338 | registerValidSW(swUrl, config); 339 | } 340 | }); 341 | } 342 | } 343 | 344 | function registerValidSW(swUrl, config) { 345 | navigator.serviceWorker 346 | .register(swUrl) 347 | .then(registration => { 348 | registration.onupdatefound = () => { 349 | const installingWorker = registration.installing; 350 | if (installingWorker == null) { 351 | return; 352 | } 353 | installingWorker.onstatechange = () => { 354 | if (installingWorker.state === 'installed') { 355 | if (navigator.serviceWorker.controller) { 356 | // At this point, the updated precached content has been fetched, 357 | // but the previous service worker will still serve the older 358 | // content until all client tabs are closed. 359 | console.log( 360 | 'New content is available and will be used when all ' + 361 | 'tabs for this page are closed. See https://bit.ly/CRA-PWA.' 362 | ); 363 | 364 | // Execute callback 365 | if (config && config.onUpdate) { 366 | config.onUpdate(registration); 367 | } 368 | } else { 369 | // At this point, everything has been precached. 370 | // It's the perfect time to display a 371 | // "Content is cached for offline use." message. 372 | console.log('Content is cached for offline use.'); 373 | 374 | // Execute callback 375 | if (config && config.onSuccess) { 376 | config.onSuccess(registration); 377 | } 378 | } 379 | } 380 | }; 381 | }; 382 | }) 383 | .catch(error => { 384 | console.error('Error during service worker registration:', error); 385 | }); 386 | } 387 | 388 | function checkValidServiceWorker(swUrl, config) { 389 | // Check if the service worker can be found. If it can't reload the page. 390 | fetch(swUrl) 391 | .then(response => { 392 | // Ensure service worker exists, and that we really are getting a JS file. 393 | const contentType = response.headers.get('content-type'); 394 | if ( 395 | response.status === 404 || 396 | (contentType != null && contentType.indexOf('javascript') === -1) 397 | ) { 398 | // No service worker found. Probably a different app. Reload the page. 399 | navigator.serviceWorker.ready.then(registration => { 400 | registration.unregister().then(() => { 401 | window.location.reload(); 402 | }); 403 | }); 404 | } else { 405 | // Service worker found. Proceed as normal. 406 | registerValidSW(swUrl, config); 407 | } 408 | }) 409 | .catch(() => { 410 | console.log( 411 | 'No internet connection found. App is running in offline mode.' 412 | ); 413 | }); 414 | } 415 | 416 | export function unregister() { 417 | if ('serviceWorker' in navigator) { 418 | navigator.serviceWorker.ready.then(registration => { 419 | registration.unregister(); 420 | }); 421 | } 422 | } 423 | ``` 424 | --- 425 | 426 | ### /frontend-spring-boot-react-hello-world-with-routing/src/service/HelloWorldService.js 427 | 428 | ```js 429 | import axios from 'axios' 430 | 431 | class HelloWorldService { 432 | 433 | executeHelloWorldService() { 434 | return axios.get('http://localhost:8080/hello-world'); 435 | } 436 | 437 | executeHelloWorldBeanService() { 438 | return axios.get('http://localhost:8080/hello-world-bean'); 439 | } 440 | 441 | executeHelloWorldPathVariableService(name) { 442 | return axios.get(`http://localhost:8080/hello-world/path-variable/${name}`); 443 | } 444 | 445 | } 446 | 447 | export default new HelloWorldService() 448 | ``` 449 | --- 450 | 451 | ### /frontend-spring-boot-react-hello-world-with-routing/src/logo.svg 452 | 453 | ``` 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | ``` 462 | --- 463 | 464 | ### /frontend-spring-boot-react-hello-world-with-routing/src/App.js 465 | 466 | ```js 467 | import React, { Component } from 'react'; 468 | import logo from './logo.svg'; 469 | import './App.css'; 470 | import HelloWorldApp from './component/HelloWorldApp'; 471 | 472 | class App extends Component { 473 | render() { 474 | return ( 475 |
476 | 477 |
478 | ); 479 | } 480 | } 481 | 482 | export default App; 483 | ``` 484 | --- 485 | 486 | ### /frontend-spring-boot-react-hello-world-with-routing/package.json 487 | 488 | ```json 489 | { 490 | "name": "spring-boot-react-hello-world-with-routing", 491 | "version": "0.1.0", 492 | "private": true, 493 | "dependencies": { 494 | "axios": "^0.18.0", 495 | "react": "^16.8.5", 496 | "react-dom": "^16.8.5", 497 | "react-router-dom": "^5.0.0", 498 | "react-scripts": "2.1.8" 499 | }, 500 | "scripts": { 501 | "start": "react-scripts start", 502 | "build": "react-scripts build", 503 | "test": "react-scripts test", 504 | "eject": "react-scripts eject" 505 | }, 506 | "eslintConfig": { 507 | "extends": "react-app" 508 | }, 509 | "browserslist": [ 510 | ">0.2%", 511 | "not dead", 512 | "not ie <= 11", 513 | "not op_mini all" 514 | ] 515 | } 516 | ``` 517 | --- 518 | 519 | ### /backend-spring-boot-react-hello-world-with-routing/pom.xml 520 | 521 | ```xml 522 | 523 | 526 | 4.0.0 527 | 528 | org.springframework.boot 529 | spring-boot-starter-parent 530 | 2.1.3.RELEASE 531 | 532 | 533 | com.in28minutes.fullstack.springboot.react.helloworld 534 | spring-boot-fullstack-hello-world-with-routing 535 | 0.0.1-SNAPSHOT 536 | spring-boot-fullstack-hello-world-with-routing 537 | Demo project for Spring Boot 538 | 539 | 540 | 1.8 541 | 542 | 543 | 544 | 545 | org.springframework.boot 546 | spring-boot-starter-web 547 | 548 | 549 | 550 | org.springframework.boot 551 | spring-boot-devtools 552 | runtime 553 | 554 | 555 | org.springframework.boot 556 | spring-boot-starter-test 557 | test 558 | 559 | 560 | 561 | 562 | 563 | 564 | org.springframework.boot 565 | spring-boot-maven-plugin 566 | 567 | 568 | 569 | 570 | 571 | ``` 572 | --- 573 | 574 | ### /backend-spring-boot-react-hello-world-with-routing/src/test/java/com/in28minutes/fullstack/springboot/react/helloworld/springbootreacthelloworldwithrouting/SpringBootReactHelloWorldWithRoutingApplicationTests.java 575 | 576 | ```java 577 | package com.in28minutes.fullstack.springboot.react.helloworld.springbootreacthelloworldwithrouting; 578 | 579 | import org.junit.Test; 580 | import org.junit.runner.RunWith; 581 | import org.springframework.boot.test.context.SpringBootTest; 582 | import org.springframework.test.context.junit4.SpringRunner; 583 | 584 | @RunWith(SpringRunner.class) 585 | @SpringBootTest 586 | public class SpringBootReactHelloWorldWithRoutingApplicationTests { 587 | 588 | @Test 589 | public void contextLoads() { 590 | } 591 | 592 | } 593 | ``` 594 | --- 595 | 596 | ### /backend-spring-boot-react-hello-world-with-routing/src/main/resources/application.properties 597 | 598 | ```properties 599 | 600 | ``` 601 | --- 602 | 603 | ### /backend-spring-boot-react-hello-world-with-routing/src/main/java/com/in28minutes/fullstack/springboot/helloworld/springboothelloworldwithrouting/helloworld/HelloWorldController.java 604 | 605 | ```java 606 | package com.in28minutes.fullstack.springboot.helloworld.springboothelloworldwithrouting.helloworld; 607 | import org.springframework.web.bind.annotation.CrossOrigin; 608 | import org.springframework.web.bind.annotation.GetMapping; 609 | import org.springframework.web.bind.annotation.PathVariable; 610 | import org.springframework.web.bind.annotation.RestController; 611 | 612 | //Controller 613 | @RestController 614 | @CrossOrigin(origins = { "http://localhost:3000", "http://localhost:4200" }) 615 | // 616 | public class HelloWorldController { 617 | 618 | @GetMapping(path = "/hello-world") 619 | public String helloWorld() { 620 | return "Hello World"; 621 | } 622 | 623 | @GetMapping(path = "/hello-world-bean") 624 | public HelloWorldBean helloWorldBean() { 625 | return new HelloWorldBean("Hello World From a Java Bean"); 626 | } 627 | 628 | ///hello-world/path-variable/in28minutes 629 | @GetMapping(path = "/hello-world/path-variable/{name}") 630 | public HelloWorldBean helloWorldPathVariable(@PathVariable String name) { 631 | //throw new RuntimeException("Something went wrong"); 632 | return new HelloWorldBean(String.format("Hello World, %s", name)); 633 | } 634 | } 635 | ``` 636 | --- 637 | 638 | ### /backend-spring-boot-react-hello-world-with-routing/src/main/java/com/in28minutes/fullstack/springboot/helloworld/springboothelloworldwithrouting/helloworld/HelloWorldBean.java 639 | 640 | ```java 641 | package com.in28minutes.fullstack.springboot.helloworld.springboothelloworldwithrouting.helloworld; 642 | public class HelloWorldBean { 643 | 644 | private String message; 645 | 646 | public HelloWorldBean(String message) { 647 | this.message = message; 648 | } 649 | 650 | public String getMessage() { 651 | return message; 652 | } 653 | 654 | public void setMessage(String message) { 655 | this.message = message; 656 | } 657 | 658 | @Override 659 | public String toString() { 660 | return String.format("HelloWorldBean [message=%s]", message); 661 | } 662 | 663 | } 664 | ``` 665 | --- 666 | 667 | ### /backend-spring-boot-react-hello-world-with-routing/src/main/java/com/in28minutes/fullstack/springboot/helloworld/springboothelloworldwithrouting/SpringBootFullStackHelloWorldWithRoutingApplication.java 668 | 669 | ```java 670 | package com.in28minutes.fullstack.springboot.helloworld.springboothelloworldwithrouting; 671 | 672 | import org.springframework.boot.SpringApplication; 673 | import org.springframework.boot.autoconfigure.SpringBootApplication; 674 | 675 | @SpringBootApplication 676 | public class SpringBootFullStackHelloWorldWithRoutingApplication { 677 | 678 | public static void main(String[] args) { 679 | SpringApplication.run(SpringBootFullStackHelloWorldWithRoutingApplication.class, args); 680 | } 681 | 682 | } 683 | ``` 684 | --- 685 | -------------------------------------------------------------------------------- /spring-boot-react-hello-world-with-routing/backend-spring-boot-react-hello-world-with-routing/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | org.springframework.boot 8 | spring-boot-starter-parent 9 | 2.1.3.RELEASE 10 | 11 | 12 | com.in28minutes.fullstack.springboot.react.helloworld 13 | spring-boot-fullstack-hello-world-with-routing 14 | 0.0.1-SNAPSHOT 15 | spring-boot-fullstack-hello-world-with-routing 16 | Demo project for Spring Boot 17 | 18 | 19 | 1.8 20 | 21 | 22 | 23 | 24 | org.springframework.boot 25 | spring-boot-starter-web 26 | 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-devtools 31 | runtime 32 | 33 | 34 | org.springframework.boot 35 | spring-boot-starter-test 36 | test 37 | 38 | 39 | 40 | 41 | 42 | 43 | org.springframework.boot 44 | spring-boot-maven-plugin 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /spring-boot-react-hello-world-with-routing/backend-spring-boot-react-hello-world-with-routing/src/main/java/com/in28minutes/fullstack/springboot/helloworld/springboothelloworldwithrouting/SpringBootFullStackHelloWorldWithRoutingApplication.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.fullstack.springboot.helloworld.springboothelloworldwithrouting; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class SpringBootFullStackHelloWorldWithRoutingApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(SpringBootFullStackHelloWorldWithRoutingApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /spring-boot-react-hello-world-with-routing/backend-spring-boot-react-hello-world-with-routing/src/main/java/com/in28minutes/fullstack/springboot/helloworld/springboothelloworldwithrouting/helloworld/HelloWorldBean.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.fullstack.springboot.helloworld.springboothelloworldwithrouting.helloworld; 2 | public class HelloWorldBean { 3 | 4 | private String message; 5 | 6 | public HelloWorldBean(String message) { 7 | this.message = message; 8 | } 9 | 10 | public String getMessage() { 11 | return message; 12 | } 13 | 14 | public void setMessage(String message) { 15 | this.message = message; 16 | } 17 | 18 | @Override 19 | public String toString() { 20 | return String.format("HelloWorldBean [message=%s]", message); 21 | } 22 | 23 | } -------------------------------------------------------------------------------- /spring-boot-react-hello-world-with-routing/backend-spring-boot-react-hello-world-with-routing/src/main/java/com/in28minutes/fullstack/springboot/helloworld/springboothelloworldwithrouting/helloworld/HelloWorldController.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.fullstack.springboot.helloworld.springboothelloworldwithrouting.helloworld; 2 | import org.springframework.web.bind.annotation.CrossOrigin; 3 | import org.springframework.web.bind.annotation.GetMapping; 4 | import org.springframework.web.bind.annotation.PathVariable; 5 | import org.springframework.web.bind.annotation.RestController; 6 | 7 | //Controller 8 | @RestController 9 | @CrossOrigin(origins = { "http://localhost:3000", "http://localhost:4200" }) 10 | // 11 | public class HelloWorldController { 12 | 13 | @GetMapping(path = "/hello-world") 14 | public String helloWorld() { 15 | return "Hello World"; 16 | } 17 | 18 | @GetMapping(path = "/hello-world-bean") 19 | public HelloWorldBean helloWorldBean() { 20 | return new HelloWorldBean("Hello World From a Java Bean"); 21 | } 22 | 23 | ///hello-world/path-variable/in28minutes 24 | @GetMapping(path = "/hello-world/path-variable/{name}") 25 | public HelloWorldBean helloWorldPathVariable(@PathVariable String name) { 26 | //throw new RuntimeException("Something went wrong"); 27 | return new HelloWorldBean(String.format("Hello World, %s", name)); 28 | } 29 | } -------------------------------------------------------------------------------- /spring-boot-react-hello-world-with-routing/backend-spring-boot-react-hello-world-with-routing/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /spring-boot-react-hello-world-with-routing/backend-spring-boot-react-hello-world-with-routing/src/test/java/com/in28minutes/fullstack/springboot/react/helloworld/springbootreacthelloworldwithrouting/SpringBootReactHelloWorldWithRoutingApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.fullstack.springboot.react.helloworld.springbootreacthelloworldwithrouting; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | @RunWith(SpringRunner.class) 9 | @SpringBootTest 10 | public class SpringBootReactHelloWorldWithRoutingApplicationTests { 11 | 12 | @Test 13 | public void contextLoads() { 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /spring-boot-react-hello-world-with-routing/frontend-spring-boot-react-hello-world-with-routing/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "spring-boot-react-hello-world-with-routing", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "axios": "^0.18.0", 7 | "react": "^16.8.5", 8 | "react-dom": "^16.8.5", 9 | "react-router-dom": "^5.0.0", 10 | "react-scripts": "2.1.8" 11 | }, 12 | "scripts": { 13 | "start": "react-scripts start", 14 | "build": "react-scripts build", 15 | "test": "react-scripts test", 16 | "eject": "react-scripts eject" 17 | }, 18 | "eslintConfig": { 19 | "extends": "react-app" 20 | }, 21 | "browserslist": [ 22 | ">0.2%", 23 | "not dead", 24 | "not ie <= 11", 25 | "not op_mini all" 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /spring-boot-react-hello-world-with-routing/frontend-spring-boot-react-hello-world-with-routing/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/spring-boot-react-fullstack-examples/99836f3c5c709bbc901a3c762399cded6371db47/spring-boot-react-hello-world-with-routing/frontend-spring-boot-react-hello-world-with-routing/public/favicon.ico -------------------------------------------------------------------------------- /spring-boot-react-hello-world-with-routing/frontend-spring-boot-react-hello-world-with-routing/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | 15 | 16 | 25 | React App 26 | 27 | 28 | 29 |
30 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /spring-boot-react-hello-world-with-routing/frontend-spring-boot-react-hello-world-with-routing/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /spring-boot-react-hello-world-with-routing/frontend-spring-boot-react-hello-world-with-routing/src/App.css: -------------------------------------------------------------------------------- 1 | @import url(https://unpkg.com/bootstrap@4.1.0/dist/css/bootstrap.min.css) -------------------------------------------------------------------------------- /spring-boot-react-hello-world-with-routing/frontend-spring-boot-react-hello-world-with-routing/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import logo from './logo.svg'; 3 | import './App.css'; 4 | import HelloWorldApp from './component/HelloWorldApp'; 5 | 6 | class App extends Component { 7 | render() { 8 | return ( 9 |
10 | 11 |
12 | ); 13 | } 14 | } 15 | 16 | export default App; 17 | -------------------------------------------------------------------------------- /spring-boot-react-hello-world-with-routing/frontend-spring-boot-react-hello-world-with-routing/src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | it('renders without crashing', () => { 6 | const div = document.createElement('div'); 7 | ReactDOM.render(, div); 8 | ReactDOM.unmountComponentAtNode(div); 9 | }); 10 | -------------------------------------------------------------------------------- /spring-boot-react-hello-world-with-routing/frontend-spring-boot-react-hello-world-with-routing/src/component/HelloWorldApp.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { BrowserRouter as Router, Route, Switch } from 'react-router-dom' 3 | import MenuComponent from './MenuComponent' 4 | import HelloWorldBeanComponent from './HelloWorldBeanComponent'; 5 | import HelloWorldStringComponent from './HelloWorldStringComponent'; 6 | 7 | class HelloWorldApp extends Component { 8 | render() { 9 | return ( 10 | <> 11 | 12 | <> 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 |
21 | 22 |
23 | 24 | ) 25 | } 26 | } 27 | 28 | export default HelloWorldApp; -------------------------------------------------------------------------------- /spring-boot-react-hello-world-with-routing/frontend-spring-boot-react-hello-world-with-routing/src/component/HelloWorldBeanComponent.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import HelloWorldService from '../service/HelloWorldService'; 3 | 4 | class HelloWorldBeanComponent extends Component { 5 | constructor(props) { 6 | super(props) 7 | this.state = { 8 | welcomeMessage: '' 9 | } 10 | } 11 | 12 | componentDidMount() { 13 | HelloWorldService.executeHelloWorldBeanService() 14 | .then(response => this.setState({ welcomeMessage: response.data.message })) 15 | .catch(this.setState({ welcomeMessage: 'Error Processing Request' })) 16 | } 17 | 18 | render() { 19 | return (<> 20 |

Hello World String Component

21 |
22 | {this.state.welcomeMessage} 23 |
24 |
25 | 26 |
27 | 28 | ) 29 | } 30 | 31 | gotoStringComponent = () => { 32 | this.props.history.push('/hello-world-string') 33 | } 34 | } 35 | 36 | export default HelloWorldBeanComponent -------------------------------------------------------------------------------- /spring-boot-react-hello-world-with-routing/frontend-spring-boot-react-hello-world-with-routing/src/component/HelloWorldStringComponent.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import HelloWorldService from '../service/HelloWorldService'; 3 | 4 | class HelloWorldStringComponent extends Component { 5 | constructor(props) { 6 | super(props) 7 | this.state = { 8 | welcomeMessage: '' 9 | } 10 | } 11 | 12 | componentDidMount() { 13 | HelloWorldService.executeHelloWorldService() 14 | .then(response => this.setState({ welcomeMessage: response.data })) 15 | .catch(this.setState({ welcomeMessage: 'Error Processing Request' })) 16 | } 17 | 18 | render() { 19 | return (<> 20 |

Hello World String Component

21 |
22 | {this.state.welcomeMessage} 23 |
24 |
25 | 26 |
27 | 28 | ) 29 | } 30 | 31 | gotoBeanComponent = () => { 32 | this.props.history.push('/hello-world-bean') 33 | } 34 | } 35 | 36 | export default HelloWorldStringComponent -------------------------------------------------------------------------------- /spring-boot-react-hello-world-with-routing/frontend-spring-boot-react-hello-world-with-routing/src/component/MenuComponent.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { Link } from 'react-router-dom' 3 | 4 | class MenuComponent extends Component { 5 | componentDidMount() { 6 | 7 | } 8 | render() { 9 | return ( 10 |
11 | 18 |
19 | ) 20 | 21 | } 22 | 23 | } 24 | 25 | export default MenuComponent -------------------------------------------------------------------------------- /spring-boot-react-hello-world-with-routing/frontend-spring-boot-react-hello-world-with-routing/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", 5 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", 6 | sans-serif; 7 | -webkit-font-smoothing: antialiased; 8 | -moz-osx-font-smoothing: grayscale; 9 | } 10 | 11 | code { 12 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", 13 | monospace; 14 | } 15 | -------------------------------------------------------------------------------- /spring-boot-react-hello-world-with-routing/frontend-spring-boot-react-hello-world-with-routing/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | import * as serviceWorker from './serviceWorker'; 6 | 7 | ReactDOM.render(, document.getElementById('root')); 8 | 9 | // If you want your app to work offline and load faster, you can change 10 | // unregister() to register() below. Note this comes with some pitfalls. 11 | // Learn more about service workers: https://bit.ly/CRA-PWA 12 | serviceWorker.unregister(); 13 | -------------------------------------------------------------------------------- /spring-boot-react-hello-world-with-routing/frontend-spring-boot-react-hello-world-with-routing/src/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /spring-boot-react-hello-world-with-routing/frontend-spring-boot-react-hello-world-with-routing/src/service/HelloWorldService.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | 3 | class HelloWorldService { 4 | 5 | executeHelloWorldService() { 6 | return axios.get('http://localhost:8080/hello-world'); 7 | } 8 | 9 | executeHelloWorldBeanService() { 10 | return axios.get('http://localhost:8080/hello-world-bean'); 11 | } 12 | 13 | executeHelloWorldPathVariableService(name) { 14 | return axios.get(`http://localhost:8080/hello-world/path-variable/${name}`); 15 | } 16 | 17 | } 18 | 19 | export default new HelloWorldService() -------------------------------------------------------------------------------- /spring-boot-react-hello-world-with-routing/frontend-spring-boot-react-hello-world-with-routing/src/serviceWorker.js: -------------------------------------------------------------------------------- 1 | // This optional code is used to register a service worker. 2 | // register() is not called by default. 3 | 4 | // This lets the app load faster on subsequent visits in production, and gives 5 | // it offline capabilities. However, it also means that developers (and users) 6 | // will only see deployed updates on subsequent visits to a page, after all the 7 | // existing tabs open on the page have been closed, since previously cached 8 | // resources are updated in the background. 9 | 10 | // To learn more about the benefits of this model and instructions on how to 11 | // opt-in, read https://bit.ly/CRA-PWA 12 | 13 | const isLocalhost = Boolean( 14 | window.location.hostname === 'localhost' || 15 | // [::1] is the IPv6 localhost address. 16 | window.location.hostname === '[::1]' || 17 | // 127.0.0.1/8 is considered localhost for IPv4. 18 | window.location.hostname.match( 19 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ 20 | ) 21 | ); 22 | 23 | export function register(config) { 24 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { 25 | // The URL constructor is available in all browsers that support SW. 26 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href); 27 | if (publicUrl.origin !== window.location.origin) { 28 | // Our service worker won't work if PUBLIC_URL is on a different origin 29 | // from what our page is served on. This might happen if a CDN is used to 30 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374 31 | return; 32 | } 33 | 34 | window.addEventListener('load', () => { 35 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; 36 | 37 | if (isLocalhost) { 38 | // This is running on localhost. Let's check if a service worker still exists or not. 39 | checkValidServiceWorker(swUrl, config); 40 | 41 | // Add some additional logging to localhost, pointing developers to the 42 | // service worker/PWA documentation. 43 | navigator.serviceWorker.ready.then(() => { 44 | console.log( 45 | 'This web app is being served cache-first by a service ' + 46 | 'worker. To learn more, visit https://bit.ly/CRA-PWA' 47 | ); 48 | }); 49 | } else { 50 | // Is not localhost. Just register service worker 51 | registerValidSW(swUrl, config); 52 | } 53 | }); 54 | } 55 | } 56 | 57 | function registerValidSW(swUrl, config) { 58 | navigator.serviceWorker 59 | .register(swUrl) 60 | .then(registration => { 61 | registration.onupdatefound = () => { 62 | const installingWorker = registration.installing; 63 | if (installingWorker == null) { 64 | return; 65 | } 66 | installingWorker.onstatechange = () => { 67 | if (installingWorker.state === 'installed') { 68 | if (navigator.serviceWorker.controller) { 69 | // At this point, the updated precached content has been fetched, 70 | // but the previous service worker will still serve the older 71 | // content until all client tabs are closed. 72 | console.log( 73 | 'New content is available and will be used when all ' + 74 | 'tabs for this page are closed. See https://bit.ly/CRA-PWA.' 75 | ); 76 | 77 | // Execute callback 78 | if (config && config.onUpdate) { 79 | config.onUpdate(registration); 80 | } 81 | } else { 82 | // At this point, everything has been precached. 83 | // It's the perfect time to display a 84 | // "Content is cached for offline use." message. 85 | console.log('Content is cached for offline use.'); 86 | 87 | // Execute callback 88 | if (config && config.onSuccess) { 89 | config.onSuccess(registration); 90 | } 91 | } 92 | } 93 | }; 94 | }; 95 | }) 96 | .catch(error => { 97 | console.error('Error during service worker registration:', error); 98 | }); 99 | } 100 | 101 | function checkValidServiceWorker(swUrl, config) { 102 | // Check if the service worker can be found. If it can't reload the page. 103 | fetch(swUrl) 104 | .then(response => { 105 | // Ensure service worker exists, and that we really are getting a JS file. 106 | const contentType = response.headers.get('content-type'); 107 | if ( 108 | response.status === 404 || 109 | (contentType != null && contentType.indexOf('javascript') === -1) 110 | ) { 111 | // No service worker found. Probably a different app. Reload the page. 112 | navigator.serviceWorker.ready.then(registration => { 113 | registration.unregister().then(() => { 114 | window.location.reload(); 115 | }); 116 | }); 117 | } else { 118 | // Service worker found. Proceed as normal. 119 | registerValidSW(swUrl, config); 120 | } 121 | }) 122 | .catch(() => { 123 | console.log( 124 | 'No internet connection found. App is running in offline mode.' 125 | ); 126 | }); 127 | } 128 | 129 | export function unregister() { 130 | if ('serviceWorker' in navigator) { 131 | navigator.serviceWorker.ready.then(registration => { 132 | registration.unregister(); 133 | }); 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /spring-boot-react-jpa-hibernate-with-h2-full-stack/backend-spring-boot-react-jpa-hibernate-with-h2-full-stack/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | org.springframework.boot 8 | spring-boot-starter-parent 9 | 2.1.3.RELEASE 10 | 11 | 12 | com.in28minutes.fullstack.springboot.react.jpa.hibernate 13 | spring-boot-fullstack-jpa-hibernate-with-h2-full-stack 14 | 0.0.1-SNAPSHOT 15 | spring-boot-fullstack-jpa-hibernate-with-h2-full-stack 16 | Demo project for Spring Boot 17 | 18 | 19 | 1.8 20 | 21 | 22 | 23 | 24 | org.springframework.boot 25 | spring-boot-starter-data-jpa 26 | 27 | 28 | org.springframework.boot 29 | spring-boot-starter-web 30 | 31 | 32 | 33 | org.springframework.boot 34 | spring-boot-devtools 35 | runtime 36 | 37 | 38 | com.h2database 39 | h2 40 | runtime 41 | 42 | 43 | org.springframework.boot 44 | spring-boot-starter-test 45 | test 46 | 47 | 48 | 49 | 50 | 51 | 52 | org.springframework.boot 53 | spring-boot-maven-plugin 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /spring-boot-react-jpa-hibernate-with-h2-full-stack/backend-spring-boot-react-jpa-hibernate-with-h2-full-stack/src/main/java/com/in28minutes/fullstack/springboot/jpa/hibernate/springbootjpahibernatewithh2fullstack/SpringBootFullStackJpaHibernateWithH2FullStackApplication.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.fullstack.springboot.jpa.hibernate.springbootjpahibernatewithh2fullstack; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class SpringBootFullStackJpaHibernateWithH2FullStackApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(SpringBootFullStackJpaHibernateWithH2FullStackApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /spring-boot-react-jpa-hibernate-with-h2-full-stack/backend-spring-boot-react-jpa-hibernate-with-h2-full-stack/src/main/java/com/in28minutes/fullstack/springboot/jpa/hibernate/springbootjpahibernatewithh2fullstack/course/Course.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.fullstack.springboot.jpa.hibernate.springbootjpahibernatewithh2fullstack.course; 2 | 3 | import javax.persistence.Entity; 4 | import javax.persistence.GeneratedValue; 5 | import javax.persistence.Id; 6 | 7 | @Entity 8 | public class Course { 9 | @Id 10 | @GeneratedValue 11 | private Long id; 12 | private String username; 13 | private String description; 14 | 15 | public Course() { 16 | 17 | } 18 | 19 | public Course(long id, String username, String description) { 20 | super(); 21 | this.id = id; 22 | this.username = username; 23 | this.description = description; 24 | } 25 | 26 | public Long getId() { 27 | return id; 28 | } 29 | 30 | public void setId(Long id) { 31 | this.id = id; 32 | } 33 | 34 | public String getUsername() { 35 | return username; 36 | } 37 | 38 | public void setUsername(String username) { 39 | this.username = username; 40 | } 41 | 42 | public String getDescription() { 43 | return description; 44 | } 45 | 46 | public void setDescription(String description) { 47 | this.description = description; 48 | } 49 | 50 | @Override 51 | public int hashCode() { 52 | final int prime = 31; 53 | int result = 1; 54 | result = prime * result + ((description == null) ? 0 : description.hashCode()); 55 | result = prime * result + ((id == null) ? 0 : id.hashCode()); 56 | result = prime * result + ((username == null) ? 0 : username.hashCode()); 57 | return result; 58 | } 59 | 60 | @Override 61 | public boolean equals(Object obj) { 62 | if (this == obj) 63 | return true; 64 | if (obj == null) 65 | return false; 66 | if (getClass() != obj.getClass()) 67 | return false; 68 | Course other = (Course) obj; 69 | if (description == null) { 70 | if (other.description != null) 71 | return false; 72 | } else if (!description.equals(other.description)) 73 | return false; 74 | if (id == null) { 75 | if (other.id != null) 76 | return false; 77 | } else if (!id.equals(other.id)) 78 | return false; 79 | if (username == null) { 80 | if (other.username != null) 81 | return false; 82 | } else if (!username.equals(other.username)) 83 | return false; 84 | return true; 85 | } 86 | 87 | } -------------------------------------------------------------------------------- /spring-boot-react-jpa-hibernate-with-h2-full-stack/backend-spring-boot-react-jpa-hibernate-with-h2-full-stack/src/main/java/com/in28minutes/fullstack/springboot/jpa/hibernate/springbootjpahibernatewithh2fullstack/course/CourseJpaRepository.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.fullstack.springboot.jpa.hibernate.springbootjpahibernatewithh2fullstack.course; 2 | import java.util.List; 3 | 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | import org.springframework.stereotype.Repository; 6 | 7 | @Repository 8 | public interface CourseJpaRepository extends JpaRepository{ 9 | List findByUsername(String username); 10 | } -------------------------------------------------------------------------------- /spring-boot-react-jpa-hibernate-with-h2-full-stack/backend-spring-boot-react-jpa-hibernate-with-h2-full-stack/src/main/java/com/in28minutes/fullstack/springboot/jpa/hibernate/springbootjpahibernatewithh2fullstack/course/CourseResource.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.fullstack.springboot.jpa.hibernate.springbootjpahibernatewithh2fullstack.course; 2 | 3 | import java.net.URI; 4 | import java.util.List; 5 | 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.http.HttpStatus; 8 | import org.springframework.http.ResponseEntity; 9 | import org.springframework.web.bind.annotation.CrossOrigin; 10 | import org.springframework.web.bind.annotation.DeleteMapping; 11 | import org.springframework.web.bind.annotation.GetMapping; 12 | import org.springframework.web.bind.annotation.PathVariable; 13 | import org.springframework.web.bind.annotation.PostMapping; 14 | import org.springframework.web.bind.annotation.PutMapping; 15 | import org.springframework.web.bind.annotation.RequestBody; 16 | import org.springframework.web.bind.annotation.RestController; 17 | import org.springframework.web.servlet.support.ServletUriComponentsBuilder; 18 | 19 | @CrossOrigin(origins = { "http://localhost:3000", "http://localhost:4200" }) 20 | @RestController 21 | public class CourseResource { 22 | 23 | @Autowired 24 | private CourseJpaRepository courseRepository; 25 | 26 | @GetMapping("/instructors/{username}/courses") 27 | public List getAllCourses(@PathVariable String username) { 28 | return courseRepository.findAll(); 29 | } 30 | 31 | @GetMapping("/instructors/{username}/courses/{id}") 32 | public Course getCourse(@PathVariable String username, @PathVariable long id) { 33 | return courseRepository.findById(id).orElseThrow(() -> new RuntimeException("Course Not Found with id " + id)); 34 | } 35 | 36 | @DeleteMapping("/instructors/{username}/courses/{id}") 37 | public ResponseEntity deleteCourse(@PathVariable String username, @PathVariable long id) { 38 | 39 | courseRepository.deleteById(id); 40 | 41 | return ResponseEntity.noContent().build(); 42 | } 43 | 44 | @PutMapping("/instructors/{username}/courses/{id}") 45 | public ResponseEntity updateCourse(@PathVariable String username, @PathVariable long id, 46 | @RequestBody Course course) { 47 | 48 | course.setUsername(username); 49 | 50 | Course courseUpdated = courseRepository.save(course); 51 | 52 | return new ResponseEntity(courseUpdated, HttpStatus.OK); 53 | } 54 | 55 | @PostMapping("/instructors/{username}/courses") 56 | public ResponseEntity createCourse(@PathVariable String username, @RequestBody Course course) { 57 | 58 | course.setUsername(username); 59 | 60 | Course createdCourse = courseRepository.save(course); 61 | 62 | URI uri = ServletUriComponentsBuilder.fromCurrentRequest().path("/{id}").buildAndExpand(createdCourse.getId()) 63 | .toUri(); 64 | 65 | return ResponseEntity.created(uri).build(); 66 | } 67 | 68 | } -------------------------------------------------------------------------------- /spring-boot-react-jpa-hibernate-with-h2-full-stack/backend-spring-boot-react-jpa-hibernate-with-h2-full-stack/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /spring-boot-react-jpa-hibernate-with-h2-full-stack/backend-spring-boot-react-jpa-hibernate-with-h2-full-stack/src/main/resources/data.sql: -------------------------------------------------------------------------------- 1 | insert into course(id, username,description) 2 | values(10001, 'in28minutes', 'Learn JPA'); 3 | 4 | insert into course(id, username,description) 5 | values(10002, 'in28minutes', 'Learn Data JPA'); 6 | 7 | insert into course(id, username,description) 8 | values(10003, 'in28minutes', 'Learn Microservices'); -------------------------------------------------------------------------------- /spring-boot-react-jpa-hibernate-with-h2-full-stack/backend-spring-boot-react-jpa-hibernate-with-h2-full-stack/src/test/java/com/in28minutes/fullstack/springboot/react/jpa/hibernate/springbootreactjpahibernatewithh2fullstack/SpringBootReactJpaHibernateWithH2FullStackApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.fullstack.springboot.react.jpa.hibernate.springbootreactjpahibernatewithh2fullstack; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | @RunWith(SpringRunner.class) 9 | @SpringBootTest 10 | public class SpringBootReactJpaHibernateWithH2FullStackApplicationTests { 11 | 12 | @Test 13 | public void contextLoads() { 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /spring-boot-react-jpa-hibernate-with-h2-full-stack/frontend-spring-boot-react-jpa-hibernate-with-h2-full-stack/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "spring-boot-react-jpa-hibernate-with-h2-full-stack", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "react": "^16.8.5", 7 | "react-dom": "^16.8.5", 8 | "react-scripts": "2.1.8" 9 | }, 10 | "scripts": { 11 | "start": "react-scripts start", 12 | "build": "react-scripts build", 13 | "test": "react-scripts test", 14 | "eject": "react-scripts eject" 15 | }, 16 | "eslintConfig": { 17 | "extends": "react-app" 18 | }, 19 | "browserslist": [ 20 | ">0.2%", 21 | "not dead", 22 | "not ie <= 11", 23 | "not op_mini all" 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /spring-boot-react-jpa-hibernate-with-h2-full-stack/frontend-spring-boot-react-jpa-hibernate-with-h2-full-stack/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/in28minutes/spring-boot-react-fullstack-examples/99836f3c5c709bbc901a3c762399cded6371db47/spring-boot-react-jpa-hibernate-with-h2-full-stack/frontend-spring-boot-react-jpa-hibernate-with-h2-full-stack/public/favicon.ico -------------------------------------------------------------------------------- /spring-boot-react-jpa-hibernate-with-h2-full-stack/frontend-spring-boot-react-jpa-hibernate-with-h2-full-stack/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | 15 | 16 | 25 | React App 26 | 27 | 28 | 29 |
30 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /spring-boot-react-jpa-hibernate-with-h2-full-stack/frontend-spring-boot-react-jpa-hibernate-with-h2-full-stack/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /spring-boot-react-jpa-hibernate-with-h2-full-stack/frontend-spring-boot-react-jpa-hibernate-with-h2-full-stack/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | animation: App-logo-spin infinite 20s linear; 7 | height: 40vmin; 8 | pointer-events: none; 9 | } 10 | 11 | .App-header { 12 | background-color: #282c34; 13 | min-height: 100vh; 14 | display: flex; 15 | flex-direction: column; 16 | align-items: center; 17 | justify-content: center; 18 | font-size: calc(10px + 2vmin); 19 | color: white; 20 | } 21 | 22 | .App-link { 23 | color: #61dafb; 24 | } 25 | 26 | @keyframes App-logo-spin { 27 | from { 28 | transform: rotate(0deg); 29 | } 30 | to { 31 | transform: rotate(360deg); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /spring-boot-react-jpa-hibernate-with-h2-full-stack/frontend-spring-boot-react-jpa-hibernate-with-h2-full-stack/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import logo from './logo.svg'; 3 | import './App.css'; 4 | 5 | class App extends Component { 6 | render() { 7 | return ( 8 |
9 |
10 | logo 11 |

12 | Edit src/App.js and save to reload. 13 |

14 | 20 | Learn React 21 | 22 |
23 |
24 | ); 25 | } 26 | } 27 | 28 | export default App; 29 | -------------------------------------------------------------------------------- /spring-boot-react-jpa-hibernate-with-h2-full-stack/frontend-spring-boot-react-jpa-hibernate-with-h2-full-stack/src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | it('renders without crashing', () => { 6 | const div = document.createElement('div'); 7 | ReactDOM.render(, div); 8 | ReactDOM.unmountComponentAtNode(div); 9 | }); 10 | -------------------------------------------------------------------------------- /spring-boot-react-jpa-hibernate-with-h2-full-stack/frontend-spring-boot-react-jpa-hibernate-with-h2-full-stack/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", 5 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", 6 | sans-serif; 7 | -webkit-font-smoothing: antialiased; 8 | -moz-osx-font-smoothing: grayscale; 9 | } 10 | 11 | code { 12 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", 13 | monospace; 14 | } 15 | -------------------------------------------------------------------------------- /spring-boot-react-jpa-hibernate-with-h2-full-stack/frontend-spring-boot-react-jpa-hibernate-with-h2-full-stack/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | import * as serviceWorker from './serviceWorker'; 6 | 7 | ReactDOM.render(, document.getElementById('root')); 8 | 9 | // If you want your app to work offline and load faster, you can change 10 | // unregister() to register() below. Note this comes with some pitfalls. 11 | // Learn more about service workers: https://bit.ly/CRA-PWA 12 | serviceWorker.unregister(); 13 | -------------------------------------------------------------------------------- /spring-boot-react-jpa-hibernate-with-h2-full-stack/frontend-spring-boot-react-jpa-hibernate-with-h2-full-stack/src/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /spring-boot-react-jpa-hibernate-with-h2-full-stack/frontend-spring-boot-react-jpa-hibernate-with-h2-full-stack/src/serviceWorker.js: -------------------------------------------------------------------------------- 1 | // This optional code is used to register a service worker. 2 | // register() is not called by default. 3 | 4 | // This lets the app load faster on subsequent visits in production, and gives 5 | // it offline capabilities. However, it also means that developers (and users) 6 | // will only see deployed updates on subsequent visits to a page, after all the 7 | // existing tabs open on the page have been closed, since previously cached 8 | // resources are updated in the background. 9 | 10 | // To learn more about the benefits of this model and instructions on how to 11 | // opt-in, read https://bit.ly/CRA-PWA 12 | 13 | const isLocalhost = Boolean( 14 | window.location.hostname === 'localhost' || 15 | // [::1] is the IPv6 localhost address. 16 | window.location.hostname === '[::1]' || 17 | // 127.0.0.1/8 is considered localhost for IPv4. 18 | window.location.hostname.match( 19 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ 20 | ) 21 | ); 22 | 23 | export function register(config) { 24 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { 25 | // The URL constructor is available in all browsers that support SW. 26 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href); 27 | if (publicUrl.origin !== window.location.origin) { 28 | // Our service worker won't work if PUBLIC_URL is on a different origin 29 | // from what our page is served on. This might happen if a CDN is used to 30 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374 31 | return; 32 | } 33 | 34 | window.addEventListener('load', () => { 35 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; 36 | 37 | if (isLocalhost) { 38 | // This is running on localhost. Let's check if a service worker still exists or not. 39 | checkValidServiceWorker(swUrl, config); 40 | 41 | // Add some additional logging to localhost, pointing developers to the 42 | // service worker/PWA documentation. 43 | navigator.serviceWorker.ready.then(() => { 44 | console.log( 45 | 'This web app is being served cache-first by a service ' + 46 | 'worker. To learn more, visit https://bit.ly/CRA-PWA' 47 | ); 48 | }); 49 | } else { 50 | // Is not localhost. Just register service worker 51 | registerValidSW(swUrl, config); 52 | } 53 | }); 54 | } 55 | } 56 | 57 | function registerValidSW(swUrl, config) { 58 | navigator.serviceWorker 59 | .register(swUrl) 60 | .then(registration => { 61 | registration.onupdatefound = () => { 62 | const installingWorker = registration.installing; 63 | if (installingWorker == null) { 64 | return; 65 | } 66 | installingWorker.onstatechange = () => { 67 | if (installingWorker.state === 'installed') { 68 | if (navigator.serviceWorker.controller) { 69 | // At this point, the updated precached content has been fetched, 70 | // but the previous service worker will still serve the older 71 | // content until all client tabs are closed. 72 | console.log( 73 | 'New content is available and will be used when all ' + 74 | 'tabs for this page are closed. See https://bit.ly/CRA-PWA.' 75 | ); 76 | 77 | // Execute callback 78 | if (config && config.onUpdate) { 79 | config.onUpdate(registration); 80 | } 81 | } else { 82 | // At this point, everything has been precached. 83 | // It's the perfect time to display a 84 | // "Content is cached for offline use." message. 85 | console.log('Content is cached for offline use.'); 86 | 87 | // Execute callback 88 | if (config && config.onSuccess) { 89 | config.onSuccess(registration); 90 | } 91 | } 92 | } 93 | }; 94 | }; 95 | }) 96 | .catch(error => { 97 | console.error('Error during service worker registration:', error); 98 | }); 99 | } 100 | 101 | function checkValidServiceWorker(swUrl, config) { 102 | // Check if the service worker can be found. If it can't reload the page. 103 | fetch(swUrl) 104 | .then(response => { 105 | // Ensure service worker exists, and that we really are getting a JS file. 106 | const contentType = response.headers.get('content-type'); 107 | if ( 108 | response.status === 404 || 109 | (contentType != null && contentType.indexOf('javascript') === -1) 110 | ) { 111 | // No service worker found. Probably a different app. Reload the page. 112 | navigator.serviceWorker.ready.then(registration => { 113 | registration.unregister().then(() => { 114 | window.location.reload(); 115 | }); 116 | }); 117 | } else { 118 | // Service worker found. Proceed as normal. 119 | registerValidSW(swUrl, config); 120 | } 121 | }) 122 | .catch(() => { 123 | console.log( 124 | 'No internet connection found. App is running in offline mode.' 125 | ); 126 | }); 127 | } 128 | 129 | export function unregister() { 130 | if ('serviceWorker' in navigator) { 131 | navigator.serviceWorker.ready.then(registration => { 132 | registration.unregister(); 133 | }); 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /spring-boot-react-jwt-auth-login-logout/backend-spring-boot-react-jwt-auth-login-logout/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | org.springframework.boot 8 | spring-boot-starter-parent 9 | 2.1.3.RELEASE 10 | 11 | 12 | com.in28minutes.fullstack.springboot.jwt.basic.authentication 13 | spring-boot-fullstack-jwt-auth-login-logout 14 | 0.0.1-SNAPSHOT 15 | spring-boot-fullstack-jwt-auth-login-logout 16 | Demo project for Spring Boot 17 | 18 | 19 | 1.8 20 | 21 | 22 | 23 | 24 | org.springframework.boot 25 | spring-boot-starter-security 26 | 27 | 28 | org.springframework.boot 29 | spring-boot-starter-web 30 | 31 | 32 | io.jsonwebtoken 33 | jjwt 34 | 0.9.1 35 | 36 | 37 | org.springframework.boot 38 | spring-boot-devtools 39 | runtime 40 | 41 | 42 | org.springframework.boot 43 | spring-boot-starter-test 44 | test 45 | 46 | 47 | org.springframework.security 48 | spring-security-test 49 | test 50 | 51 | 52 | 53 | 54 | 55 | 56 | org.springframework.boot 57 | spring-boot-maven-plugin 58 | 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /spring-boot-react-jwt-auth-login-logout/backend-spring-boot-react-jwt-auth-login-logout/src/main/java/com/in28minutes/fullstack/springboot/jwt/basic/authentication/springbootjwtauthloginlogout/SpringBootFullStackJwtAuthLoginLogoutApplication.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.fullstack.springboot.jwt.basic.authentication.springbootjwtauthloginlogout; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class SpringBootFullStackJwtAuthLoginLogoutApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(SpringBootFullStackJwtAuthLoginLogoutApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /spring-boot-react-jwt-auth-login-logout/backend-spring-boot-react-jwt-auth-login-logout/src/main/java/com/in28minutes/fullstack/springboot/jwt/basic/authentication/springbootjwtauthloginlogout/course/Course.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.fullstack.springboot.jwt.basic.authentication.springbootjwtauthloginlogout.course; 2 | 3 | public class Course { 4 | private Long id; 5 | private String username; 6 | private String description; 7 | 8 | public Course() { 9 | 10 | } 11 | 12 | public Course(long id, String username, String description) { 13 | super(); 14 | this.id = id; 15 | this.username = username; 16 | this.description = description; 17 | } 18 | 19 | public Long getId() { 20 | return id; 21 | } 22 | 23 | public void setId(Long id) { 24 | this.id = id; 25 | } 26 | 27 | public String getUsername() { 28 | return username; 29 | } 30 | 31 | public void setUsername(String username) { 32 | this.username = username; 33 | } 34 | 35 | public String getDescription() { 36 | return description; 37 | } 38 | 39 | public void setDescription(String description) { 40 | this.description = description; 41 | } 42 | 43 | @Override 44 | public int hashCode() { 45 | final int prime = 31; 46 | int result = 1; 47 | result = prime * result + ((description == null) ? 0 : description.hashCode()); 48 | result = prime * result + ((id == null) ? 0 : id.hashCode()); 49 | result = prime * result + ((username == null) ? 0 : username.hashCode()); 50 | return result; 51 | } 52 | 53 | @Override 54 | public boolean equals(Object obj) { 55 | if (this == obj) 56 | return true; 57 | if (obj == null) 58 | return false; 59 | if (getClass() != obj.getClass()) 60 | return false; 61 | Course other = (Course) obj; 62 | if (description == null) { 63 | if (other.description != null) 64 | return false; 65 | } else if (!description.equals(other.description)) 66 | return false; 67 | if (id == null) { 68 | if (other.id != null) 69 | return false; 70 | } else if (!id.equals(other.id)) 71 | return false; 72 | if (username == null) { 73 | if (other.username != null) 74 | return false; 75 | } else if (!username.equals(other.username)) 76 | return false; 77 | return true; 78 | } 79 | 80 | } -------------------------------------------------------------------------------- /spring-boot-react-jwt-auth-login-logout/backend-spring-boot-react-jwt-auth-login-logout/src/main/java/com/in28minutes/fullstack/springboot/jwt/basic/authentication/springbootjwtauthloginlogout/course/CourseResource.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.fullstack.springboot.jwt.basic.authentication.springbootjwtauthloginlogout.course; 2 | 3 | import java.util.List; 4 | 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.web.bind.annotation.CrossOrigin; 7 | import org.springframework.web.bind.annotation.GetMapping; 8 | import org.springframework.web.bind.annotation.PathVariable; 9 | import org.springframework.web.bind.annotation.RestController; 10 | 11 | @CrossOrigin(origins = { "http://localhost:3000", "http://localhost:4200" }) 12 | @RestController 13 | public class CourseResource { 14 | 15 | @Autowired 16 | private CoursesHardcodedService courseManagementService; 17 | 18 | @GetMapping("/instructors/{username}/courses") 19 | public List getAllCourses(@PathVariable String username) { 20 | return courseManagementService.findAll(); 21 | } 22 | } -------------------------------------------------------------------------------- /spring-boot-react-jwt-auth-login-logout/backend-spring-boot-react-jwt-auth-login-logout/src/main/java/com/in28minutes/fullstack/springboot/jwt/basic/authentication/springbootjwtauthloginlogout/course/CoursesHardcodedService.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.fullstack.springboot.jwt.basic.authentication.springbootjwtauthloginlogout.course; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import org.springframework.stereotype.Service; 7 | 8 | @Service 9 | public class CoursesHardcodedService { 10 | 11 | private static List courses = new ArrayList<>(); 12 | private static long idCounter = 0; 13 | 14 | static { 15 | courses.add(new Course(++idCounter, "in28minutes", "Learn Full stack with Spring Boot and Angular")); 16 | courses.add(new Course(++idCounter, "in28minutes", "Learn Full stack with Spring Boot and React")); 17 | courses.add(new Course(++idCounter, "in28minutes", "Master Microservices with Spring Boot and Spring Cloud")); 18 | courses.add(new Course(++idCounter, "in28minutes", 19 | "Deploy Spring Boot Microservices to Cloud with Docker and Kubernetes")); 20 | } 21 | 22 | public List findAll() { 23 | return courses; 24 | } 25 | } -------------------------------------------------------------------------------- /spring-boot-react-jwt-auth-login-logout/backend-spring-boot-react-jwt-auth-login-logout/src/main/java/com/in28minutes/fullstack/springboot/jwt/basic/authentication/springbootjwtauthloginlogout/jwt/AuthenticationException.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.fullstack.springboot.jwt.basic.authentication.springbootjwtauthloginlogout.jwt; 2 | public class AuthenticationException extends RuntimeException { 3 | public AuthenticationException(String message, Throwable cause) { 4 | super(message, cause); 5 | } 6 | } 7 | 8 | -------------------------------------------------------------------------------- /spring-boot-react-jwt-auth-login-logout/backend-spring-boot-react-jwt-auth-login-logout/src/main/java/com/in28minutes/fullstack/springboot/jwt/basic/authentication/springbootjwtauthloginlogout/jwt/JWTWebSecurityConfig.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.fullstack.springboot.jwt.basic.authentication.springbootjwtauthloginlogout.jwt; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.beans.factory.annotation.Value; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | import org.springframework.http.HttpMethod; 8 | import org.springframework.security.authentication.AuthenticationManager; 9 | import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 10 | import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; 11 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 12 | import org.springframework.security.config.annotation.web.builders.WebSecurity; 13 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 14 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 15 | import org.springframework.security.config.http.SessionCreationPolicy; 16 | import org.springframework.security.core.userdetails.UserDetailsService; 17 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 18 | import org.springframework.security.crypto.password.PasswordEncoder; 19 | import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; 20 | 21 | @Configuration 22 | @EnableWebSecurity 23 | @EnableGlobalMethodSecurity(prePostEnabled = true) 24 | public class JWTWebSecurityConfig extends WebSecurityConfigurerAdapter { 25 | 26 | @Autowired 27 | private JwtUnAuthorizedResponseAuthenticationEntryPoint jwtUnAuthorizedResponseAuthenticationEntryPoint; 28 | 29 | @Autowired 30 | private UserDetailsService jwtInMemoryUserDetailsService; 31 | 32 | @Autowired 33 | private JwtTokenAuthorizationOncePerRequestFilter jwtAuthenticationTokenFilter; 34 | 35 | @Value("${jwt.get.token.uri}") 36 | private String authenticationPath; 37 | 38 | @Autowired 39 | public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { 40 | auth 41 | .userDetailsService(jwtInMemoryUserDetailsService) 42 | .passwordEncoder(passwordEncoderBean()); 43 | } 44 | 45 | @Bean 46 | public PasswordEncoder passwordEncoderBean() { 47 | return new BCryptPasswordEncoder(); 48 | } 49 | 50 | @Bean 51 | @Override 52 | public AuthenticationManager authenticationManagerBean() throws Exception { 53 | return super.authenticationManagerBean(); 54 | } 55 | 56 | @Override 57 | protected void configure(HttpSecurity httpSecurity) throws Exception { 58 | httpSecurity 59 | .csrf().disable() 60 | .exceptionHandling().authenticationEntryPoint(jwtUnAuthorizedResponseAuthenticationEntryPoint).and() 61 | .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and() 62 | .authorizeRequests() 63 | .anyRequest().authenticated(); 64 | 65 | httpSecurity 66 | .addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); 67 | 68 | httpSecurity 69 | .headers() 70 | .frameOptions().sameOrigin() //H2 Console Needs this setting 71 | .cacheControl(); //disable caching 72 | } 73 | 74 | @Override 75 | public void configure(WebSecurity webSecurity) throws Exception { 76 | webSecurity 77 | .ignoring() 78 | .antMatchers( 79 | HttpMethod.POST, 80 | authenticationPath 81 | ) 82 | .antMatchers(HttpMethod.OPTIONS, "/**") 83 | .and() 84 | .ignoring() 85 | .antMatchers( 86 | HttpMethod.GET, 87 | "/" //Other Stuff You want to Ignore 88 | ) 89 | .and() 90 | .ignoring() 91 | .antMatchers("/h2-console/**/**");//Should not be in Production! 92 | } 93 | } 94 | 95 | -------------------------------------------------------------------------------- /spring-boot-react-jwt-auth-login-logout/backend-spring-boot-react-jwt-auth-login-logout/src/main/java/com/in28minutes/fullstack/springboot/jwt/basic/authentication/springbootjwtauthloginlogout/jwt/JwtAuthenticationRestController.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.fullstack.springboot.jwt.basic.authentication.springbootjwtauthloginlogout.jwt; 2 | 3 | import java.util.Objects; 4 | 5 | import javax.servlet.http.HttpServletRequest; 6 | 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.beans.factory.annotation.Value; 9 | import org.springframework.http.HttpStatus; 10 | import org.springframework.http.ResponseEntity; 11 | import org.springframework.security.authentication.AuthenticationManager; 12 | import org.springframework.security.authentication.BadCredentialsException; 13 | import org.springframework.security.authentication.DisabledException; 14 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; 15 | import org.springframework.security.core.userdetails.UserDetails; 16 | import org.springframework.security.core.userdetails.UserDetailsService; 17 | import org.springframework.web.bind.annotation.CrossOrigin; 18 | import org.springframework.web.bind.annotation.ExceptionHandler; 19 | import org.springframework.web.bind.annotation.RequestBody; 20 | import org.springframework.web.bind.annotation.RequestMapping; 21 | import org.springframework.web.bind.annotation.RequestMethod; 22 | import org.springframework.web.bind.annotation.RestController; 23 | 24 | @RestController 25 | @CrossOrigin(origins={ "http://localhost:3000", "http://localhost:4200" }) 26 | public class JwtAuthenticationRestController { 27 | 28 | @Value("${jwt.http.request.header}") 29 | private String tokenHeader; 30 | 31 | @Autowired 32 | private AuthenticationManager authenticationManager; 33 | 34 | @Autowired 35 | private JwtTokenUtil jwtTokenUtil; 36 | 37 | @Autowired 38 | private UserDetailsService jwtInMemoryUserDetailsService; 39 | 40 | @RequestMapping(value = "${jwt.get.token.uri}", method = RequestMethod.POST) 41 | public ResponseEntity createAuthenticationToken(@RequestBody JwtTokenRequest authenticationRequest) 42 | throws AuthenticationException { 43 | 44 | authenticate(authenticationRequest.getUsername(), authenticationRequest.getPassword()); 45 | 46 | final UserDetails userDetails = jwtInMemoryUserDetailsService.loadUserByUsername(authenticationRequest.getUsername()); 47 | 48 | final String token = jwtTokenUtil.generateToken(userDetails); 49 | 50 | return ResponseEntity.ok(new JwtTokenResponse(token)); 51 | } 52 | 53 | @RequestMapping(value = "${jwt.refresh.token.uri}", method = RequestMethod.GET) 54 | public ResponseEntity refreshAndGetAuthenticationToken(HttpServletRequest request) { 55 | String authToken = request.getHeader(tokenHeader); 56 | final String token = authToken.substring(7); 57 | String username = jwtTokenUtil.getUsernameFromToken(token); 58 | JwtUserDetails user = (JwtUserDetails) jwtInMemoryUserDetailsService.loadUserByUsername(username); 59 | 60 | if (jwtTokenUtil.canTokenBeRefreshed(token)) { 61 | String refreshedToken = jwtTokenUtil.refreshToken(token); 62 | return ResponseEntity.ok(new JwtTokenResponse(refreshedToken)); 63 | } else { 64 | return ResponseEntity.badRequest().body(null); 65 | } 66 | } 67 | 68 | @ExceptionHandler({ AuthenticationException.class }) 69 | public ResponseEntity handleAuthenticationException(AuthenticationException e) { 70 | return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(e.getMessage()); 71 | } 72 | 73 | private void authenticate(String username, String password) { 74 | Objects.requireNonNull(username); 75 | Objects.requireNonNull(password); 76 | 77 | try { 78 | authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password)); 79 | } catch (DisabledException e) { 80 | throw new AuthenticationException("USER_DISABLED", e); 81 | } catch (BadCredentialsException e) { 82 | throw new AuthenticationException("INVALID_CREDENTIALS", e); 83 | } 84 | } 85 | } 86 | 87 | -------------------------------------------------------------------------------- /spring-boot-react-jwt-auth-login-logout/backend-spring-boot-react-jwt-auth-login-logout/src/main/java/com/in28minutes/fullstack/springboot/jwt/basic/authentication/springbootjwtauthloginlogout/jwt/JwtInMemoryUserDetailsService.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.fullstack.springboot.jwt.basic.authentication.springbootjwtauthloginlogout.jwt; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.Optional; 6 | 7 | import org.springframework.security.core.userdetails.UserDetails; 8 | import org.springframework.security.core.userdetails.UserDetailsService; 9 | import org.springframework.security.core.userdetails.UsernameNotFoundException; 10 | import org.springframework.stereotype.Service; 11 | 12 | @Service 13 | public class JwtInMemoryUserDetailsService implements UserDetailsService { 14 | 15 | static List inMemoryUserList = new ArrayList<>(); 16 | 17 | static { 18 | inMemoryUserList.add(new JwtUserDetails(1L, "in28minutes", 19 | "$2a$10$3zHzb.Npv1hfZbLEU5qsdOju/tk2je6W6PnNnY.c1ujWPcZh4PL6e", "ROLE_USER_2")); 20 | } 21 | 22 | @Override 23 | public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { 24 | Optional findFirst = inMemoryUserList.stream() 25 | .filter(user -> user.getUsername().equals(username)).findFirst(); 26 | 27 | if (!findFirst.isPresent()) { 28 | throw new UsernameNotFoundException(String.format("USER_NOT_FOUND '%s'.", username)); 29 | } 30 | 31 | return findFirst.get(); 32 | } 33 | 34 | } 35 | 36 | 37 | -------------------------------------------------------------------------------- /spring-boot-react-jwt-auth-login-logout/backend-spring-boot-react-jwt-auth-login-logout/src/main/java/com/in28minutes/fullstack/springboot/jwt/basic/authentication/springbootjwtauthloginlogout/jwt/JwtTokenAuthorizationOncePerRequestFilter.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.fullstack.springboot.jwt.basic.authentication.springbootjwtauthloginlogout.jwt; 2 | 3 | import java.io.IOException; 4 | 5 | import javax.servlet.FilterChain; 6 | import javax.servlet.ServletException; 7 | import javax.servlet.http.HttpServletRequest; 8 | import javax.servlet.http.HttpServletResponse; 9 | 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | import org.springframework.beans.factory.annotation.Autowired; 13 | import org.springframework.beans.factory.annotation.Value; 14 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; 15 | import org.springframework.security.core.context.SecurityContextHolder; 16 | import org.springframework.security.core.userdetails.UserDetails; 17 | import org.springframework.security.core.userdetails.UserDetailsService; 18 | import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; 19 | import org.springframework.stereotype.Component; 20 | import org.springframework.web.filter.OncePerRequestFilter; 21 | 22 | import io.jsonwebtoken.ExpiredJwtException; 23 | 24 | @Component 25 | public class JwtTokenAuthorizationOncePerRequestFilter extends OncePerRequestFilter { 26 | 27 | private final Logger logger = LoggerFactory.getLogger(this.getClass()); 28 | 29 | @Autowired 30 | private UserDetailsService jwtInMemoryUserDetailsService; 31 | 32 | @Autowired 33 | private JwtTokenUtil jwtTokenUtil; 34 | 35 | @Value("${jwt.http.request.header}") 36 | private String tokenHeader; 37 | 38 | @Override 39 | protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException { 40 | logger.debug("Authentication Request For '{}'", request.getRequestURL()); 41 | 42 | final String requestTokenHeader = request.getHeader(this.tokenHeader); 43 | 44 | String username = null; 45 | String jwtToken = null; 46 | if (requestTokenHeader != null && requestTokenHeader.startsWith("Bearer ")) { 47 | jwtToken = requestTokenHeader.substring(7); 48 | try { 49 | username = jwtTokenUtil.getUsernameFromToken(jwtToken); 50 | } catch (IllegalArgumentException e) { 51 | logger.error("JWT_TOKEN_UNABLE_TO_GET_USERNAME", e); 52 | } catch (ExpiredJwtException e) { 53 | logger.warn("JWT_TOKEN_EXPIRED", e); 54 | } 55 | } else { 56 | logger.warn("JWT_TOKEN_DOES_NOT_START_WITH_BEARER_STRING"); 57 | } 58 | 59 | logger.debug("JWT_TOKEN_USERNAME_VALUE '{}'", username); 60 | if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) { 61 | 62 | UserDetails userDetails = this.jwtInMemoryUserDetailsService.loadUserByUsername(username); 63 | 64 | if (jwtTokenUtil.validateToken(jwtToken, userDetails)) { 65 | UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); 66 | usernamePasswordAuthenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); 67 | SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken); 68 | } 69 | } 70 | 71 | chain.doFilter(request, response); 72 | } 73 | } 74 | 75 | 76 | -------------------------------------------------------------------------------- /spring-boot-react-jwt-auth-login-logout/backend-spring-boot-react-jwt-auth-login-logout/src/main/java/com/in28minutes/fullstack/springboot/jwt/basic/authentication/springbootjwtauthloginlogout/jwt/JwtTokenRequest.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.fullstack.springboot.jwt.basic.authentication.springbootjwtauthloginlogout.jwt; 2 | 3 | import java.io.Serializable; 4 | 5 | public class JwtTokenRequest implements Serializable { 6 | 7 | private static final long serialVersionUID = -5616176897013108345L; 8 | 9 | private String username; 10 | private String password; 11 | 12 | public JwtTokenRequest() { 13 | super(); 14 | } 15 | 16 | public JwtTokenRequest(String username, String password) { 17 | this.setUsername(username); 18 | this.setPassword(password); 19 | } 20 | 21 | public String getUsername() { 22 | return this.username; 23 | } 24 | 25 | public void setUsername(String username) { 26 | this.username = username; 27 | } 28 | 29 | public String getPassword() { 30 | return this.password; 31 | } 32 | 33 | public void setPassword(String password) { 34 | this.password = password; 35 | } 36 | } 37 | 38 | -------------------------------------------------------------------------------- /spring-boot-react-jwt-auth-login-logout/backend-spring-boot-react-jwt-auth-login-logout/src/main/java/com/in28minutes/fullstack/springboot/jwt/basic/authentication/springbootjwtauthloginlogout/jwt/JwtTokenResponse.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.fullstack.springboot.jwt.basic.authentication.springbootjwtauthloginlogout.jwt; 2 | 3 | import java.io.Serializable; 4 | 5 | public class JwtTokenResponse implements Serializable { 6 | 7 | private static final long serialVersionUID = 8317676219297719109L; 8 | 9 | private final String token; 10 | 11 | public JwtTokenResponse(String token) { 12 | this.token = token; 13 | } 14 | 15 | public String getToken() { 16 | return this.token; 17 | } 18 | } -------------------------------------------------------------------------------- /spring-boot-react-jwt-auth-login-logout/backend-spring-boot-react-jwt-auth-login-logout/src/main/java/com/in28minutes/fullstack/springboot/jwt/basic/authentication/springbootjwtauthloginlogout/jwt/JwtTokenUtil.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.fullstack.springboot.jwt.basic.authentication.springbootjwtauthloginlogout.jwt; 2 | 3 | import java.io.Serializable; 4 | import java.util.Date; 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | import java.util.function.Function; 8 | 9 | import org.springframework.beans.factory.annotation.Value; 10 | import org.springframework.security.core.userdetails.UserDetails; 11 | import org.springframework.stereotype.Component; 12 | 13 | import io.jsonwebtoken.Claims; 14 | import io.jsonwebtoken.Clock; 15 | import io.jsonwebtoken.Jwts; 16 | import io.jsonwebtoken.SignatureAlgorithm; 17 | import io.jsonwebtoken.impl.DefaultClock; 18 | 19 | @Component 20 | public class JwtTokenUtil implements Serializable { 21 | 22 | static final String CLAIM_KEY_USERNAME = "sub"; 23 | static final String CLAIM_KEY_CREATED = "iat"; 24 | private static final long serialVersionUID = -3301605591108950415L; 25 | private Clock clock = DefaultClock.INSTANCE; 26 | 27 | @Value("${jwt.signing.key.secret}") 28 | private String secret; 29 | 30 | @Value("${jwt.token.expiration.in.seconds}") 31 | private Long expiration; 32 | 33 | public String getUsernameFromToken(String token) { 34 | return getClaimFromToken(token, Claims::getSubject); 35 | } 36 | 37 | public Date getIssuedAtDateFromToken(String token) { 38 | return getClaimFromToken(token, Claims::getIssuedAt); 39 | } 40 | 41 | public Date getExpirationDateFromToken(String token) { 42 | return getClaimFromToken(token, Claims::getExpiration); 43 | } 44 | 45 | public T getClaimFromToken(String token, Function claimsResolver) { 46 | final Claims claims = getAllClaimsFromToken(token); 47 | return claimsResolver.apply(claims); 48 | } 49 | 50 | private Claims getAllClaimsFromToken(String token) { 51 | return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody(); 52 | } 53 | 54 | private Boolean isTokenExpired(String token) { 55 | final Date expiration = getExpirationDateFromToken(token); 56 | return expiration.before(clock.now()); 57 | } 58 | 59 | private Boolean ignoreTokenExpiration(String token) { 60 | // here you specify tokens, for that the expiration is ignored 61 | return false; 62 | } 63 | 64 | public String generateToken(UserDetails userDetails) { 65 | Map claims = new HashMap<>(); 66 | return doGenerateToken(claims, userDetails.getUsername()); 67 | } 68 | 69 | private String doGenerateToken(Map claims, String subject) { 70 | final Date createdDate = clock.now(); 71 | final Date expirationDate = calculateExpirationDate(createdDate); 72 | 73 | return Jwts.builder().setClaims(claims).setSubject(subject).setIssuedAt(createdDate) 74 | .setExpiration(expirationDate).signWith(SignatureAlgorithm.HS512, secret).compact(); 75 | } 76 | 77 | public Boolean canTokenBeRefreshed(String token) { 78 | return (!isTokenExpired(token) || ignoreTokenExpiration(token)); 79 | } 80 | 81 | public String refreshToken(String token) { 82 | final Date createdDate = clock.now(); 83 | final Date expirationDate = calculateExpirationDate(createdDate); 84 | 85 | final Claims claims = getAllClaimsFromToken(token); 86 | claims.setIssuedAt(createdDate); 87 | claims.setExpiration(expirationDate); 88 | 89 | return Jwts.builder().setClaims(claims).signWith(SignatureAlgorithm.HS512, secret).compact(); 90 | } 91 | 92 | public Boolean validateToken(String token, UserDetails userDetails) { 93 | JwtUserDetails user = (JwtUserDetails) userDetails; 94 | final String username = getUsernameFromToken(token); 95 | return (username.equals(user.getUsername()) && !isTokenExpired(token)); 96 | } 97 | 98 | private Date calculateExpirationDate(Date createdDate) { 99 | return new Date(createdDate.getTime() + expiration * 1000); 100 | } 101 | } 102 | 103 | -------------------------------------------------------------------------------- /spring-boot-react-jwt-auth-login-logout/backend-spring-boot-react-jwt-auth-login-logout/src/main/java/com/in28minutes/fullstack/springboot/jwt/basic/authentication/springbootjwtauthloginlogout/jwt/JwtUnAuthorizedResponseAuthenticationEntryPoint.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.fullstack.springboot.jwt.basic.authentication.springbootjwtauthloginlogout.jwt; 2 | 3 | import java.io.IOException; 4 | import java.io.Serializable; 5 | 6 | import javax.servlet.http.HttpServletRequest; 7 | import javax.servlet.http.HttpServletResponse; 8 | 9 | import org.springframework.security.core.AuthenticationException; 10 | import org.springframework.security.web.AuthenticationEntryPoint; 11 | import org.springframework.stereotype.Component; 12 | 13 | @Component 14 | public class JwtUnAuthorizedResponseAuthenticationEntryPoint implements AuthenticationEntryPoint, Serializable { 15 | 16 | private static final long serialVersionUID = -8970718410437077606L; 17 | 18 | @Override 19 | public void commence(HttpServletRequest request, HttpServletResponse response, 20 | AuthenticationException authException) throws IOException { 21 | response.sendError(HttpServletResponse.SC_UNAUTHORIZED, 22 | "You would need to provide the Jwt Token to Access This resource"); 23 | } 24 | } -------------------------------------------------------------------------------- /spring-boot-react-jwt-auth-login-logout/backend-spring-boot-react-jwt-auth-login-logout/src/main/java/com/in28minutes/fullstack/springboot/jwt/basic/authentication/springbootjwtauthloginlogout/jwt/JwtUserDetails.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.fullstack.springboot.jwt.basic.authentication.springbootjwtauthloginlogout.jwt; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Collection; 5 | import java.util.List; 6 | 7 | import org.springframework.security.core.GrantedAuthority; 8 | import org.springframework.security.core.authority.SimpleGrantedAuthority; 9 | import org.springframework.security.core.userdetails.UserDetails; 10 | 11 | import com.fasterxml.jackson.annotation.JsonIgnore; 12 | 13 | public class JwtUserDetails implements UserDetails { 14 | 15 | private static final long serialVersionUID = 5155720064139820502L; 16 | 17 | private final Long id; 18 | private final String username; 19 | private final String password; 20 | private final Collection authorities; 21 | 22 | public JwtUserDetails(Long id, String username, String password, String role) { 23 | this.id = id; 24 | this.username = username; 25 | this.password = password; 26 | 27 | List authorities = new ArrayList(); 28 | authorities.add(new SimpleGrantedAuthority(role)); 29 | 30 | this.authorities = authorities; 31 | } 32 | 33 | @JsonIgnore 34 | public Long getId() { 35 | return id; 36 | } 37 | 38 | @Override 39 | public String getUsername() { 40 | return username; 41 | } 42 | 43 | @JsonIgnore 44 | @Override 45 | public boolean isAccountNonExpired() { 46 | return true; 47 | } 48 | 49 | @JsonIgnore 50 | @Override 51 | public boolean isAccountNonLocked() { 52 | return true; 53 | } 54 | 55 | @JsonIgnore 56 | @Override 57 | public boolean isCredentialsNonExpired() { 58 | return true; 59 | } 60 | 61 | @JsonIgnore 62 | @Override 63 | public String getPassword() { 64 | return password; 65 | } 66 | 67 | @Override 68 | public Collection getAuthorities() { 69 | return authorities; 70 | } 71 | 72 | @Override 73 | public boolean isEnabled() { 74 | return true; 75 | } 76 | 77 | } 78 | 79 | 80 | -------------------------------------------------------------------------------- /spring-boot-react-jwt-auth-login-logout/backend-spring-boot-react-jwt-auth-login-logout/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | jwt.signing.key.secret=mySecret 2 | jwt.get.token.uri=/authenticate 3 | jwt.refresh.token.uri=/refresh 4 | jwt.http.request.header=Authorization 5 | jwt.token.expiration.in.seconds=604800 -------------------------------------------------------------------------------- /spring-boot-react-jwt-auth-login-logout/backend-spring-boot-react-jwt-auth-login-logout/src/test/java/com/in28minutes/fullstack/springboot/jwt/basic/authentication/springbootreactjwtauthloginlogout/SpringBootReactJwtAuthLoginLogoutApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.in28minutes.fullstack.springboot.jwt.basic.authentication.springbootreactjwtauthloginlogout; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | @RunWith(SpringRunner.class) 9 | @SpringBootTest 10 | public class SpringBootReactJwtAuthLoginLogoutApplicationTests { 11 | 12 | @Test 13 | public void contextLoads() { 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /spring-boot-react-jwt-auth-login-logout/frontend-spring-boot-react-jwt-auth-login-logout/README.md: -------------------------------------------------------------------------------- 1 | Front Code is same as the Basic Authentication Example except for a small change. 2 | 3 | This is an extract from `class LoginComponent extends Component {` in the frontend project for [Basic Authentication](https://github.com/in28minutes/spring-boot-react-fullstack-examples/tree/master/spring-boot-react-basic-auth-login-logout/frontend-spring-boot-react-basic-auth-login-logout) 4 | 5 | ``` 6 | AuthenticationService 7 | .executeBasicAuthenticationService(this.state.username, this.state.password) 8 | .then(() => { 9 | AuthenticationService.registerSuccessfulLogin(this.state.username, this.state.password) 10 | this.props.history.push(`/courses`) 11 | }).catch(() => { 12 | this.setState({ showSuccessMessage: false }) 13 | this.setState({ hasLoginFailed: true }) 14 | }) 15 | 16 | // AuthenticationService 17 | // .executeJwtAuthenticationService(this.state.username, this.state.password) 18 | // .then((response) => { 19 | // AuthenticationService.registerSuccessfulLoginForJwt(this.state.username, response.data.token) 20 | // this.props.history.push(`/courses`) 21 | // }).catch(() => { 22 | // this.setState({ showSuccessMessage: false }) 23 | // this.setState({ hasLoginFailed: true }) 24 | // }) 25 | ``` 26 | 27 | Comment out the Basic Authentication and Un Comment the JWT Authentication Call. 28 | 29 | This is how the code should look like! 30 | 31 | ``` 32 | // AuthenticationService 33 | // .executeBasicAuthenticationService(this.state.username, this.state.password) 34 | // .then(() => { 35 | // AuthenticationService.registerSuccessfulLogin(this.state.username, this.state.password) 36 | // this.props.history.push(`/courses`) 37 | // }).catch(() => { 38 | // this.setState({ showSuccessMessage: false }) 39 | // this.setState({ hasLoginFailed: true }) 40 | // }) 41 | 42 | AuthenticationService 43 | .executeJwtAuthenticationService(this.state.username, this.state.password) 44 | .then((response) => { 45 | AuthenticationService.registerSuccessfulLoginForJwt(this.state.username, response.data.token) 46 | this.props.history.push(`/courses`) 47 | }).catch(() => { 48 | this.setState({ showSuccessMessage: false }) 49 | this.setState({ hasLoginFailed: true }) 50 | }) 51 | ``` --------------------------------------------------------------------------------