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 |
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 | Id |
64 | Description |
65 | Update |
66 | Delete |
67 |
68 |
69 |
70 | {
71 | this.state.courses.map(
72 | course =>
73 |
74 | {course.id} |
75 | {course.description} |
76 | |
77 | |
78 |
79 | )
80 | }
81 |
82 |
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 |
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 |
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 |
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 |
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 |
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 extends GrantedAuthority> 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 extends GrantedAuthority> 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 | ```
--------------------------------------------------------------------------------