javascript
使用Spring Boot和React进行Bootiful开发
“我喜歡編寫身份驗證和授權代碼。” ?從來沒有Java開發人員。 厭倦了一次又一次地建立相同的登錄屏幕? 嘗試使用Okta API進行托管身份驗證,授權和多因素身份驗證。
在過去的幾年中,React受到了很多積極的報導,使其成為Java開發人員的吸引人的前端選擇! 一旦了解了它的工作原理,它就會變得很有意義,并且可以很有趣地進行開發。 不僅如此,而且速度也很快! 如果您一直在關注我,或者已經閱讀了此博客,那么您可能還記得我的《 使用Spring Boot和Angular進行Bootiful開發》教程。 今天,我將向您展示如何構建相同的應用程序,除了這次使用React。 在深入探討之前,讓我們先討論一下React有什么用處,以及我為什么選擇在本文中探索它。
首先,React不是一個成熟的Web框架。 它更像是用于開發UI的工具包,如la GWT。 如果您想發出HTTP請求以從服務器獲取數據,React將不提供任何實用程序。 但是,它確實有一個龐大的生態系統,提供許多庫和組件。 我所說的巨大意味著什么? 這么說:根據npmjs.com , Angular有17,938個軟件包 。 反應幾乎三倍多在42428!
Angular是我的好朋友,已經有很長時間了。 我并沒有放棄我的老朋友采用React。 我只是結交新朋友。 擁有很多具有不同背景和不同見解的朋友,對于人類的觀點來說是件好事!
這篇文章展示了如何將UI和API構建為單獨的應用程序。 您將學習如何使用Spring MVC創建REST端點,如何配置Spring Boot以允許CORS,以及如何創建一個React應用來顯示其數據。 該應用程序將顯示API中的啤酒列表,然后從GIPHY提取與啤酒名稱匹配的GIF。 我還將向您展示如何集成Okta及其OpenID Connect(OIDC)支持以鎖定API并向UI添加身份驗證。
讓我們開始吧!
使用Spring Boot構建API
注意:以下有關構建Spring Boot API的說明與使用Spring Boot和Angular進行Bootiful開發中的說明相同。 為了方便起見,我在下面將它們復制了下來。
要開始使用Spring Boot,請導航到start.spring.io 。 在“搜索依賴項”字段中,選擇以下內容:
- H2 :內存數據庫
- JPA :Java的標準ORM
- 其余存儲庫 :允許您將JPA存儲庫公開為REST端點
- Web :具有Jackson(用于JSON),Hibernate Validator和嵌入式Tomcat的Spring MVC
如果你喜歡命令行更好,你可以使用下面的命令來下載一個demo.zip與文件HTTPie 。
http https://start.spring.io/starter.zip \ dependencies==h2,data-jpa,data-rest,web -d創建一個名為spring-boot-react-example目錄,其中包含server目錄。 將demo.zip的內容demo.zip到server目錄中。
在您喜歡的IDE中打開“服務器”項目,然后運行DemoApplication或使用./mvnw spring-boot:run從命令行啟動它。
在其中創建com.example.demo.beer程序包和Beer.java文件。 此類將是保存您的數據的實體。
package com.example.demo.beer;import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id;@Entity public class Beer {@Id@GeneratedValueprivate Long id;private String name;public Beer() {}public Beer(String name) {this.name = name;}public Long getId() {return id;}public void setId(Long id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}@Overridepublic String toString() {return "Beer{" +"id=" + id +", name='" + name + '\'' +'}';} }添加一個利用Spring Data在此實體上執行CRUD的BeerRepository類。
package com.example.demo.beer;import org.springframework.data.jpa.repository.JpaRepository;interface BeerRepository extends JpaRepository<Beer, Long> { }添加使用此存儲庫的BeerCommandLineRunner并創建一組默認數據。
package com.example.demo.beer;import org.springframework.boot.CommandLineRunner; import org.springframework.stereotype.Component;import java.util.stream.Stream;@Component public class BeerCommandLineRunner implements CommandLineRunner {private final BeerRepository repository;public BeerCommandLineRunner(BeerRepository repository) {this.repository = repository;}@Overridepublic void run(String... strings) throws Exception {// Top beers from https://www.beeradvocate.com/lists/top/Stream.of("Kentucky Brunch Brand Stout", "Good Morning", "Very Hazy", "King Julius","Budweiser", "Coors Light", "PBR").forEach(name ->repository.save(new Beer(name)));repository.findAll().forEach(System.out::println);} }重建您的項目,您應該會在終端上看到印刷的啤酒清單。
a添加@RepositoryRestResource注釋BeerRepository揭露其所有CRUD操作的REST端點。
import org.springframework.data.rest.core.annotation.RepositoryRestResource;@RepositoryRestResource interface BeerRepository extends JpaRepository<Beer, Long> { }添加一個BeerController類來創建一個端點,該端點過濾出的啤酒數量少于大啤酒。
package com.example.demo.beer;import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController;import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.stream.Collectors;@RestController public class BeerController {private BeerRepository repository;public BeerController(BeerRepository repository) {this.repository = repository;}@GetMapping("/good-beers")public Collection<Beer> goodBeers() {return repository.findAll().stream().filter(this::isGreat).collect(Collectors.toList());}private boolean isGreat(Beer beer) {return !beer.getName().equals("Budweiser") &&!beer.getName().equals("Coors Light") &&!beer.getName().equals("PBR");} }重新構建您的應用程序并導航到http://localhost:8080/good-beers 。 您應該在瀏覽器中看到優質啤酒的列表。
使用HTTPie時,您也應該在終端窗口中看到相同的結果。
http localhost:8080/good-beers使用Create React App創建一個項目
這些天來,創建API似乎很容易,這在很大程度上要歸功于Spring Boot。 在本部分中,我希望向您展示使用React創建UI也非常容易。 如果您按照以下步驟操作,則將創建一個新的React應用,從API獲取啤酒名稱和圖像,并創建用于顯示數據的組件。
要創建一個React項目,請確保您已安裝Node.js , Create React App和Yarn 。
npm install -g create-react-app@1.4.3在終端窗口中,cd進入spring-boot-react-example目錄的根目錄并運行以下命令。 該命令將創建一個具有TypeScript支持的新React應用程序。
create-react-app client --scripts-version=react-scripts-ts運行此過程之后,您將擁有一個新的client目錄,其中安裝了所有必需的依賴項。 為了驗證一切正常,將cd進入client目錄并運行yarn start 。 如果一切正常,您應該在瀏覽器中看到以下內容。
到目前為止,您已經創建了一個good-beers API和一個React應用程序,但是尚未創建UI來顯示API中的啤酒列表。 為此,請打開client/src/App.tsx并添加componentDidMount()方法。
componentDidMount() {this.setState({isLoading: true});fetch('http://localhost:8080/good-beers').then(response => response.json()).then(data => this.setState({beers: data, isLoading: false})); }React的組件生命周期將調用componentDidMount()方法。 上面的代碼使用fetch ,這是XMLHttpRequest的現代替代。 根據caniuse.com,大多數瀏覽器均支持該功能 。
您會看到它使用響應數據設置了beers狀態。 要初始化此組件的狀態,您需要重寫構造函數。
constructor(props: any) {super(props);this.state = {beers: [],isLoading: false}; }為此,您需要將參數類型添加到類簽名中。 下面的代碼顯示了此時App類頂部的外觀。
class App extends React.Component<{}, any> {constructor(props: any) {super(props);this.state = {beers: [],isLoading: false};}// componentDidMount() and render() }更改render()方法以具有以下JSX。 JSX是Facebook的類XML語法,可通過JavaScript呈現HTML。
render() {const {beers, isLoading} = this.state;if (isLoading) {return <p>Loading...</p>;}return (<div className="App"><div className="App-header"><img src={logo} className="App-logo" alt="logo" /><h2>Welcome to React</h2></div><div><h2>Beer List</h2>{beers.map((beer: any) =><div key={beer.id}>{beer.name}</div>)}</div></div>); }如果在瀏覽器中查看http://localhost:3000 ,則會看到“正在加載...”消息。 如果您在瀏覽器的控制臺中查看,可能會看到有關CORS的問題。
Failed to load http://localhost:8080/good-beers: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:3000' is therefore not allowed access.要解決此問題,您需要將Spring Boot配置為允許從http://localhost:3000進行跨域訪問。
為Spring Boot配置CORS
在服務器項目中,打開server/src/main/java/com/example/demo/beer/BeerController.java并添加@CrossOrigin批注以啟用來自客戶端的跨域資源共享(CORS)( http://localhost:3000 )。
import org.springframework.web.bind.annotation.CrossOrigin; ...@GetMapping("/good-beers")@CrossOrigin(origins = "http://localhost:3000")public Collection goodBeers() {進行了這些更改之后,重新啟動服務器,刷新瀏覽器,您應該能夠從Spring Boot API中看到啤酒列表。
創建一個BeerList組件
為了使此應用程序更易于維護,請將啤酒清單的獲取和呈現從App.tsx到其自己的BeerList組件。 創建src/BeerList.tsx并使用App.tsx的代碼填充它。
import * as React from 'react';class BeerList extends React.Component<{}, any> {constructor(props: any) {super(props);this.state = {beers: [],isLoading: false};}componentDidMount() {this.setState({isLoading: true});fetch('http://localhost:8080/good-beers').then(response => response.json()).then(data => this.setState({beers: data, isLoading: false}));}render() {const {beers, isLoading} = this.state;if (isLoading) {return <p>Loading...</p>;}return (<div><h2>Beer List</h2>{beers.map((beer: any) =><div key={beer.id}>{beer.name}</div>)}</div>);} }export default BeerList;然后更改client/src/App.tsx ,使其僅包含一個外殼和對<BeerList/>的引用。
import * as React from 'react'; import './App.css'; import BeerList from './BeerList';const logo = require('./logo.svg');class App extends React.Component<{}, any> {render() {return (<div className="App"><div className="App-header"><img src={logo} className="App-logo" alt="logo"/><h2>Welcome to React</h2></div><BeerList/></div>);} }export default App;創建一個GiphyImage組件
為了使其看起來更好一點,添加GIPHY組件以根據啤酒的名稱獲取圖像。 創建client/src/GiphyImage.tsx并將以下代碼放入其中。
import * as React from 'react';interface GiphyImageProps {name: string; }class GiphyImage extends React.Component<GiphyImageProps, any> {constructor(props: GiphyImageProps) {super(props);this.state = {giphyUrl: '',isLoading: false};}componentDidMount() {const giphyApi = '//api.giphy.com/v1/gifs/search?api_key=dc6zaTOxFJmzC&limit=1&q=';fetch(giphyApi + this.props.name).then(response => response.json()).then(response => {if (response.data.length > 0) {this.setState({giphyUrl: response.data[0].images.original.url});} else {// dancing cat for no images foundthis.setState({giphyUrl: '//media.giphy.com/media/YaOxRsmrv9IeA/giphy.gif'});}this.setState({isLoading: false});});}render() {const {giphyUrl, isLoading} = this.state;if (isLoading) {return <p>Loading image...</p>;}return (<img src={giphyUrl} alt={this.props.name} width="200"/>);} }export default GiphyImage;更改BeerList.tsx的render()方法以使用此組件。
import GiphyImage from './GiphyImage'; ... render() {const {beers, isLoading} = this.state;if (isLoading) {return <p>Loading...</p>;}return (<div><h2>Beer List</h2>{beers.map((beer: any) =><div key={beer.id}>{beer.name}<br/><GiphyImage name={beer.name}/></div>)}</div>); }結果應類似于以下帶有圖像的啤酒名稱列表。
您剛剛創建了一個React應用,該應用使用跨域請求與Spring Boot API進行通訊。 恭喜你!
添加PWA支持
Create React App開箱即用地支持漸進式Web應用程序(PWA)。 要了解其集成方式,請打開client/README.md并搜索“制作漸進式Web應用程序”。
要查看其工作方式,請在client目錄中運行yarn build 。 該命令完成后,您將看到類似以下的消息。
The build folder is ready to be deployed. You may serve it with a static server:yarn global add serveserve -s build運行建議的命令,您應該能夠打開瀏覽器以查看http://localhost:5000 。 您的瀏覽器可能會在其控制臺中顯示CORS錯誤,因此BeerController.java再次打開BeerController.java并調整其允許的來源以允許端口5000。
@CrossOrigin(origins = {"http://localhost:3000", "http://localhost:5000"})重新啟動服務器,并且http://localhost:5000應該加載啤酒名稱和圖像。
我在Chrome中進行了Lighthouse審核,發現此應用目前僅獲得73/100的評分。
您會在上面的屏幕截圖中注意到“清單沒有至少512px的圖標”。 聽起來很容易修復。 您可以從此頁面下載512像素的免費啤酒圖標。
注意:此圖標由Freepik從www.flaticon.com制作 。 它由CC 3.0 BY許可。
將下載的beer.png復制到client/public 。 修改client/public/manifest.json以具有特定于此應用程序的名稱,并添加512像素的圖標。
{"short_name": "Beer","name": "Good Beer","icons": [{"src": "favicon.ico","sizes": "192x192","type": "image/png"},{"src": "beer.png","sizes": "512x512","type": "image/png"}],"start_url": "./index.html","display": "standalone","theme_color": "#000000","background_color": "#ffffff" }進行此更改后,我的PWA得分達到82燈塔評分。 該報告最突出的抱怨是我沒有使用HTTPS。 為了查看該應用使用HTTPS時的評分,我將其部署到Pivotal Cloud Foundry和Heroku 。 我很高興發現它在兩個平臺上的得分都為💯。
要閱讀我用于部署所有內容的腳本,請參閱本文附帶的GitHub存儲庫中的cloudfoundry.sh和heroku.sh 。 我非常感謝@starbuxman和@codefinger在創建它們方面的幫助!
使用Okta添加身份驗證
您可能會想,“這很酷,很容易看出人們為什么愛上React。” 試用過后,您可能會愛上另一個工具:使用Okta進行身份驗證! 為什么選擇Okta? 因為您可以免費獲得7,000個每月活躍用戶 ! 值得一試,特別是當您看到將auth添加到Spring Boot和使用Okta進行React很容易時。
Okta Spring啟動啟動器
要鎖定后端,可以使用Okta的Spring Boot Starter 。 要集成此啟動器,請將以下依賴項添加到server/pom.xml :
<dependency><groupId>com.okta.spring</groupId><artifactId>okta-spring-boot-starter</artifactId><version>0.2.0</version> </dependency>您還需要添加<dependencyManagement>部分以升級Spring Security的OAuth支持。
<dependencyManagement><dependencies><dependency><groupId>org.springframework.security.oauth</groupId><artifactId>spring-security-oauth2</artifactId><version>2.2.0.RELEASE</version></dependency></dependencies> </dependencyManagement>注意: 有一個問題與1563的Spring開機啟動它不使用Spring Boot的DevTools工作。
現在,您需要配置服務器以使用Okta進行身份驗證。 為此,您需要在Okta中創建OIDC應用。
在Okta中創建OIDC應用
登錄到您的1563開發者帳戶(或者注冊 ,如果你沒有一個帳戶)并導航到應用程序 > 添加應用程序 。 單擊“ 單頁應用程序” ,再單擊“ 下一步” ,然后為該應用程序命名。 將localhost:8080所有實例更改為localhost:3000 ,然后單擊完成 。
將客戶端ID復制到您的server/src/main/resources/application.properties文件中。 在其中時,添加與您的Okta域匹配的okta.oauth2.issuer屬性。 例如:
okta.oauth2.issuer=https://{yourOktaDomain}.com/oauth2/default okta.oauth2.clientId={clientId}注意: { yourOktaDomain }的值應類似于dev-123456.oktapreview.com 。 確保在值中不包括-admin !
更新server/src/main/java/com/okta/developer/demo/DemoApplication.java以將其啟用為資源服務器。
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;@EnableResourceServer @SpringBootApplication進行了這些更改之后,您應該能夠重新啟動服務器,并在嘗試導航到http:// localhost:8080時看到訪問被拒絕。
Okta的React支持
Okta的React SDK允許您將OIDC集成到React應用程序中。 您可以在npmjs.com上了解有關Okta的React SDK的更多信息。 要安裝,請運行以下命令:
yarn add @okta/okta-react react-router-dom yarn add -D @types/react-router-domOkta的React SDK依賴于react-router ,因此需要安裝react-router-dom的原因。 在client/src/App.tsx配置路由是一種常見的做法,因此,用下面的TypeScript替換其代碼,該TypeScript使用Okta設置身份驗證。
import * as React from 'react'; import './App.css'; import Home from './Home'; import { BrowserRouter as Router, Route } from 'react-router-dom'; import { Security, ImplicitCallback } from '@okta/okta-react';const config = {issuer: 'https://{yourOktaDomain}.com/oauth2/default',redirectUri: window.location.origin + '/implicit/callback',clientId: '{clientId}' };export interface Auth {login(): {};logout(): {};isAuthenticated(): boolean;getAccessToken(): string; }class App extends React.Component {render() {return (<Router><Securityissuer={config.issuer}client_id={config.clientId}redirect_uri={config.redirectUri}><Route path="/" exact={true} component={Home}/><Route path="/implicit/callback" component={ImplicitCallback}/></Security></Router>);} }export default App;創建client/src/Home.tsx以包含App.tsx以前包含的應用程序外殼。 此類呈現應用程序外殼,登錄/注銷按鈕以及<BeerList/>如果已通過身份驗證)。
import * as React from 'react'; import './App.css'; import BeerList from './BeerList'; import { withAuth } from '@okta/okta-react'; import { Auth } from './App';const logo = require('./logo.svg');interface HomeProps {auth: Auth; }interface HomeState {authenticated: boolean; }export default withAuth(class Home extends React.Component<HomeProps, HomeState> {constructor(props: HomeProps) {super(props);this.state = {authenticated: false};this.checkAuthentication = this.checkAuthentication.bind(this);this.checkAuthentication();}async checkAuthentication() {const isAuthenticated = await this.props.auth.isAuthenticated();const {authenticated} = this.state;if (isAuthenticated !== authenticated) {this.setState({authenticated: isAuthenticated});}}componentDidUpdate() {this.checkAuthentication();}render() {const {authenticated} = this.state;let body = null;if (authenticated) {body = (<div className="Buttons"><button onClick={this.props.auth.logout}>Logout</button><BeerList auth={this.props.auth}/></div>);} else {body = (<div className="Buttons"><button onClick={this.props.auth.login}>Login</button></div>);}return (<div className="App"><div className="App-header"><img src={logo} className="App-logo" alt="logo"/><h2>Welcome to React</h2></div>{body}</div>);} });如果您在瀏覽器中查看React應用,則可能會看到類似以下的錯誤:
./src/Home.tsx (4,26): error TS7016: Could not find a declaration file for module '@okta/okta-react'. '/Users/mraible/spring-boot-react-example/client/node_modules/@okta/okta-react/dist/index.js' implicitly has an 'any' type.Try `npm install @types/@okta/okta-react` if it exists or add a new declaration (.d.ts) filecontaining `declare module '@okta/okta-react';`使用以下聲明創建client/src/okta.d.ts來解決此問題。
declare module '@okta/okta-react';重新啟動客戶端,您將看到在BeerList組件上有一些工作要做。
./src/Home.tsx (44,21): error TS2339: Property 'auth' does not exist on type 'IntrinsicAttributes & IntrinsicClassAttributes<BeerList> & Readonly<{ children?: ReactNode; }> & ...'.在client/src/BeerList.tsx ,通過創建一個傳遞到類簽名中的BeerListProps接口,將auth屬性添加到道具中。
import { Auth } from './App';interface BeerListProps {auth: Auth; }interface BeerListState {beers: Array<{}>;isLoading: boolean; }class BeerList extends React.Component<BeerListProps, BeerListState> {... }將以下CSS規則添加到client/src/App.css以使“登錄/注銷”按鈕更加可見。
.Buttons {margin-top: 10px; }.Buttons button {font-size: 1em; }您的瀏覽器后臺,請檢查以下內容。
單擊按鈕登錄后,輸入用于創建Okta Developer帳戶的電子郵件和密碼。 當它將您重定向回您的應用程序時,您可能會在瀏覽器的控制臺中看到“正在加載...”和CORS錯誤。
發生此錯誤是因為Spring的@CrossOrigin在Spring Security中不能很好地發揮作用。 要解決此問題,請在DemoApplication.java的主體中添加一個simpleCorsFilter bean。
package com.example.demo;import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.core.Ordered; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.filter.CorsFilter;import java.util.Arrays; import java.util.Collections;@EnableResourceServer @SpringBootApplication public class DemoApplication {public static void main(String[] args) {SpringApplication.run(DemoApplication.class, args);}@Beanpublic FilterRegistrationBean simpleCorsFilter() {UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();CorsConfiguration config = new CorsConfiguration();config.setAllowCredentials(true);config.setAllowedOrigins(Arrays.asList("http://localhost:3000", "http://localhost:5000"));config.setAllowedMethods(Collections.singletonList("*"));config.setAllowedHeaders(Collections.singletonList("*"));source.registerCorsConfiguration("/**", config);FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));bean.setOrder(Ordered.HIGHEST_PRECEDENCE);return bean;} }要使其在客戶端上都能正常工作,請修改client/src/BeerList.tsx的componentDidMount()方法以設置授權標頭。
async componentDidMount() {this.setState({isLoading: true});try {const response = await fetch('http://localhost:8080/good-beers', {headers: {Authorization: 'Bearer ' + await this.props.auth.getAccessToken()}});const data = await response.json();this.setState({beers: data, isLoading: false});} catch (err) {this.setState({error: err});} }您還需要在BeerListState接口中添加error 。
interface BeerListState {beers: Array<{}>;isLoading: boolean;error: string; }更改構造函數,以便將error初始化為空字符串。
this.state = {beers: [],isLoading: false,error: '' };然后更改render()方法以在發生錯誤時顯示錯誤。
render() {const {beers, isLoading, error} = this.state;if (isLoading) {return <p>Loading ...</p>;}if (error.length > 0) {return <p>Error: {error}</p>;}return (...) }現在您應該能夠以經過身份驗證的用戶身份查看啤酒清單。
如果有效,那么恭喜!
清理那些TypeScript警告
您可能會注意到,瀏覽器的控制臺報告了一些TypeScript警告。
./src/BeerList.tsx [16, 22]: Type declaration of 'any' loses type-safety. Consider replacing it with a more precise type, the empty type ('{}'), or suppress this occurrence. [52, 27]: Type declaration of 'any' loses type-safety. Consider replacing it with a more precise type, the empty type ('{}'), or suppress this occurrence. ./src/GiphyImage.tsx [7, 59]: Type declaration of 'any' loses type-safety. Consider replacing it with a more precise type, the empty type ('{}'), or suppress this occurrence.要解決第一個問題,請更改client/src/BeerList.tsx ,使其構造函數如下:
constructor(props: BeerListProps) {... }對于第二個問題,在client/src/BeerList.tsx創建一個Beer接口。 將其放在頂部的其他接口旁邊。
interface Beer {id: number;name: string; }然后將{ beers.map((beer: any) =>更改為{ beers.map((beer: Beer) => 。
第三個問題可以通過在client/src/GiphyImage.tsx創建一個新的GiphyImageState接口來定義狀態屬性來解決。
interface GiphyImageState {giphyUrl: string;isLoading: boolean; }class GiphyImage extends React.Component<GiphyImageProps, GiphyImageState> {... }進行了這些更改之后,您應該擺脫TypeScript警告。
了解有關Spring Boot和React的更多信息
要了解有關React,Spring Boot或Okta的更多信息,請查看以下資源:
- Eric Vicenti的React工作坊簡介 -強烈建議您學習React!
- 我與Deepu K Sasidharan 在比利時Devoxx上進行的Angular vs React Smackdown演講
- Robin Wieruch 如何在React中獲取數據
- 15分鐘內通過用戶身份驗證構建React應用程序
- 使用身份驗證構建預先應用
- 使用Okta的React SDK創建自定義登錄表單
您可以在GitHub上找到與本文相關的源代碼。 主要示例(無身份驗證)在master分支中,而Okta集成在okta分支中。 要簽出本地計算機上的Okta分支,請運行以下命令。
git clone git@github.com:oktadeveloper/spring-boot-react-example.git git checkout okta如果您發現任何問題,請在下面添加評論,我們將盡力為您提供幫助。 如果您喜歡本教程,希望您在Twitter上關注我 。 要獲得更多類似此類文章的通知,請關注@oktadev 。
“我喜歡編寫身份驗證和授權代碼。” ?從來沒有Java開發人員。 厭倦了一次又一次地建立相同的登錄屏幕? 嘗試使用Okta API進行托管身份驗證,授權和多因素身份驗證。
翻譯自: https://www.javacodegeeks.com/2018/01/bootiful-development-spring-boot-react.html
總結
以上是生活随笔為你收集整理的使用Spring Boot和React进行Bootiful开发的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 安卓双人联机游戏(安卓双人)
- 下一篇: 安卓卡顿解决办法(安卓卡顿解决)