跳转至

Vue

Authany 帮助您为 Vue 应用添加用户登录功能。它提供了预构建的登录页面和用户设置页面,加快开发速度。

跟随这篇 🕐 15 分钟的教程,使用 Vue 和 Authany SDK 创建一个简单的应用。

说明

查看并克隆 GitHub 上的示例项目。

在 Authany 中设置应用

要使用 Authany,您需要一个 Authany 账号和一个项目。前往 https://manage.authany.com/ 注册免费账号并创建一个项目。

之后,我们需要在项目管理台中创建一个应用。

在管理台中创建应用

在这一步,我们将在 Authany 管理台中创建一个 Authany 客户端应用。后续步骤中我们会用到这个应用的配置,将 Vue 应用与 Authany 连接起来。

要创建客户端应用,在 Authany 管理台左侧菜单栏中导航到应用。

管理台导航到应用

接下来,点击顶部工具栏的 ⊕添加应用,打开新建应用页面。

输入应用名称,例如「MyAwesomeApp」,然后选择单页应用作为应用类型。

点击保存创建该应用。

创建新的客户端应用

在下一个页面,您会看到不同框架的教程链接。点击下一步跳过,进入应用配置页面

配置授权重定向 URI

重定向 URI 是您应用中的一个 URL,用户在 Authany 登录后会被重定向到这里。在这个路径中,需要发起一个完成身份验证调用来完成登录流程。

滚动到您在上一步创建的客户端应用配置页面的 URI 部分。在本教程中,将 http://localhost:4000/auth-redirect 添加到 Authorize Redirect URIs。

继续之前,点击保存按钮保存您的更改。

配置 Authorized Redirect URIs

将 Authany 添加到 Vue 应用

在这一部分,我们将把 Authany 添加到一个 Vue 应用中,使用 Authany JavaScript SDK 实现以下功能:

  • 用户登录/注册
  • 读取已登录用户的信息
  • 打开用户设置页面
  • 使用登出按钮结束用户会话

第 1 步:创建一个简单的 Vue 项目

以下是搭建 Vue 项目的一些推荐步骤。如果您是将 Authany 添加到现有项目中,可以跳过这部分,参见下一部分的第 3 步:将 Authany SDK 安装到项目中。

安装基本项目依赖

创建项目文件夹并安装依赖。我们将使用 Vite 作为构建工具,以及 vue-router 包。本教程中我们也会使用 TypeScript。

运行以下命令创建一个新项目:

创建项目并进入项目目录:

npm create vite@latest my-app -- --template vue-ts
cd my-app

安装依赖:

npm install

安装 Vue router:

npm install --save-exact vue-router

为开发模式添加端口配置

由于本教程使用 4000 端口,我们需要在配置中添加端口信息。在 vite.config.ts 文件中,按以下内容修改文件:

// vite.config.ts
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [vue()],
  server: {
    port: 4000,
  },
});

完成后,运行 npm run dev 时,服务器将运行在 4000 端口。

创建 Home.vue 文件

在 src/components 文件夹中创建一个名为 Home.vue 的新文件,只需在屏幕上显示 Hello World。将以下代码添加到 Home.vue:

// src/components/Home.vue
<script setup lang="ts"></script>

<template><div>Hello World</div></template>

编辑 App.vue 文件

App.vue 文件已经由 Vite 生成,但其中一些部分在本教程中可能用不上。将 App.vue 的内容编辑为以下内容:

// src/components/App.vue
<script setup lang="ts">
import Home from "./components/Home.vue";
</script>

<template>
  <Home />
</template>

删除不需要的文件

有些文件可能用不到,因此可以删除。您可以执行以下脚本来删除这些文件:

rm -rf src/assets src/components/HelloWorld.vue

文件结构

现在您项目中的文件结构是:

my-app
├── node_modules
│   └── (...)
├── package-lock.json
├── package.json
├── vite.config.ts
├── (...)
└── src
    ├── components
    │   └── Home.vue
    ├── App.vue
    ├── main.ts
    └── (...)

现在运行 npm run dev 来运行项目,您会在 http://localhost:4000 看到带有标题 Vite + Vue 的默认页面,以及一条 "Hello World" 消息。

第 2 步:为项目创建路由

在 src/components 文件夹中创建一个 AuthRedirect.vue 文件,此时内容与 src/components/Home.vue 相同。

在 src/ 文件夹中创建一个名为 router.ts 的文件。我们会导入 Home 和 AuthRedirect 组件作为路由,稍后再实现这些组件。该文件的内容如下:

