建筑物身份管理,包括身份验证和授权? 尝试Stormpath! 我们的REST API和强大的Java SDK支持可以消除您的安全风险,并且可以在几分钟内实现。 注册 ,再也不会建立auth了!
React是用于创建Web应用程序前端的最受欢迎的库之一。 使用Spring Boot 为面向React的应用程序创建CRUD后端比以往任何时候都更加容易。 在本教程中,我们将它们捆绑在一起,然后使用Stormpath添加身份验证和授权协议。
我们将从使用React创建一个静态数据视图开始。 然后,我们将使用Spring Boot创建一个REST后端,进行绑定,并使用Stormpath添加用户安全性。 即使您以前从未使用过React,一切也应该简单明了。
支持这篇文章的源代码可以在这个GitHub repo中找到。
服务前线
通常,React应用程序是使用Node.js来提供服务的,但是如果您是Java开发人员,则可能会对这种Spring Boot方法感到非常满意。
最初,您将整个应用程序放在一个文件index.html
。 要告诉Spring Boot将其用作主页,可以使用@Controller
批注。
@Controller
public class HomeController {@RequestMapping(value = "/")public String index() {return "index.html";}
}
创建一个空目录,并将上面的代码放入src/main/java/tutorial/HomeController.java
。 然后,当您加载站点时,Spring Boot将寻找src/main/resources/static/index.html
。
<!DOCTYPE html>
<html>
<head><title>React + Spring</title>
</head>
<body>
</body>
</html>
创建一个pom.xml
和一个Spring Boot应用程序类。 将以下内容用于您的POM。
<project><modelVersion>4.0.0</modelVersion><groupId>com.example</groupId><artifactId>demo</artifactId><version>0.0.1-SNAPSHOT</version><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>1.4.1.RELEASE</version></parent><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build>
</project>
将以下内容放入src/main/java/tutorial/Application.java
。
@SpringBootApplication
public class Application {public static void main(String[] args) {SpringApplication.run(Application.class, args);}
}
当使用mvn spring-boot:run
启动服务器并访问localhost:8080
您应该看到一个空白页,标题为“ React + Spring”。
删除重启
通常,每次更改前端时都必须重新启动服务器,这很麻烦。 使用Spring Boot的开发人员工具可以使我们解决这个问题。 将以下依赖项添加到您的POM。
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><optional>true</optional>
</dependency>
还要将此配置添加到您的Spring Boot Maven插件中:
<configuration><addResources>true</addResources>
</configuration>
现在,当您更改应用程序或重新编译任何类时,刷新浏览器时它应该会更新。
反应准系统HTML
开始反应! 最基本的React页面包含三件事:根元素,JavaScript导入和脚本标签。
<!DOCTYPE html>
<html>
<head><title>React + Spring</title>
</head>
<body><div id='root'></div><script src="https://fb.me/react-15.0.1.js"></script><script src="https://fb.me/react-dom-15.0.1.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.23/browser.min.js"></script><script type="text/babel"></script>
</body>
</html>
根元素是React将在其中插入视图HTML的位置。 导入引入了三个库-两个用于React本身,另一个用于使用babel翻译我们的视图模板。
注意:为了便于测试,我们使用CDN引入库,但是通常您会使用webpack之类的东西将所有Javascript合并到一个文件中。
现在,将您的React代码放入script标签中。
React基础
如反应教程中的想法所述,您应该通过将接口分解为components
来开始对应用程序进行编码。
<script type="text/babel">
var Employee = React.createClass({});
var EmployeeTable = React.createClass({});
</script>
在这里,您创建了两个-一个用于雇员表,另一个用于雇员条目。 然后,每个组件都需要一个渲染函数,该函数描述要生成HTML。
<script type="text/babel">
var Employee = React.createClass({render: function() {return (<div>employee</div>);}
});
var EmployeeTable = React.createClass({render: function() {return (<div>employee table</div>);}
});
</script>
这是Babel编译器用来将HTML代码转换为正确的React语句的地方。 注意div
标签如何从render
语句返回。
您需要告诉React将父组件HTML插入根元素。 这是使用ReactDOM.render
方法完成的。
<script type="text/babel">
var Employee = React.createClass({render: function() {return (<div>employee</div>);}
});
var EmployeeTable = React.createClass({render: function() {return (<div>employee table</div>);}
});ReactDOM.render(<EmployeeTable />, document.getElementById('root')
);
</script>
通过刷新浏览器,您应该看到您创建的简单文本元素。
要查看插入到根元素中HTML React,可以使用浏览器的检查器(Chrome中的Ctrl-Shift-J)。
将组件捆绑在一起
现在您已经有了组件,让我们将它们绑在一起。 您可以从尝试呈现硬编码的数据开始。 您稍后将使用REST服务器。
在ReactDOM
命令上方,输入以下内容:
var EMPLOYEES = [{name: 'Joe Biden', age: 45, years: 5},{name: 'President Obama', age: 54, years: 8},{name: 'Crystal Mac', age: 34, years: 12},{name: 'James Henry', age: 33, years: 2}
];
然后在实例化表时添加employees={EMPLOYEES}
。
ReactDOM.render(<EmployeeTable employees={EMPLOYEES} />, document.getElementById('root')
);
如您所料,这会将数据传递到名为employees
的变量中。 在EmployeeTable内部,您可以使用this.props
进行访问。 让我们用它来为每个雇员生成一个带有一行的表。
var EmployeeTable = React.createClass({render: function() {var rows = [];this.props.employees.forEach(function(employee) {rows.push(<Employee employee={employee} />);});return (<table><thead><tr><th>Name</th><th>Age</th><th>Years</th></tr></thead><tbody>{rows}</tbody></table>);}
});
这将为数据中的每个元素实例化一个新的Employee类(设置employee
属性)并将其推入数组。 然后{rows}
从子类中放入所需HTML。
现在,您需要做的就是更新Employee上的render方法。
var Employee = React.createClass({render: function() {return (<tr><td>{this.props.employee.name}</td><td>{this.props.employee.age}</td><td>{this.props.employee.years}</td></tr>);}
});
您可以添加Bootstrap以使表看起来不错。 在脚本导入标签的下方添加以下内容:
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
然后用容器div包围主表,并为表元素提供一些Bootstrap类名称。
<div className="container"><table className="table table-striped"><thead><tr><th>Name</th><th>Age</th><th>Years</th></tr></thead><tbody>{rows}</tbody></table>
</div>
刷新浏览器应该可以很好地查看您硬编码的数据!
添加真实数据
要使用来自服务器的数据对象,您需要添加服务器! 使用Spring Boot做到这一点非常简单。 在src/main/java/tutorial/Employee.java
添加以下代码:
@Data
@Entity
public class Employee {private @Id @GeneratedValue Long id;private String name;private int age;private int years;private Employee() {}public Employee(String name, int age, int years) {this.name = name;this.age = age;this.years = years;}
}
这是我们的豆。 注意: @Data
批注来自Project Lombok 。
现在,使用Spring Data JPA创建一个存储库。
public interface EmployeeRepository extends CrudRepository<Employee, Long> {}
要加载数据,请创建一个CommandLineRunner
实现,该实现使用存储库在数据库中创建新记录。
@Component
public class DatabaseLoader implements CommandLineRunner {private final EmployeeRepository repository;@Autowiredpublic DatabaseLoader(EmployeeRepository repository) {this.repository = repository;}@Overridepublic void run(String... strings) throws Exception {this.repository.save(new Employee("Joe Biden", 45, 5));this.repository.save(new Employee("President Obama", 54, 8));this.repository.save(new Employee("Crystal Mac", 34, 12));this.repository.save(new Employee("James Henry", 33, 2));}
}
剩下的唯一事情就是引入依赖关系。 将以下内容添加到pom.xml
将使您的存储库成为REST端点。
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
您还需要包括Project Lombok (可让您忽略为bean创建getter和setter)。
<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.16.10</version><scope>provided</scope>
</dependency>
并且您需要一个数据库(Spring Boot会自动配置该数据库)。 您可以使用H2,它是嵌入式的(即在内存中/不会重新启动)。
<dependency><groupId>com.h2database</groupId><artifactId>h2</artifactId>
</dependency>
就是这样! 如果现在重新启动,您将拥有一个具有数据功能的REST服务器。
映射URL
如果将以下内容添加到src/main/resources/application.properties
则所有REST端点调用都将位于localhost:8080/api
spring.data.rest.basePath=/api
从命令行调用localhost:8080/api/employees
应该会给出您加载的数据的列表。
反应和REST
现在,您需要将数据从REST端点拉入React视图。 您可以使用jQuery完成此操作。 将以下导入添加到HTML:
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
现在创建一个包装器类,该包装器类在其render方法中返回EmployeeTable
。
var App = React.createClass({loadEmployeesFromServer: function () {var self = this;$.ajax({url: "http://localhost:8080/api/employees"}).then(function (data) {self.setState({employees: data._embedded.employees});});},getInitialState: function () {return {employees: []};},componentDidMount: function () {this.loadEmployeesFromServer();},render() {return ( <EmployeeTable employees={this.state.employees}/> );}
});
您必须首先使用getInitialState
进行初始化来设置state
,然后使用componentDidMount
来完成加载所有内容时所需的操作。
现在,将主ReactDOM.render
替换为新类。
ReactDOM.render(<App />, document.getElementById('root') );
刷新后,您应该会看到与以前相同的视图,只是现在正在从服务器加载数据。
互动性
您需要为前端做的最后一件事是交互性。 让我们添加一个delete
按钮,看看它如何工作。
将以下列添加到您的员工渲染中。
<td><button className="btn btn-info" onClick={this.handleDelete}>Delete</button>
</td>
您将在handleDelete
秒钟内编写handleDelete
方法。 在雇员表类中添加另一个标题后,您应该看到每个条目旁边都出现了按钮。
从服务器删除
在将删除请求发送到后端之前,最好添加通知消息。 为此,您可以使用Toastr ,这将允许您显示弹出窗口。 在HTML顶部添加以下内容:
<script src="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/2.1.3/toastr.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/2.1.3/toastr.min.css">
现在,您可以在脚本中使用toastr.error('something went wrong')
类的命令发送消息。
让我们测试一下! 将您的员工类别更改为以下内容:
var Employee = React.createClass({getInitialState: function() {return {display: true };},handleDelete() {var self = this;$.ajax({url: self.props.employee._links.self.href,type: 'DELETE',success: function(result) {self.setState({display: false});},error: function(xhr, ajaxOptions, thrownError) {toastr.error(xhr.responseJSON.message);}});},render: function() {if (this.state.display==false) return null;else return (<tr><td>{this.props.employee.name}</td><td>{this.props.employee.age}</td><td>{this.props.employee.years}</td><td><button className="btn btn-info" onClick={this.handleDelete}>Delete</button></td></tr>);}
});
这将设置一个display
状态,该状态决定是否渲染。 如果成功删除了员工,则此变量设置为true。 handleDelete
方法将删除请求发送到服务器(使用从get请求返回的href)。 如果成功,则将display
设置为false并更新渲染。 否则,Toastr会通知用户发生错误。
尝试删除条目并刷新页面。 它应该保持删除状态。
注意:由于您正在使用内存数据库,因此重新启动服务器将恢复相同的数据。
添加用户身份验证
让我们在React应用程序中添加一项最终功能,即Stormpath,以进行用户身份验证。 您将需要使用Stormpath永久免费的开发人员帐户 。
您需要做的第一件事是将Stormpath应用程序详细信息放在application.properties
。
stormpath.application.href = <your app href>
stormpath.client.apiKey.id = <your api key id>
stormpath.client.apiKey.secret = <your api key secret>
注意:出于安全原因,您不应将Stormpath密钥存储在项目文件中。 而是使用环境变量。 看这里 。
接下来,将Stormpath启动器添加到您的Maven依赖项中。
<dependency><groupId>com.stormpath.spring</groupId><artifactId>stormpath-default-spring-boot-starter</artifactId><version>1.1.2</version>
</dependency>
您还需要将index.html
文件移动到src/main/resources/templates
这是因为Stormpath的Spring Boot启动程序默认使用Thymeleaf模板库。 更改HomeController
以也返回index
。
@Controller
public class HomeController {@RequestMapping(value = "/")public String index() {return "index";}
}
您还需要将您的React代码移动到一个单独的文件中。 这是因为Thymeleaf不喜欢某些角色。 将代码从脚本标签的内部移至src/main/webapp/public/app.js
默认情况下,此文件夹对公众开放。 然后将此脚本导入HTML的底部。
<script type="text/babel" src="/public/app.js"></script>
然后创建一个安全适配器,调用该适配器适用于stormpath()
。
@Configuration
public class Security extends WebSecurityConfigurerAdapter {@Overrideprotected void configure(HttpSecurity http) throws Exception {http.apply(stormpath());}
}
现在,当您重新启动服务器并尝试访问主页时,将出现登录页面提示。
输入您在application.properties
放入的Stormpath应用程序附带的详细信息,应像以前一样将您带到数据视图页面。
注销
您还需要能够注销。 这就像添加一个将帖子发送到/logout
的表单一样简单(Stormpath会默认设置该表单)。
<div class='container'><div id='root'></div><form action="/logout" method="post"><input class="btn btn-danger center-block" type="submit" value="Logout" /></form>
</div>
您可以用Bootstrap容器包围React根元素和表单,以更好地对齐。
单击注销按钮将使您返回到以前的登录屏幕。
设置授权
最后,您只想让具有正确访问权限的用户删除员工。 要锁定内容,可以使用Spring Security的PreAuthorize
注释。 将存储库代码更改为以下内容:
public interface EmployeeRepository extends CrudRepository<Employee, Long> {@PreAuthorize("hasAuthority('ROLE_ADMIN')")@Overridevoid delete(Long aLong);
}
现在,只有具有ROLE_ADMIN权限的用户才能删除。 如果重新启动服务器并尝试单击“删除”,则应收到一条消息,提示“访问被拒绝”。
要为用户提供所需的权限,您需要通过管理控制台将其添加到Stormpath组中。
在此示例中,有一个名为Supervisor的组已附加到相关应用程序。 要与该组集成,只需将ROLE_ADMIN字符串替换为该组的HREF并重新启动。 如果登录的用户是Supervisor组的成员(请参阅帐户),则应允许您删除。
尘埃落定
就像您已经创建了一个以授权和React为前端的兼容CRUD Web应用程序。 希望本教程对您有所帮助! 如果您对集成React,Spring Boot和Stormpath有任何疑问,请发表评论。
要查看使用Spring Boot后端的更完整的React应用程序,请参阅React.js和Spring Data REST 。
建筑物身份管理,包括身份验证和授权? 尝试Stormpath! 我们的REST API和强大的Java SDK支持可以消除您的安全风险,并且可以在几分钟内实现。 注册 ,再也不会建立auth了!
翻译自: https://www.javacodegeeks.com/2016/12/build-crud-application-react-spring-boot-user-authentication.html