65.9K
CodeProject 正在变化。 阅读更多。
Home

如何在没有 Redux 的情况下使用 React 实现 OIDC 身份验证和授权

starIconstarIconstarIconstarIconstarIcon

5.00/5 (4投票s)

2018 年 6 月 25 日

CPOL

10分钟阅读

viewsIcon

17751

如何在没有 Redux 的情况下使用 React 实现 OIDC 身份验证和授权。

引言

在本教程中,我将在 ASP.NET Core React.js 单页应用程序中实现 OpenID Connect (OIDC) 身份验证和授权,而无需使用 Redux(完全不需要它)。

我将使用 Visual Studio 2017 Community 和 ASP.NET Core 2.x、React.js 和 EntityFramework Core。本教程假定您熟悉 Visual Studio 和 ASP.NET 应用程序,并且至少编写过一些 C# 和 JavaScript 代码。

背景

OIDC 是处理身份验证和授权的最新、最好的方法,它具有单点登录 (SSO)、认证即服务、基于声明的授权等功能——简而言之:它是您希望在现代 Web 应用程序中处理身份验证和授权的方式。

构建我们的 React 应用程序

Visual Studio 2017 包含一个基本的 React 项目模板,所以我们将从它开始。

让我们创建我们的项目...

  1. 打开 Visual Studio 并选择“新建项目”。

  2. 从“新建项目”对话框中,选择“.NET Core”,然后选择“ASP.NET Core Web 应用程序”(图 1)

    fig 1

    图 1
  3. 从“ASP.NET Core Web 应用程序”对话框中,选择“React.js”(图 2)

    fig 2

    图 2
  4. 将应用程序命名为“BssReactOidcLoginApp”。

配置应用程序

我们的 OIDC 组件使用 Any Typescript 数据类型,因此我们必须关闭 TypeScript 的“strict”模式。