// src/router.ts
import { createRouter, createWebHistory } from "vue-router";

export const history = createWebHistory();
export const router = createRouter({
  history,
  routes: [
    {
      path: "/",
      // We will implement this component later
      component: () => import("./components/Home.vue"),
    },
    {
      path: "/auth-redirect",
      // We will implement this component later
      component: () => import("./components/AuthRedirect.vue"),
    },
  ],
});

第 3 步:将 Authany SDK 安装到项目中

在您的 Vue 项目目录中运行以下命令,安装 Authany Web SDK

npm install --save-exact @authgear/web

在 src/main.ts 中,导入 authgear 并调用 configure 函数,在应用加载时初始化一个 Authany 实例。我们还会导入 router,用它来为我们构建路由。您的 main.ts 文件应如下所示:

// src/main.ts
import { createApp } from "vue";
import './style.css'
import App from "./App.vue";
import { router } from "./router";
import authgear from "@authgear/web";

const app = createApp(App);

async function init() {
  try {
    // configure Authgear container instance
    await authgear.configure({
      endpoint: import.meta.env.VITE_AUTHGEAR_ENDPOINT,
      clientID: import.meta.env.VITE_AUTHGEAR_CLIENT_ID,
      sessionType: "refresh_token",
    });
  } finally {
    app.use(router);
    app.mount("#app");
  }
}

init().catch((e) => {
  // Error handling
  console.error(e)
});

Authany 容器实例接受 endpoint 和 clientID 作为参数。它们可以从您在在 Authany 中设置应用中创建的应用页面获取。

在项目根目录创建一个 .env 文件,用以下字段添加您的 Authany 客户端应用配置:

VITE_AUTHGEAR_CLIENT_ID=<CLIENT_ID>
VITE_AUTHGEAR_ENDPOINT=<AUTHGEAR_ENDPOINT>
VITE_AUTHGEAR_REDIRECT_URL=http://localhost:4000/auth-redirect

建议在 configure() 解析完成后再渲染应用。这样,当应用渲染时,Authany 已经可以使用。

说明

现在运行 npm run dev,如果 Authany SDK 配置成功,您应该会看到同样的页面,并且控制台中没有错误信息

第 4 步:实现上下文提供者

由于我们希望在应用的任何地方都能引用登录状态,让我们在 /src/contexts 文件夹中用 UserProvider.vue 把该状态放进一个上下文提供者(context provider)中。

在 UserProvider.vue 中,会有一个 isLoggedIn 布尔值。isLoggedIn 这个布尔状态可以通过 onSessionStateChange 回调自动更新。该回调可以存储在本地 SDK 容器的 delegate 中。

// src/contexts/UserProvider.vue
<script lang="ts">
import {
  defineComponent,
  InjectionKey,
  provide,
  readonly,
  ref,
  Ref,
  toRefs,
} from "vue";
import authgear from "@authgear/web";

export interface UserContextValue {
  isLoggedIn: Ref<boolean>;
}

export const UserStateSymbol: InjectionKey<UserContextValue> =
  Symbol("UserState");

export default defineComponent({
  setup() {
    const isLoggedIn = ref(false);

    const state: UserContextValue = {
      isLoggedIn,
    };

    authgear.delegate = {
      onSessionStateChange: (container) => {
        const sessionState = container.sessionState;
        if (sessionState === "AUTHENTICATED") {
          isLoggedIn.value = true;
        } else {
          isLoggedIn.value = false;
        }
      },
    };

    provide<UserContextValue>(UserStateSymbol, toRefs(readonly(state)));

    return { state };
  },
});
</script>

<template>
  <slot />
</template>

第 5 步:实现身份验证重定向

接下来,我们会添加一个「AuthRedirect」页面,用于在用户通过 Authany 完成身份验证后处理验证结果。

在 src/components/ 文件夹中创建 AuthRedirect.vue 组件文件。

在 Auth Redirect 组件中调用 Authany 的 finishAuthentication() 函数,将令牌发回 Authany 服务器以换取访问令牌和刷新令牌。不用担心这些技术细节,finishAuthentication() 会替您完成所有繁重工作并保存身份验证数据。

身份验证完成后,UserContextProvider 中的 isLoggedIn 状态会自动被设为 true。最后,导航回根路径(/),也就是我们的首页。

最终的 AuthRedirect.vue 如下所示

