使用Spring Boot和Vue进行有益的开发

“我喜欢编写身份验证和授权代码。” 〜从来没有Java开发人员。 厌倦了一次又一次地建立相同的登录屏幕? 尝试使用Okta API进行托管身份验证,授权和多因素身份验证。

Vue是一个Web框架,由于它的精简和刻薄,最近引起了很多关注。 它的基准框架成本约为4万,被称为简约Web框架。 随着最近对Web性能的关注以及移动优先,移动快速的关注,Vue变得越来越流行也就不足为奇了。 如果您花时间学习AngularJS,很可能会在Vue.js中找到老朋友。

Spring Boot是Java生态系统中我最喜欢的框架之一。 是的,我有偏见。 自2004年以来,我就一直是Spring Framework的爱好者。能够使用Spring MVC编写Java Webapp真是太好了,但是大多数人都使用XML进行配置。 尽管Spring支持JavaConfig,但直到Spring Boot(在2014年)才真正起步。 如今,您再也看不到Spring教程,该教程向您展示了如何使用XML进行配置。 做得好,Spring Boot团队!

我之所以写本教程,是因为我是Vue的忠实拥护者。 如果您了解我,就会知道我是一个Web框架爱好者。 也就是说,我是Web框架的忠实拥护者。 就像NBA球迷有一些喜欢的球员一样,我也有一些喜欢的框架。 Vue最近成为其中之一,我想向您展示原因。

在本文中,我将向您展示如何使用Spring Data JPA和Hibernate构建Spring Boot API。 然后,我将向您展示如何创建Vue PWA并对其进行自定义以显示API中的数据。 然后,您将添加一些动画gif,一些认证,并祝您玩得开心!

使用Spring Boot构建REST API

要开始使用Spring Boot,请导航至start.spring.io并选择版本2.1.1+。 在“搜索依赖项”字段中,选择以下内容:

  • H2 :内存数据库
  • Lombok(Lombok) :因为没有人喜欢生成(甚至更糟糕的是编写!)getter和setter。
  • JPA :Java的标准ORM
  • 其余存储库 :允许您将JPA存储库公开为REST端点
  • Web :具有Jackson(用于JSON),Hibernate Validator和嵌入式Tomcat的Spring MVC
引导发展

如果您更喜欢命令行,请安装HTTPie并运行以下命令以下载demo.zip

http https://start.spring.io/starter.zip dependencies==h2,lombok,data-jpa,data-rest,web \
  packageName==com.okta.developer.demo -d

创建一个名为spring-boot-vue-example 。 将demo.zip的内容demo.zip到其server目录中。

mkdir spring-boot-vue-example
unzip demo.zip -d spring-boot-vue-example/server

在您喜欢的IDE中打开“服务器”项目,然后运行DemoApplication或使用./mvnw spring-boot:run从命令行启动它。

创建一个com.okta.developer.demo.beer程序包和其中的Beer.java文件。 此类将是保存您的数据的实体。

package com.okta.developer.demo.beer;

import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.NonNull;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Data
@NoArgsConstructor
@Entity
class Beer {

    public Beer(String name) {
        this.name = name;
    }

    @Id
    @GeneratedValue
    private Long id;

    @NonNull
    private String name;
}

添加一个利用Spring Data在此实体上执行CRUD的BeerRepository类。

package com.okta.developer.demo.beer;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;

@RepositoryRestResource
interface BeerRepository extends JpaRepository<Beer, Long> {
}

添加@RepositoryRestResource注释BeerRepository暴露了其所有的CRUD操作的REST端点。

添加使用此存储库的BeerCommandLineRunner并创建一组默认数据。

package com.okta.developer.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;
    }

    @Override
    public void run(String... strings) throws Exception {
        // Top beers from https://www.beeradvocate.com/lists/us, November 2018
        Stream.of("Kentucky Brunch Brand Stout", "Marshmallow Handjee", "Barrel-Aged Abraxas",
            "Hunahpu's Imperial Stout", "King Julius", "Heady Topper",
            "Budweiser", "Coors Light", "PBR").forEach(name ->
            repository.save(new Beer(name))
        );
        repository.findAll().forEach(System.out::println);
    }
}