在应用程序根目录下的 *tsconfig.json* 中,将“strict”设置为 false,如下所示

    "compilerOptions": {
    "strict": false

一个用于“繁重工作”的 Nuget 包

我们需要安装一个 Nuget 包,用于处理 OIDC 令牌验证的“繁重工作”,名为 IdentityServer4.AccessTokenValidation

为此,请从“包管理器控制台”(工具 | Nuget 包管理器 | 包管理器控制台)运行以下命令

    Install-Package IdentityServer4.AccessTokenValidation -Version 2.6.0

我们还需要安装几个 NPM 包:oidc-clientbrowserify(允许我们从客户端应用程序调用 oidc-client 包)。

我们还需要一个脚本来构建我们的 browserify 客户端包。为此,只需将以下代码块添加到 *Package.json* 文件中“devdependencies{}”块下方,当您保存文件时,Visual Studio 将自动下载这些包。

    "dependencies": {
        "oidc-client": "^1.4.1",
        "browserify": "^16.2.2"
    },
    "scripts": {
        "build-js": "browserify -r oidc-client -o wwwroot/dist/bundle.js"
    }

我们可以通过几种不同的方式运行 NPM 脚本:有一个出色的免费 Visual Studio 扩展,名为:NPM Task Runner,您可以使用它。在本教程中,我们将采用一种不那么华丽的方法:使用 Visual Studio 的 Exec 命令,它不需要您安装任何东西。我们需要编辑我们的 *.csproj* 文件。为此

  1. 右键单击解决方案中的项目,然后选择“卸载项目”(图 3)

    fig 3

    图 3
  2. 然后右键单击项目并选择“编辑 BssReactOidcLoginApp.csproj”(图 4)

    fig 4

    图 4
  3. 向下滚动到 标签,并键入下面第 69 行所示的 Exec 命令。

    注意:有两个 标签;确保选择名称为 DebugRunWebpack 的那个。

    fig 5

    图 5
  4. 保存并关闭文件。然后右键单击项目并选择“重新加载项目”(图 6)。

    fig 6

    图 6
  5. 重新生成项目。如果生成成功,并且您在最后一行看到 browserify 脚本(图 7),恭喜您 - 一切顺利!

    注意:编译配置应设置为 Debug。

    fig 7

    图 7

OidcLogin React 组件

现在我们需要安装 OidcLogin React 组件并稍微调整我们的应用程序。

BetterSoftwareSolutions.OidcLogin 是一个免费的 Visual Studio 2017 扩展。您可以在这里下载它:https://marketplace.visualstudio.com/items?itemName=BetterSoftwareSolutionsLLC.OidcLogin。您需要关闭 Visual Studio 才能安装它。准备好继续时,返回此处即可。

如果您正在继续本教程,您应该已经安装了上面提到的免费 BetterSoftwareSolutions.OidcLogin Visual Studio 扩展。安装扩展后,右键单击项目,然后选择“添加新项”,然后选择“React OidcLogin”项(图 8)。

fig 8

图 8

OidcLogin 项模板包含一个名为 *OidcLoginReadme.txt* 的文件。如果您打开该文件,您会看到该模板还将以下项添加到项目中。它还会告诉您接下来需要做什么(下一步)。

  • wwwroot\dist\bundle.js
  • ClientApp\components\OidcLogin\*
  • ClientApp\components\OidcConfig.tsx
  • Views\Home\SignedIn.cshtml

配置我们的应用程序

让我们在我们的应用程序中配置 OIDC 身份验证和授权...

您会注意到我们已经完成了一些“下一步”操作。我们将继续执行步骤 3:更改 Startup.ConfigureServices()。如 readme 所述,我们希望将下面显示的代码块复制到 *Startup.cs* 中的 ConfigureServices() 方法中。将其粘贴在 services.AddMvc() 行之前。

    // add this block before services.AddMvc()
    services.AddMvcCore()
    .AddAuthorization()
    .AddJsonFormatters();
    services.AddAuthentication("Bearer")
    .AddIdentityServerAuthentication(options =>
    {
    options.Authority = "https://ids.bssdev.biz";
    options.RequireHttpsMetadata = true;//change this to false for dev/testing only
    options.ApiName = "enter your api name from identity server(ids.bssdev.biz) here";
    });

我们还需要在同一个文件中的 Configure() 方法中添加一行。将其粘贴在 app.UserMvc(); 之前。

    //add this line was added before app.UseMvc();
    app.UseAuthentication();

我们需要将 [Authorize] 属性添加到我们的 SampleDataController(我们的“API”)。这可以保护我们的 API 免受未经身份验证的访问,即所有用户必须先登录才能访问我们 API 中的任何内容。将其粘贴在类声明(“public class SampleDataController”)之前。

    //this protects the entire controller
    [Authorize]

我们需要一种让用户登录的方式,所以我们将 <OidcLogin /> 组件添加到 *navmenu.tsx* 组件(ClientApp/components)。将以下 import 语句粘贴到顶部,在最后一个 import 语句下方。

    //add an import statement to the top of the component
    import { OidcLogin } from './OidcLogin/OidcLogin'

然后将 <OidcLogin /> 组件添加到 render() 方法中,紧跟在 <div className='navbar-header'> 的结束 </div> 之后。只添加 <oidclogin /> 行。

//add only the <OidcLogin / > line
public render() {
    return <div classname='main-nav' >
        <div classname='navbar navbar-inverse' >
            <div classname='navbar-header' >
            ...
            </div >
            <oidclogin />
            ...
        </div >
}

此外,我们需要稍微修改我们的组件视图。我们将修改 *ClientApp/components/FetchData.tsx*。首先,将以下 import 语句添加到组件的顶部。

    import * as Oidc from 'oidc-client'
    import { OidcUnauthorized } from './OidcLogin/OidcUnauthroized'
    import * as Config from './OidcConfig'

接下来,我们需要向组件状态添加一个字段,以跟踪已认证的用户。将 _user?: any; 行粘贴在 FetchDataExampleState 接口的结束 } 之前。

interface FetchDataExampleState {
    forecasts: WeatherForecast[];
    loading: boolean;
    //here's where we put our authenticated user
    _user?: any;
}

在构造函数中,紧跟在状态初始化行的下方,粘贴以下调用 UserManager 并检索已登录用户的代码块。

    //get the authenticated user
    let mgr = new Oidc.UserManager(Config.UserManagerSettings);
    mgr.getUser().then((user) => {
        this.setState({ _user: user });
        console.log(user);
    })

我们还需要对组件做一件事,那就是修改每个 fetch() 调用。我们需要注释掉现有的 fetch() 调用,并将其替换为下面显示的代码块。您会注意到我们的新 fetch() 调用包含 Config.FetchSettings(this.state._user),它包含 OIDC 所需的设置,并来自 *ClientApp/components/OidcConfig.tsx*。

         //replace the existing fetch() statement with this
        .then(() => {
            console.log(this.state);
            return fetch('api/SampleData/WeatherForecasts', Config.FetchSettings(this.state._user))
        })