// src/components/AuthRedirect.vue
<script setup lang="ts">
import { onMounted } from "vue";
import authgear from "@authgear/web";
import { router } from "../router";

onMounted(() => {
  async function updateToken() {
    try {
      await authgear.finishAuthentication();
    } finally {
      router.replace({ path: "/" });
    }
  }
  updateToken().catch((e) => console.error(e));
});
</script>

<template></template>

第 6 步:将路由和上下文提供者应用到应用中

由于我们已经在上一部分配置好了路由,现在只需在 App.vue 中添加 <router-view /> 标签即可。然后我们可以导入 UserProvider,并用它包裹 router-view。

您最终的 App.vue 应如下所示:

// src/App.vue
<script setup lang="ts">
import UserProvider from "./contexts/UserProvider.vue";
</script>

<template>
  <UserProvider>
    <router-view />
  </UserProvider>
</template>

现在文件结构应如下所示

src
├── App.vue
├── main.ts
├── router.ts
├── vite-env.d.ts
├── contexts
│   └── UserProvider.vue
└── components
    ├── AuthRedirect.vue
    └── Home.vue

第 7 步:添加登录按钮

首先,我们会导入 Authany 依赖。然后添加登录按钮,点击时会通过 startLogin 回调调用 startAuthentication(ConfigureOptions)。这会将用户重定向到登录页面。

// src/components/Home.vue
<script setup lang="ts">
import authgear, { PromptOption } from "@authgear/web";

const startLogin = () => {
  authgear
    .startAuthentication({
      redirectURI: import.meta.env.VITE_AUTHGEAR_REDIRECT_URL,
      prompt: PromptOption.Login,
    })
    .then(
      () => {
        // started authorization, user should be redirected to Authgear
      },
      (err) => {
        // failed to start authorization
        console.error(err);
      }
    );
};
</script>

<template>
  <h1>Home Page</h1>
  <button @click="startLogin">Login</button>
</template>

现在您可以运行 npm run dev,点击登录按钮后会被重定向到 Authany 登录页面。

点击登录按钮后,用户会被重定向到 Authany 登录页面

第 8 步:显示用户信息

Authany SDK 可以帮助您轻松获取已登录用户的信息。

上一步中用户已成功登录,现在让我们尝试在首页打印用户的用户 ID(sub)。

在 Home.vue 中,我们会添加一个简单的加载提示,以及一条打印 Sub ID 的问候语。我们会添加两个条件元素,使它们仅在用户登录时显示。我们也可以让登录按钮仅在用户未登录时显示。

利用 UserProvider 中的 isLoggedIn 来控制页面上的组件。通过 fetchUserInfo() 获取用户信息,并访问其 sub 属性。

登录按钮同样可以做成条件渲染,仅在用户未登录时可见。

// src/components/Home.vue  
<script setup lang="ts">
import authgear, { PromptOption } from "@authgear/web";
import { inject, onMounted, ref } from "vue";
import { UserStateSymbol } from "../contexts/UserProvider.vue";

const { isLoggedIn } = inject(UserStateSymbol)!;
const isLoading = ref(false);
const greetingMessage = ref("");

onMounted(() => {
  async function updateGreetingMessage() {
    isLoading.value = true;
    try {
      if (isLoggedIn.value) {
        const userInfo = await authgear.fetchUserInfo();
        greetingMessage.value = "The current User sub: " + userInfo.sub;
      }
    } finally {
      isLoading.value = false;
    }
  }

  updateGreetingMessage().catch((e) => {
    console.error(e);
  });
});

const startLogin = () => {
  authgear
    .startAuthentication({
      redirectURI: import.meta.env.VITE_AUTHGEAR_REDIRECT_URL,
      prompt: PromptOption.Login,
    })
    .then(
      () => {
        // started authorization, user should be redirected to Authgear
      },
      (err) => {
        // failed to start authorization
        console.error(err);
      }
    );
};
</script>

<template>
  <h1>Home Page</h1>
  <span v-if="isLoading">Loading...</span>
  <span v-if="greetingMessage">{{ greetingMessage }}</span>
  <div v-if="!isLoggedIn">
    <button @click="startLogin">Login</button>
  </div>
</template>

再次运行应用,页面上应该会打印出用户的用户 ID(sub)。

第 9 步:添加登出按钮

最后,让我们在用户登录后添加一个登出按钮。

在 Home.vue 的模板中,我们会添加以下条件元素:

<div v-if="isLoggedIn">
  <button @click="logout">Logout</button>
</div>