重新启动您的应用程序,您应该会在终端上看到印刷的啤酒列表。

引导发展

添加一个BeerController类来创建一个端点,该端点过滤出的啤酒数量少于大啤酒。

package com.okta.developer.demo.beer;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Collection;
import java.util.stream.Collectors;

@RestController
public class BeerController {
    private BeerRepository repository;

    public BeerController(BeerRepository repository) {
        this.repository = repository;
    }

    @GetMapping("/good-beers")
    public Collection
   
   
    
     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 :8080/good-beers

使用Vue CLI创建项目

这些天来,创建API似乎很容易,这在很大程度上要归功于Spring Boot。 在本节中,我希望向您展示使用Vue创建UI也非常简单。 我还将向您展示如何使用TypeScript开发Vue应用。 如果您按照以下步骤操作,则将创建一个新的Vue应用,从API获取啤酒名称和图像,并创建组件以显示其数据。

要创建Vue项目,请确保已安装Node.jsVue CLI 3 。 创建本教程时,我使用了Node 11.3.0。

npm install -g @vue/cli@3.2.1

在终端窗口中,使用cd进入spring-boot-vue-example目录的根目录并运行以下命令。 该命令将创建一个新的Vue应用程序并提示您选择。

vue create client

当提示您选择礼物时,选择手动选择功能

引导发展

检查TypeScriptPWARouter功能。 选择其余问题的默认值(按Enter )。

在终端窗口中,cd进入client目录,然后在您喜欢的编辑器中打开package.json 。 添加与serve脚本相同的start脚本。

"scripts": {
  "start": "vue-cli-service serve",
  "serve": "vue-cli-service serve",
  "build": "vue-cli-service build",
  "lint": "vue-cli-service lint"
},

现在,您可以使用npm start Vue应用npm start 。 您的Spring Boot应用程序仍应在端口8080上运行,这将导致您的Vue应用程序使用端口8081。我希望您在本教程中始终在8081上运行您的Vue应用程序。 为确保它始终在此端口上运行,请创建一个client/vue.config.js文件,并向其中添加以下JavaScript。

module.exports = {
  devServer: {
    port: 8081
  }
};

在浏览器中打开http://localhost:8081 ,您应该会看到类似下面的页面。

引导发展

在Vue中创建良好的Beers UI

到目前为止,您已经创建了一个好的啤酒API和一个Vue客户端,但是尚未创建UI来显示API中的啤酒列表。 为此,请打开client/src/views/Home.vue并添加一个created()方法。

import axios from 'axios';
...

private async created() {
  const response = await axios.get('/good-beers');
  this.beers = await response.data;
}

Vue的组件生命周期将调用created()方法。

John Papa的带有TypeScriptVue.js对弄清楚如何将TypeScript与Vue一起使用提供了很大的帮助。 Vue的TypeScript文档也很有帮助。

您需要安装axios才能编译此代码。

npm i axios

您会看到这会将响应数据放入本地beers变量中。 要正确定义此变量,请创建一个Beer接口并将Home类的beers变量初始化为一个空数组。

export interface Beer {
  id: number;
  name: string;
  giphyUrl: string;
}

@Component({
  components: {
    HelloWorld,
  },
})
export default class Home extends Vue {
  public beers: Beer[] = [];

