├── .gitignore ├── README.md ├── pom.xml └── src └── main ├── java └── tutorial │ ├── Application.java │ ├── DatabaseLoader.java │ ├── Employee.java │ ├── EmployeeRepository.java │ ├── HomeController.java │ └── Security.java ├── resources ├── application.properties └── templates │ └── index.html └── webapp └── public └── app.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### JetBrains template 3 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 4 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 5 | 6 | # User-specific stuff: 7 | .idea/ 8 | 9 | ## File-based project format: 10 | *.iml 11 | *.iws 12 | 13 | ## Plugin-specific files: 14 | 15 | # IntelliJ 16 | /out/ 17 | 18 | # mpeltonen/sbt-idea plugin 19 | .idea_modules/ 20 | 21 | # JIRA plugin 22 | atlassian-ide-plugin.xml 23 | 24 | # Crashlytics plugin (for Android Studio and IntelliJ) 25 | com_crashlytics_export_strings.xml 26 | crashlytics.properties 27 | crashlytics-build.properties 28 | fabric.properties 29 | ### Maven template 30 | target/ 31 | pom.xml.tag 32 | pom.xml.releaseBackup 33 | pom.xml.versionsBackup 34 | pom.xml.next 35 | release.properties 36 | dependency-reduced-pom.xml 37 | buildNumber.properties 38 | .mvn/timing.properties 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #Stormpath is Joining Okta 2 | We are incredibly excited to announce that [Stormpath is joining forces with Okta](https://stormpath.com/blog/stormpaths-new-path?utm_source=github&utm_medium=readme&utm-campaign=okta-announcement). Please visit [the Migration FAQs](https://stormpath.com/oktaplusstormpath?utm_source=github&utm_medium=readme&utm-campaign=okta-announcement) for a detailed look at what this means for Stormpath users. 3 | 4 | We're available to answer all questions at [support@stormpath.com](mailto:support@stormpath.com). 5 | 6 | # React App with Spring Boot 7 | 8 | This is the code developed in a [Stormpath blog post on creating a React App using Spring Boot](https://stormpath.com/blog/crud-application-react-spring-boot-user-authentication). 9 | 10 | ### Requirements 11 | 12 | - Maven 13 | - JDK 7 14 | 15 | ### Configuration 16 | 17 | First you need to edit `application.properties` and put in your Stormpath application keys. 18 | 19 | Also to enable deletion you need to either delete the `void delete()` method in `EmployeeRepository.java` 20 | or replace 'ROLE_ADMIN' with the URL of the Stormpath Group you are using to authorize. 21 | 22 | ### Running 23 | 24 | To build and start the server simply type 25 | 26 | ```sh 27 | $ mvn spring-boot:run 28 | ``` 29 | 30 | from the root directory. 31 | 32 | ### Using 33 | 34 | Browse to `localhost:8080` to see the application in action. 35 | 36 | The database is in memory so rebooting should reset the data. 37 | 38 | ### License 39 | 40 | MIT 41 | 42 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | com.example 6 | demo 7 | 0.0.1-SNAPSHOT 8 | 9 | 10 | org.springframework.boot 11 | spring-boot-starter-parent 12 | 1.4.1.RELEASE 13 | 14 | 15 | 16 | 17 | org.springframework.boot 18 | spring-boot-starter-web 19 | 20 | 21 | org.springframework.boot 22 | spring-boot-starter-data-rest 23 | 24 | 25 | org.springframework.boot 26 | spring-boot-starter-data-jpa 27 | 28 | 29 | org.projectlombok 30 | lombok 31 | 1.16.10 32 | provided 33 | 34 | 35 | org.springframework.boot 36 | spring-boot-devtools 37 | true 38 | 39 | 40 | com.h2database 41 | h2 42 | 43 | 44 | com.stormpath.spring 45 | stormpath-default-spring-boot-starter 46 | 1.1.2 47 | 48 | 49 | 50 | 51 | 52 | 53 | org.springframework.boot 54 | spring-boot-maven-plugin 55 | 56 | true 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /src/main/java/tutorial/Application.java: -------------------------------------------------------------------------------- 1 | package tutorial; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class Application { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(Application.class, args); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/tutorial/DatabaseLoader.java: -------------------------------------------------------------------------------- 1 | package tutorial; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.boot.CommandLineRunner; 5 | import org.springframework.stereotype.Component; 6 | 7 | @Component 8 | public class DatabaseLoader implements CommandLineRunner { 9 | 10 | private final EmployeeRepository repository; 11 | 12 | @Autowired 13 | public DatabaseLoader(EmployeeRepository repository) { 14 | this.repository = repository; 15 | } 16 | 17 | @Override 18 | public void run(String... strings) throws Exception { 19 | this.repository.save(new Employee("Joe Biden", 45, 5)); 20 | this.repository.save(new Employee("President Obama", 54, 8)); 21 | this.repository.save(new Employee("Crystal Mac", 34, 12)); 22 | this.repository.save(new Employee("James Henry", 33, 2)); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/tutorial/Employee.java: -------------------------------------------------------------------------------- 1 | package tutorial; 2 | 3 | import lombok.Data; 4 | 5 | import javax.persistence.Entity; 6 | import javax.persistence.GeneratedValue; 7 | import javax.persistence.Id; 8 | 9 | @Data 10 | @Entity 11 | public class Employee { 12 | 13 | private @Id @GeneratedValue Long id; 14 | private String name; 15 | private int age; 16 | private int years; 17 | 18 | private Employee() {} 19 | 20 | public Employee(String name, int age, int years) { 21 | this.name = name; 22 | this.age = age; 23 | this.years = years; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/tutorial/EmployeeRepository.java: -------------------------------------------------------------------------------- 1 | package tutorial; 2 | 3 | import org.springframework.data.repository.CrudRepository; 4 | import org.springframework.security.access.prepost.PreAuthorize; 5 | 6 | public interface EmployeeRepository extends CrudRepository { 7 | 8 | @PreAuthorize("hasAuthority('ROLE_ADMIN')") 9 | @Override 10 | void delete(Long aLong); 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/tutorial/HomeController.java: -------------------------------------------------------------------------------- 1 | package tutorial; 2 | 3 | import org.springframework.stereotype.Controller; 4 | import org.springframework.web.bind.annotation.RequestMapping; 5 | 6 | @Controller 7 | public class HomeController { 8 | 9 | @RequestMapping(value = "/") 10 | public String index() { 11 | return "index"; 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/tutorial/Security.java: -------------------------------------------------------------------------------- 1 | package tutorial; 2 | 3 | import org.springframework.context.annotation.Configuration; 4 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 5 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 6 | 7 | import static com.stormpath.spring.config.StormpathWebSecurityConfigurer.stormpath; 8 | 9 | @Configuration 10 | public class Security extends WebSecurityConfigurerAdapter { 11 | @Override 12 | protected void configure(HttpSecurity http) throws Exception { 13 | http.apply(stormpath()); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.data.rest.basePath=/api 2 | 3 | stormpath.application.href = 4 | stormpath.client.apiKey.id = 5 | stormpath.client.apiKey.secret = 6 | -------------------------------------------------------------------------------- /src/main/resources/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | React + Spring 6 | 7 | 8 | 9 | 10 |
11 |
12 |
13 | 14 |
15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /src/main/webapp/public/app.js: -------------------------------------------------------------------------------- 1 | var Employee = React.createClass({ 2 | 3 | getInitialState: function() { 4 | return {display: true }; 5 | }, 6 | handleDelete() { 7 | var self = this; 8 | $.ajax({ 9 | url: self.props.employee._links.self.href, 10 | type: 'DELETE', 11 | success: function(result) { 12 | self.setState({display: false}); 13 | }, 14 | error: function(xhr, ajaxOptions, thrownError) { 15 | toastr.error(xhr.responseJSON.message); 16 | } 17 | }); 18 | }, 19 | render: function() { 20 | 21 | if (this.state.display==false) return null; 22 | else return ( 23 | 24 | {this.props.employee.name} 25 | {this.props.employee.age} 26 | {this.props.employee.years} 27 | 28 | 29 | 30 | 31 | ); 32 | } 33 | }); 34 | 35 | var EmployeeTable = React.createClass({ 36 | 37 | render: function() { 38 | 39 | var rows = []; 40 | this.props.employees.forEach(function(employee) { 41 | rows.push( 42 | ); 43 | }); 44 | 45 | return ( 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | {rows} 56 |
NameAgeYearsDelete
57 | ); 58 | } 59 | }); 60 | 61 | var App = React.createClass({ 62 | 63 | loadEmployeesFromServer: function() { 64 | 65 | var self = this; 66 | $.ajax({ 67 | url: "http://localhost:8080/api/employees", 68 | }).then(function(data) { 69 | self.setState({ employees: data._embedded.employees }); 70 | }); 71 | 72 | }, 73 | 74 | getInitialState: function() { 75 | return { employees: [] }; 76 | }, 77 | 78 | componentDidMount: function() { 79 | this.loadEmployeesFromServer(); 80 | }, 81 | 82 | render() { 83 | return ( ); 84 | } 85 | }); 86 | 87 | ReactDOM.render(, document.getElementById('root') ); 88 | --------------------------------------------------------------------------------