并添加 logout 回调:

const logout = () => {
  authgear
    .logout({
      redirectURI: "http://localhost:4000/",
    })
    .then(
      () => {
        greetingMessage.value = "";
      },
      (err) => {
        console.error(err);
      }
    );
};

再次运行应用,我们现在可以点击登出按钮登出了。

第 10 步:打开用户设置

Authany 提供了一个内置 UI,供用户设置自己的属性和更改安全设置。

使用 open 函数打开 <your_app_endpoint>/settings 处的设置页面

在 Home.vue 中,在登出按钮部分后追加一个条件链接,如下所示:

<div v-if="isLoggedIn">
  <button @click="logout()">Logout</button>
  <br />
  <a
    target="_blank"
    rel="noreferrer"
    @click.stop.prevent="userSetting"
    href="#"
  >
    User Setting
  </a>
</div>

并添加 userSetting 回调:

import authgear, { PromptOption, Page } from "@authgear/web";

const userSetting = async () => {
  await authgear.open(Page.Settings);
};

这是最终生成的 Home.vue:

// src/components/Home.vue
<script setup lang="ts">
import authgear, { PromptOption, Page } from "@authgear/web";
import { inject, onMounted, ref } from "vue";
import { UserStateSymbol } from "../contexts/UserProvider.vue";

const { isLoggedIn } = inject(UserStateSymbol)!;
const isLoading = ref(false);
const greetingMessage = ref("");

onMounted(() => {
  async function updateGreetingMessage() {
    isLoading.value = true;
    try {
      if (isLoggedIn.value) {
        const userInfo = await authgear.fetchUserInfo();
        greetingMessage.value = "The current User sub: " + userInfo.sub;
      }
    } finally {
      isLoading.value = false;
    }
  }

  updateGreetingMessage().catch((e) => {
    console.error(e);
  });
});

const startLogin = () => {
  authgear
    .startAuthentication({
      redirectURI: import.meta.env.VITE_AUTHGEAR_REDIRECT_URL,
      prompt: PromptOption.Login,
    })
    .then(
      () => {
        // started authorization, user should be redirected to Authgear
      },
      (err) => {
        // failed to start authorization
        console.error(err);
      }
    );
};

const logout = () => {
  authgear
    .logout({
      redirectURI: "http://localhost:4000/",
    })
    .then(
      () => {
        greetingMessage.value = "";
      },
      (err) => {
        console.error(err);
      }
    );
};

const userSetting = async () => {
  await authgear.open(Page.Settings);
};
</script>

<template>
  <h1>Home Page</h1>
  <span v-if="isLoading">Loading...</span>
  <span v-if="greetingMessage">{{ greetingMessage }}</span>
  <div v-if="!isLoggedIn">
    <button @click="startLogin">Login</button>
  </div>
  <div v-if="isLoggedIn">
    <button @click="logout">Logout</button>
    <br />
    <a
      target="_blank"
      rel="noreferrer"
      @click.stop.prevent="userSetting"
      href="#"
    >
      User Setting
    </a>
  </div>
</template>

显示登录后的用户 ID、指向用户设置的链接和一个登出按钮

下一步,调用 API

要访问后端应用服务器上的受限资源,HTTP 请求的 Authorization 请求头中应包含访问令牌。Web SDK 提供了一个 fetch 函数,会自动处理这一点,您也可以通过 authgear.accessToken 获取该令牌。

方式一:使用 Authany SDK 提供的 fetch 函数

Authany SDK 为您提供了 fetch 函数,用于调用您的应用服务器。这个 fetch 函数会在您的应用请求中自动带上 Authorization 请求头,并自动处理刷新访问令牌。authgear.fetch 实现了 fetch。

authgear
    .fetch("YOUR_SERVER_URL")
    .then(response => response.json())
    .then(data => console.log(data));

方式二:将访问令牌添加到 HTTP 请求头

您可以通过 authgear.accessToken 获取访问令牌。每次使用访问令牌之前都调用 refreshAccessTokenIfNeeded,该函数只会在访问令牌已过期时才检查并发起网络请求。将访问令牌加入应用请求的 Authorization 请求头中。

authgear
    .refreshAccessTokenIfNeeded()
    .then(() => {
        // access token is ready to use
        // accessToken can be string or undefined
        // it will be empty if user is not logged in or session is invalid
        const accessToken = authgear.accessToken;

        // include Authorization header in your application request
        const headers = {
            Authorization: `Bearer ${accessToken}`
        };
    });