  private async created() {
    const response = await axios.get('/good-beers');
    this.beers = await response.data;
  }
}

敏锐的眼睛会注意到,这会在与Vue应用程序相同的端口上向/good-beers发出请求(因为它是相对URL)。 为此,您需要修改client/vue.config.js以使其具有将此URL发送到Spring Boot应用程序的代理。

module.exports = {
  devServer: {
    port: 8081,
    proxy: {
      "/good-beers": {
        target: "http://localhost:8080",
        secure: false
      }
    }
  }
};

修改client/src/views/Home.vue的模板,以显示API中的优质啤酒列表。

<template>
  <div class="home">
    <img alt="Vue logo" src="../assets/logo.png">
    <h1>Beer List</h1>
    <div v-for="beer in beers">
      {{ beer.name }}
    </div>
  </div>
</template>

使用npm start重新启动Vue应用,并在http://localhost:8081上刷新您的应用。 您应该从Spring Boot API中看到啤酒列表。

引导发展

创建一个BeerList组件

为了使此应用程序易于维护,请将啤酒清单逻辑和渲染移至其自己的BeerList组件。 创建client/src/components/BeerList.vue并用Home.vue的代码填充它。 删除Vue徽标,自定义模板的主类名称,然后删除HelloWorld组件。 完成后,它应该如下所示。

<template>
  <div class="beer-list">
    <h1>Beer List</h1>
    <div v-for="beer in beers">
      {{ beer.name }}
    </div>
  </div>
</template>

<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import axios from 'axios';

export interface Beer {
  id: number;
  name: string;
  giphyUrl: string;
}

@Component
export default class BeerList extends Vue {
  public beers: Beer[] = [];

  private async created() {
    const response = await axios.get('/good-beers');
    this.beers = await response.data;
  }
}
</script>

然后更改client/src/views/Home.vue ,使其仅包含徽标和对<BeerList/>的引用。

<template>
  <div class="home">
    <img alt="Vue logo" src="../assets/logo.png">
    <BeerList/>
  </div>
</template>

<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import BeerList from '@/components/BeerList.vue';

@Component({
  components: {
    BeerList,
  },
})
export default class Home extends Vue {}
</script>

创建一个GiphyImage组件

为了使外观看起来更好一点,添加GIPHY组件以根据啤酒的名称获取图像。 创建client/src/components/GiphyImage.vue并将以下代码放入其中。

<template>
  <img :src=giphyUrl v-bind:alt=name height="200"/>
</template>

<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator';
import axios from 'axios';

@Component
export default class GiphyImage extends Vue {
  @Prop() private name!: string;
  private giphyUrl: string = '';

  private async created() {
    const giphyApi = '//api.giphy.com/v1/gifs/search?api_key=dc6zaTOxFJmzC&limit=1&q=';

    const response = await axios.get(giphyApi + this.name);
    const data = await response.data.data;
    if (data.length) {
      this.giphyUrl = data[0].images.original.url;
    } else {
      this.giphyUrl = '//media.giphy.com/media/YaOxRsmrv9IeA/giphy.gif';
    }
  }
}
</script>

<!-- The "scoped" attribute limits CSS to this component only -->
<style scoped>
img {
  margin: 10px 0 0;
}
</style>

更改BeerList.vue以在其模板中使用<GiphyImage/>组件:

<div v-for="beer in beers">
  {{ beer.name }}<br/>
  <GiphyImage :name="beer.name"/>
</div>

并将其添加到<script>块中的components列表中:

import GiphyImage from '@/components/GiphyImage.vue';

@Component({
  components: {GiphyImage},
})
export default class BeerList extends Vue { ... }

在同一文件中,在底部添加<style>部分,然后使用CSS Grid布局将啤酒按行组织。

<style scoped>
.grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-gap: 10px;
  grid-auto-rows: minmax(100px, auto);
}
</style>

您需要将div环绕在啤酒清单模板上,以使其生效。

<div class="grid">
  <div v-for="beer in beers">
    {{ beer.name }}<br/>
    <GiphyImage :name="beer.name"/>
  </div>
</div>

进行这些更改后,您的用户界面应类似于以下啤酒名称和匹配图像列表。

引导发展

您刚刚创建了一个与Spring Boot API对话的Vue应用。 恭喜你! 🎉

添加PWA支持

Vue CLI开箱即用地支持渐进式Web应用程序(PWA)。 创建Vue应用程序时,您选择了PWA作为功能。