我们对组件要做的最后一件事是向 render() 方法添加几行代码,以检查已认证的用户,即,如果我们没有登录用户,我们返回 <OidcUnauthorized />,它只是显示一个“Unauthorized”消息。

   	public render() {
        //add this to the top of the render() method
        if (this.state._user == null)
            return <oidcunauthorized>
</oidcunauthorized>

登录后重定向用户

用户登录后,我们需要将其重定向到我们选择的页面——在本例中是我们的站点根目录。为了处理这个问题,我们有一个名为 SignedIn 的 MVC 视图,它是由 OidcLogin 项目模板添加的。我们所需要做的就是修改 HomeController 以在请求时提供该视图。将以下代码块添加到 HomeController

    	// This method is used to redirect a user after they log in.
        public IActionResult SignedIn()
        {
            return View();
        }

这样就解决了我们的应用程序问题。在下一节中,我们将在 Identity Server 中创建一个客户端应用程序,然后运行该应用程序。

创建 Identity Server 客户端应用程序

我们将使用 BSS 身份服务器,网址为 ids.bssdev.biz。您需要注册并创建一个免费的开发者账户。准备好继续时,我们就在这里。

创建免费开发者账户并登录后,您会在右上角看到用户名旁边有一个下拉菜单。单击下拉菜单并选择“客户端”以创建客户端应用程序(图 9)。

fig 9

图 9

创建客户端应用程序最简单的方法是使用向导。从“客户端应用程序”页面,我们将单击左上角的“创建客户端应用程序向导”链接开始,然后选择“单页应用程序”按钮(图 10)。

fig 10

图 10

在向导的第一页,您需要指定客户端 ID 和客户端名称。我喜欢使用应用程序的确切名称作为客户端 ID,这样就永远不会怀疑它是哪个应用程序。只需从 HomeController 复制命名空间并将其粘贴到客户端 ID 字段中。然后再次粘贴到客户端名称字段中,该字段会显示给用户。如果您愿意,可以添加空格使其更易读,然后单击“下一步”按钮(图 11)。

fig 11

图 11

您将在下一页需要您的站点 URL。右键单击您的项目并选择“属性”,然后从属性窗口的左侧选择“调试”选项卡。复制应用程序 URL 并将其粘贴到“重定向 URI”字段中,然后在其末尾添加“Home/SignedIn”。这是用户登录后处理重定向的视图。将您的应用程序 URL 再次粘贴到“注销后重定向 URI”和“CORS 来源”字段中。用户注销后,我们将简单地将其重定向到根页面,这不需要他们登录。CORS(跨域资源共享)来源仅表示站点根目录,其末尾不应有 /

fig 12

图 12

步骤 4 是我们指定客户端应用程序需要访问的范围。Openidprofile 分别用于 OIDC 身份验证和显示用户名。我们还需要允许客户端访问集成的“api”(我们的 FetchDataController)。通常,我们还需要在身份服务器中创建 API 和 Scope,但如果勾选“使用与客户端应用程序相同的名称创建 API 和 Scope”复选框,身份服务器将自动为我们创建这些。勾选该框,然后单击“下一步”按钮。

fig 13

图 13

就这样!只需单击向导最后一页上的“完成”按钮,我们就大功告成了。

总结

我们已经在没有 Redux 的情况下,为我们的 React 单页应用程序添加了 OIDC 身份验证和授权,并在我们的身份服务器中创建了客户端应用程序和 API 范围。我们只需检查一些设置,就可以运行我们的应用程序了。

打开 *startup.cs*,向下滚动到 ConfigureServices() 方法,并确保 ApiName 与 Identity Server 中显示的名称完全一致。它应该与 Client.Client Id 相同,因为我们勾选了让 Identity Server 自动创建我们的 API 的复选框。如果您愿意,可以从管理菜单中选择 API,并在 API 页面上检查“名称”字段。

最后,让我们检查一下 *OidcConfig.tsx* 文件(位于 *ClientApp/components* 中)中的设置。您需要确保 client_idredirect_uri、scope 的最后一部分以及 post_logout_redirect_uri 都正确无误(图 14)。
注意client_id 和 scope 的最后一部分应该相同,因为我们让 Identity Server 为我们创建了一个同名 scope。

fig 14

图 14

运行我们的应用程序

我们现在可以运行我们的应用程序了。最好重建一下,以确保一切正常。然后不调试运行。

您会注意到,导航窗格的右上角现在有一个“登录”链接(图 15)。

fig 15

图 15

如果您点击“获取数据”链接,您会注意到看到了“未授权”组件。

fig 16

图 16

如果您点击登录链接,可能会发生以下两种情况之一

  1. 如果您已经登录到 Identity Server,您将只看到您的用户名,并且“登录”链接将被“注销”链接取代。

  2. 如果您尚未登录,您将被重定向到身份服务器登录页面(图 17)。然后,在成功登录后,您将被重定向回您的站点。您会注意到它现在显示您的用户名以及一个“注销”链接。如果您点击“获取数据”链接,您现在会看到正常页面(图 18)。点击“注销”链接,然后点击“获取数据”链接,您会再次看到“未授权”消息。

fig 17

图 17

fig 18

图 18

结束语

最后一点评论……OidcLogin Visual Studio 扩展还包含一个新的项目模板,它创建了一个内置 OIDC 身份验证/授权的 React 单页应用程序。我们本可以直接选择它来创建我们的应用程序,但那样就不会那么有趣……或富有教育意义了!

祝您在 React 开发中一切顺利。

历史

  • 2018 年 6 月 27 日:初始版本

© . All rights reserved.