PWA功能仅在生产中启用,因为在开发中缓存资产可能是一个真正的难题。 在client目录中运行npm run build来创建一个可以投入生产的版本。 然后使用serve创建一个Web服务器并显示您的应用程序。

npm i -g serve
serve -s dist -p 8081

您应该能够打开浏览器,并在http://localhost:8081看到您的应用程序。 第一次尝试时,我发现加载页面没有呈现任何啤酒名称,并且所有图像均相同。 这是因为客户端尝试向/good-beers发出请求,并且在生产模式下未配置任何代理。

要解决此问题,您需要在客户端中更改URL并将Spring Boot配置为允许从http://localhost:8081进行跨域访问。

修改client/src/components/BeerList.vue以使用Spring Boot API的完整URL。

private async created() {
  const response = await axios.get('http://localhost:8080/good-beers');
  this.beers = await response.data;
}

如果进行这些更改后,您在用户界面中看不到任何更改,那是因为您的浏览器已缓存您的应用程序。 使用隐身窗口或清除缓存(在Chrome中: 开发者工具 > 应用程序 > 清除存储 > 清除网站数据 )可解决此问题。

为Spring Boot配置CORS

在服务器项目中,打开src/main/java/…​/demo/beer/BeerController.java并添加一个@CrossOrigin批注以启用来自客户端的跨域资源共享(CORS)( http://localhost:8081 ) 。

import org.springframework.web.bind.annotation.CrossOrigin;
...
    @GetMapping("/good-beers")
    @CrossOrigin(origins = "http://localhost:8081")
    public Collection<Beer> goodBeers() {

进行这些更改后,重建Vue应用以进行生产,刷新浏览器,一切都应按预期呈现。

使用Lighthouse查看您的PWA分数

我在Chrome中进行了Lighthouse审核,发现此应用目前得分为81/100。 该报告最突出的抱怨是我没有使用HTTPS。 为了查看该应用使用HTTPS时的评分,我将其部署到Pivotal Cloud FoundryHeroku 。 我很兴奋地发现它在两个平台上都得分很高。

引导发展
引导发展

得分为96的原因是因为The viewport size is 939px, whereas the window size is 412px. 我不确定是什么引起了这个问题,也许是CSS Grid布局?

要查看我用来部署所有内容的脚本,请参阅heroku.sh随附的GitHub存储库中的heroku.shcloudfoundry.sh

您将需要在运行部署脚本之前初始化Git。 运行rm -rf client/.git ,然后运行git commit -a "Add project"

使用Okta添加身份验证

您可能会想,“这很酷,很容易看出人们为什么挖Vue。” 尝试过后,您可能还会挖掘其他工具:使用Okta进行身份验证! 为什么选择Okta? 因为您可以免费获得1,000个每月活跃用户 ! 值得一试,尤其是当您看到使用Okta将auth添加到Spring Boot和Vue如此容易时。

Okta Spring启动启动器

为了保护您的API,可以使用Okta的Spring Boot Starter 。 要集成此启动器,请将以下依赖项添加到server/pom.xml

<dependency>
    <groupId>com.okta.spring</groupId>
    <artifactId>okta-spring-boot-starter</artifactId>
    <version>0.6.1</version>
</dependency>
<dependency>
    <groupId>org.springframework.security.oauth.boot</groupId>
    <artifactId>spring-security-oauth2-autoconfigure</artifactId>
    <version>2.1.1.RELEASE</version>
</dependency>

现在,您需要配置服务器以使用Okta进行身份验证。 为此,您需要在Okta中创建OIDC应用。

在Okta中创建OIDC应用

登录到您的1563开发者帐户(或者注册 ,如果你没有一个帐户)并导航到应用程序 > 添加应用程序 。 单击“ 单页应用程序” ,再单击“ 下一步” ,然后为该应用程序命名。 将localhost:8080所有实例更改为localhost:8081 ,然后单击完成

将客户端ID复制到您的server/src/main/resources/application.properties文件中。 在其中时,添加与您的Okta域匹配的okta.oauth2.issuer属性。 例如:

okta.oauth2.issuer=https://{yourOktaDomain}/oauth2/default
okta.oauth2.client-id={yourClientId}

{yourOktaDomain}替换为您的组织机构网址,您可以在开发人员控制台的仪表板上找到它。 确保在值中不包括-admin

更新server/src/main/java/…​/demo/DemoApplication.java以将其启用为资源服务器。

import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;

@EnableResourceServer
@SpringBootApplication

进行了这些更改之后,您应该能够重新启动服务器,并在尝试导航到http://localhost:8080时看到访问被拒绝。

引导发展

Okta的Vue支持

Okta的Vue SDK可让您将OIDC集成到Vue应用程序中。 您可以在npmjs.com找到有关Okta的Vue SDK的更多信息。 要安装,请在client目录中运行以下命令:

npm i @okta/okta-vue@1.0.7
npm i -D @types/okta__okta-vue


Okta的Vue SDK的类型可能会包含在将来的版本中。 我创建了一个添加请求的拉取请求

打开client/src/router.ts并添加您的Okta配置。 该router.ts下面还包含了一个路径BeerList ,这是需要进行身份验证的回调,以及导航后卫需要认证/beer-list路径。 用这个替换您的,然后更新yourClientDomainyourClientId以匹配您的设置。 确保删除{}因为它们只是占位符。

import Vue from 'vue';
import Router from 'vue-router';
import Home from './views/Home.vue';
import OktaVuePlugin from '@okta/okta-vue';
import BeerList from '@/components/BeerList.vue';

Vue.use(Router);
Vue.use(OktaVuePlugin, {
  issuer: 'https://{yourOktaDomain}/oauth2/default',
  client_id: '{yourClientId}',
  redirect_uri: window.location.origin + '/implicit/callback',
  scope: 'openid profile email',
});

const router = new Router({
  mode: 'history',
  base: process.env.BASE_URL,
  routes: [
    {
      path: '/',
      name: 'home',
      component: Home,
    },
    {
      path: '/about',
      name: 'about',
      // route level code-splitting
      // this generates a separate chunk (about.[hash].js) for this route
      // which is lazy-loaded when the route is visited.
      component: () => import(/* webpackChunkName: "about" */ './views/About.vue'),
    },
    {
      path: '/beer-list',
      name: 'beer-list',
      component: BeerList,
      meta: {
        requiresAuth: true,
      },
    },
    { path: '/implicit/callback', component: OktaVuePlugin.handleCallback() },
  ],
});

router.beforeEach(Vue.prototype.$auth.authRedirectGuard());

export default router;

由于您具有BeerList的路由,因此请从client/src/views/Home.vue删除它。

<template>
  <div class="home">
    <img alt="Vue logo" src="../assets/logo.png">
  </div>
</template>

<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';

@Component
export default class Home extends Vue {}
</script>

client/src/App.vue链接添加到>BeerList client/src/App.vue 。 您还需要添加代码来检测用户是否已登录。 替换<template>部分,并将下面的<script>添加到您的App.vue

<template>
  <div id="app">
    <div id="nav">
      <router-link to="/">Home</router-link> |
      <router-link to="/about">About</router-link>
      <template v-if="authenticated"> |
        <router-link to="/beer-list">Good Beers</router-link>
      </template>
    </div>
    <button v-if="authenticated" v-on:click="logout">Logout</button>
    <button v-else v-on:click="$auth.loginRedirect()">Login</button>
    <router-view/>
  </div>
</template>

<script lang="ts">
import { Component, Vue, Watch } from 'vue-property-decorator';

@Component
export default class App extends Vue {
  public authenticated: boolean = false;

  private created() {
    this.isAuthenticated();
  }

  @Watch('$route')
  private async isAuthenticated() {
    this.authenticated = await this.$auth.isAuthenticated();
  }

  private async logout() {
    await this.$auth.logout();
    await this.isAuthenticated();

    // Navigate back to home
    this.$router.push({path: '/'});
  }
}
</script>

重新启动Vue应用程序,您应该看到一个登录按钮。

引导发展

单击它,您将被重定向到Okta。 输入您用来注册Okta的凭据,您将被重定向回该应用程序。 您应该看到一个注销按钮和一个链接,以查看一些优质啤酒。

引导发展

如果单击“ Good Beers”链接,您将看到组件的标题,但没有数据。 如果您查看JavaScript控制台,则会看到CORS错误。

发生此错误是因为Spring的@CrossOrigin在Spring Security中不能很好地发挥作用。 要解决此问题,请在DemoApplication.java的主体中添加一个simpleCorsFilter bean。

package com.okta.developer.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.Collections;

@EnableResourceServer
@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

    @Bean
    public FilterRegistrationBean<CorsFilter> simpleCorsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true);
        config.setAllowedOrigins(Collections.singletonList("http://localhost:8081"));
        config.setAllowedMethods(Collections.singletonList("*"));
        config.setAllowedHeaders(Collections.singletonList("*"));
        source.registerCorsConfiguration("/**", config);
        FilterRegistrationBean<CorsFilter> bean = new FilterRegistrationBean<>(new CorsFilter(source));
        bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
        return bean;
    }
}

进行此更改后,重新启动服务器。 要使其在客户端上全部client/src/components/BeerList.vue ,请修改client/src/components/BeerList.vuecreated()方法以设置授权标头。

private async created() {
  const response = await axios.get('http://localhost:8080/good-beers',
    {
      headers: {
        Authorization: `Bearer ${await this.$auth.getAccessToken()}`,
      },
    },
  );
  this.beers = await response.data;
}

现在,您应该能够以经过身份验证的用户身份查看优质啤酒清单。

引导发展

如果可行,那就太好了! 👍

了解有关Spring Boot和Vue的更多信息

本教程向您展示了如何构建使用诸如Spring Boot和Vue之类的现代框架的应用程序。 您学习了如何使用Okta的Vue SDK添加OIDC身份验证和保护路由。 如果您想观看本教程的视频,我将其作为截屏视频发布到YouTube上

如果您想了解有关Vue现象的更多信息,我推荐了几篇文章。 首先,我认为这很不错,它不是由公司(例如Angular + Google和React + Facebook)赞助的,这主要是由社区推动的。 挑战Google和Facebook的Solo JavaScript开发人员是《连线》杂志的一篇文章,解释了为什么这样做如此惊人。

关于JavaScript框架的性能,JavaScript框架的基准成本Anku Sethi的一篇有趣的博客文章。 我喜欢他写这本书的动力:

上周,我对仅在页面上包含React会产生多少性能影响感到好奇。 因此,我在廉价的Android手机上运行了一些数字,并对此进行了撰写。

要了解有关Vue,Spring Boot或Okta的更多信息,请查看以下资源:

您可以在GitHub上找到与本文相关的源代码。 主要示例(无身份验证)在master分支中,而Okta集成在okta分支中。 要签出本地计算机上的Okta分支,请运行以下命令。

git clone -b okta https://github.com/oktadeveloper/spring-boot-vue-example.git

如果您发现任何问题,请在下面添加评论,我们将尽力为您提供帮助。 如果您喜欢本教程,则应该在Twitter上关注我的团队 。 我们还有一个YouTube频道 ,我们在其中发布屏幕录像。


该教程有AngularReact版本。

“我喜欢编写身份验证和授权代码。” 〜从来没有Java开发人员。 厌倦了一次又一次地建立相同的登录屏幕? 尝试使用Okta API进行托管身份验证,授权和多因素身份验证。

使用Spring Boot和Vue进行Bootiful开发最初于2018年12月3日发布在Okta开发人员博客上。

翻译自: https://www.javacodegeeks.com/2019/01/bootiful-development-spring-boot-vue.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值