当前位置:   article > 正文

React入门实战:用户认证在React应用中的实战应用

React入门实战:用户认证在React应用中的实战应用

1.背景介绍

用户认证(Authentication)

用户认证(Authentication) 是应用中非常重要的一个功能模块。它通常由密码验证、二步验证等方式组成,用于保证用户的账户安全。目前市面上大多数公司都有自己的认证服务,但是对于小型互联网企业而言,自己搭建认证服务也不太现实,所以很多时候需要选择第三方提供商进行认证。比如腾讯QQ邮箱、微信支付、支付宝等,这些都是第三方认证服务提供商。如果开发者不想自己搭建认证服务器,又想用某种形式的用户认证,那么可以选择这些认证服务提供商。

React + OAuth

前端应用一般采用单页面模式,也就是所有的页面都在同一个页面内实现,每一次页面跳转都会刷新整个页面,这种情况下用户登录信息会丢失,用户需要重新登录。为了解决这个问题,现在许多公司都在研发基于 OAuth 的方案,OAuth 是 Open Authorization 的简称,是一种授权机制,通过 OAuth,第三方应用可以使用自身账号代替用户给予的授权申请,从而让用户无感知地获取资源访问权限。

采用 OAuth 的前端应用流程如下所示:

  1. 用户访问前端应用,点击登录按钮。
  2. 前端应用发送请求到后端,向 OAuth 服务请求用户的登录授权。
  3. 用户同意后,OAuth 服务生成一个授权码并返回给前端应用。
  4. 前端应用将授权码提交给后端,后端向 OAuth 服务请求令牌,并附带客户端 ID 和客户端密钥。
  5. 如果授权码和客户端 ID / 密钥有效,OAuth 服务将生成访问令牌并返回给前端应用。
  6. 前端应用将访问令牌存储起来,并在后续的 API 请求中携带,以完成认证过程。

实际项目中,前端应用可能存在多个 OAuth 服务供用户选择。比如 QQ、微信登录等。我们可以根据需求选择适合的服务,然后把令牌存储到前端应用本地,并在每次请求时自动添加到请求头中。这样就可以实现用户免登录状态下正常访问应用。

JWT

JSON Web Token (JWT) 是一种基于 JSON 格式的数据结构,可以用来表示令牌,用于身份验证或者信息交换。它的主要特征包括签名、验证、加密、压缩等。我们可以利用 JWT 来建立单点登录 (Single Sign-On) 的功能。

JWT 可以在请求头中传递,以 Bearer 开头,例如:

Authorization: Bearer <token>

    前端应用可以存储 JWT 令牌,并且在每个请求头中添加该令牌,后端可以通过解析该令牌获取用户相关的信息。

    基于 React + OAuth + JWT 的用户认证

    本文通过对用户认证在 React 中的实践,一步步的讲解如何用 OAuth 和 JWT 在 React 中实现用户认证。 首先,我们先来看一下整体认证流程:

    1. 用户在前端应用点击登录按钮。
    2. 前端应用发送请求到后端,向 OAuth 服务请求用户的登录授权。
    3. 用户同意后,OAuth 服务生成一个授权码并返回给前端应用。
    4. 前端应用将授权码提交给后端,后端向 OAuth 服务请求令牌,并附带客户端 ID 和客户端密钥。
    5. 如果授权码和客户端 ID / 密钥有效,OAuth 服务将生成访问令牌并返回给前端应用。
    6. 前端应用将访问令牌存储起来,并在后续的 API 请求中携带,以完成认证过程。
    7. 当用户下一次访问前端应用的时候,前段应用会自动检测本地是否有访问令牌,如果有则会将其添加到请求头中。
    8. 后端收到请求之后,会校验访问令牌,确认用户身份。
    9. 如果用户身份正确,后端就能继续处理请求。否则,返回错误响应。

    从上面的流程图中,我们看到 OAuth 和 JWT 的配合可以帮助我们完成单点登录的功能。下面我们再来逐步讲解在 React 中如何实现用户认证。

    2.核心概念与联系

    OAuth

    OAuth 是 Open Authorization 的缩写,是一个关于授权的标准。它描述了如何让第三方应用获得有限的访问权限,相当于提供了一个 API 来帮你实现用户认证。它最初是 Facebook 为其应用程序开放 API 的时候提出的,但由于商业原因和版权问题,近年来开始逐渐流行。随着互联网的发展,越来越多的公司和组织开始选择使用 OAuth 来实现用户认证。

    OAuth 授权类型

    OAuth 定义了四种授权类型,分别为:

    1. Implicit grant type(授权码模式):这种授权类型通常用于客户端属于私密的场景,如移动 APP 。
    2. Authorization code grant type(授权码模式):这种授权类型通常用于客户端高度信任的场景,且可以获取用户详细信息。
    3. Password credentials grant type(密码模式):这种授权类型通常用于命令行工具、内部后台任务等。
    4. Client credentials grant type(客户端模式):这种授权类型通常用于服务器间的通信,如调用第三方 API 。

    一般来说,前两种授权类型都要求用户手动授予授权,后两种授权类型不需要用户手动授权,只需向 OAuth 服务器提供 Client ID 和 Client Secret 即可。下面我们就来分析一下 Authorization Code Grant Type 。

    Authorization Code Grant Type

    Authorization Code Grant Type 是 OAuth 协议中的授权类型,这种授权类型通常用于第三方网站或应用的用户认证。用户访问第三方应用时,应用会先请求用户给予相应权限。当用户同意后,应用会重定向到指定的回调地址,并附带一个授权码。

    授权码只能使用一次,且只能通过 HTTPS 连接。应用将得到授权码后,需要向 OAuth 服务器请求访问令牌。请求中需要提供以下参数:

    • client_id: 客户端 id ,即第三方应用的标识符。
    • redirect_uri: 授权成功后的回调地址。
    • response_type: 固定为 "code"。
    • scope: 申请的权限范围。
    • state: 应用可以指定任意字符串作为认证状态码。

    OAuth 服务器将根据客户端提供的授权码和其他参数生成访问令牌,并将访问令牌返回给应用。

    访问令牌是应用通过 OAuth 访问受保护资源时的凭据,包含应用的用户信息、权限范围等。访问令牌一般包含三部分:Header、Payload、Signature。其中 Header 包含令牌的类型、Token 使用的 Hash 函数和签名算法;Payload 则包含关于用户的信息、权限范围、过期时间、其他一些信息;Signature 则是使用 HMAC - SHA256 或 RSA 算法计算得到的结果。

    应用收到访问令牌后,可以缓存它,并在后续的 API 请求中将它放在请求头中,向 OAuth 服务器请求相应资源。访问令牌的有效期很短,通常是几分钟甚至几秒钟,过期时需要重新获取。

    JWT

    JSON Web Tokens (JWT) 是一种基于 JSON 格式的数据结构,可以用来表示令牌,用于身份验证或者信息交换。它的主要特征包括签名、验证、加密、压缩等。我们可以利用 JWT 来建立单点登录 (Single Sign-On) 的功能。

    JWT 可以在请求头中传递,以 Bearer 开头,例如:

    Authorization: Bearer <token>

      前端应用可以存储 JWT 令牌,并且在每个请求头中添加该令牌,后端可以通过解析该令牌获取用户相关的信息。

      3.核心算法原理和具体操作步骤以及数学模型公式详细讲解

      获取客户端 id 和秘钥

      首先,我们需要创建自己的 OAuth 应用,并获取 client_id 和 client_secret。这个可以在 OAuth 服务提供商那里找到,也可以直接向开源平台如 Auth0 申请。Auth0 是全球领先的云原生身份管理提供商,提供了多种安全性、可扩展性和集成度,可满足各种身份和访问控制的需求。本文使用的 Auth0 平台没有强制要求你注册,直接登录即可。

      配置 Auth0

      Auth0 提供的配置界面让我们很容易地设置好 OAuth 应用。我们只需要按照提示填写一些必要的参数,就可以创建出一个完整的 OAuth 应用。

      1. 创建一个新应用,输入名称和描述,选择单点登录选项。
      2. 添加 Allowed Callback URLs,这里填写你希望用户登录成功后的跳转 URL 。
      3. 添加 Allowed Logout URLs,这里填写用户登出后的跳转 URL 。
      4. 设置 Auth0 作为 OAuth provider ,并保存。
      5. 将 client_id 和 client_secret 复制出来,在前端应用中使用。

      配置完毕后,我们就得到了 client_id 和 client_secret。

      获取授权码

      用户需要访问我们的应用时,需要首先请求用户登录,并同意授权。在 OAuth 协议中,用户需要经历两个阶段的交互:

      1. 第一步:请求用户授权,即向用户展示出带有链接的页面,用户点击链接并登录后,浏览器会转跳回应用,并附带授权码。
      2. 第二步:使用授权码获取访问令牌,即向 OAuth 服务请求访问令牌,并附带授权码和 client_id 和 client_secret 。

      下面我们来模拟用户登录过程。假设用户想要访问应用,则需要先访问我们的 OAuth 应用授权接口。我们可以编写一个按钮,用户点击按钮后,程序会打开浏览器并请求用户登录,登录成功后,浏览器会跳转回应用并附带一个授权码。

      const clientId = 'your_client_id';
      
      function login() {
        const url = `https://${domain}/authorize?
          audience=${clientId}&
          response_type=code&
          scope=openid%20email%20profile& // 可选参数
          redirect_uri=${redirectUri}`;
      
        window.location = url;
      }
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10

      其中 domain 表示 OAuth 服务提供商的域名,redirectUri 表示用户登录成功后的跳转地址。注意,redirect_uri 需要和 OAuth 应用中设置的 Callback URLs 一致。

      授权码获取成功后,我们就可以开始获取访问令牌。

      使用授权码获取访问令牌

      我们需要向 OAuth 服务提供商请求访问令牌。访问令牌会包含用户的身份信息,在 OAuth 授权的过程中,我们需要发送请求到授权服务器,并附带授权码、client_id 和 client_secret。

      async function getAccessToken(authCode) {
        try {
          const response = await fetch(`https://${domain}/oauth/token`, {
            method: 'POST',
            headers: {
              'Content-Type': 'application/json'
            },
            body: JSON.stringify({
              client_id: clientId,
              client_secret: clientSecret,
              grant_type: 'authorization_code',
              code: authCode,
              redirect_uri: redirectUri
            })
          });
      
          if (!response.ok) {
            throw new Error('Could not retrieve access token');
          }
      
          return await response.json();
        } catch (error) {
          console.error(error);
        }
      }
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24

      其中 domain 表示 OAuth 服务提供商的域名,redirectUri 表示用户登录成功后的跳转地址。注意,redirect_uri 需要和 OAuth 应用中设置的 Callback URLs 一致。

      请求成功后,服务端会返回访问令牌,包括 access_token、expires_in、refresh_token 等属性。

      {
        "access_token": "<KEY>",
        "expires_in": 3600,
        "token_type": "Bearer",
        "scope": "openid profile email offline_access",
        "refresh_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYmYiOjE2MTMzNjMwOTksImV4cCI6MTYxMzU0MDMxOSwiaWF0IjoxNjEzMzYzMDk5LCJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IlVzZXIiLCJhdWQiOiJyZXNldCJ9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ",
        "id_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Ik1uYmljbyJ9.eyJhY3IiOiIxIiwiYW1yIjpbInBsYWluLnJlYWQiXX0.YsNpZzb_qyJaDcn3L-hfw_tT0W2NtSOfQjOX1qnoLiJpSwcEZCVxRmGzfhEotyjVB7eamZEmowrKGUDIpYw9uxTtQ"
      }
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7

      此处省略了 refresh_token 和 id_token 属性,它们是 OAuth 协议中的可选属性,用于扩展 OAuth 功能。refresh_token 可以用来获取新的访问令牌,id_token 可以用来校验用户身份。

      验证访问令牌

      应用收到访问令牌后,需要检查其有效性。我们需要向 OAuth 服务提供商发送检查访问令牌的请求。

      async function verifyAccessToken(accessToken) {
        try {
          const response = await fetch(`https://${domain}/userinfo`, {
            method: 'GET',
            headers: {
              'Authorization': `Bearer ${accessToken}`
            }
          });
      
          if (!response.ok) {
            throw new Error('Could not verify access token');
          }
      
          return await response.json();
        } catch (error) {
          console.error(error);
        }
      }
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17

      其中 domain 表示 OAuth 服务提供商的域名,请求成功后,服务端会返回关于用户的详细信息,如用户 ID、昵称、邮箱等。

      如果访问令牌有效,我们就可以认为用户已登录成功。如果访问令牌已经失效,我们需要向 OAuth 服务提供商请求更新访问令牌。

      更新访问令牌

      如果访问令牌已经失效,则需要向 OAuth 服务提供商请求更新访问令牌。更新访问令牌的步骤如下:

      1. 发送更新访问令牌的请求。
      2. 服务端验证授权码和更新令牌。
      3. 生成新访问令牌并返回给应用。
      async function updateAccessToken(refreshToken) {
        try {
          const response = await fetch(`https://${domain}/oauth/token`, {
            method: 'POST',
            headers: {
              'Content-Type': 'application/json'
            },
            body: JSON.stringify({
              client_id: clientId,
              client_secret: clientSecret,
              grant_type:'refresh_token',
              refresh_token: refreshToken
            })
          });
      
          if (!response.ok) {
            throw new Error('Could not retrieve updated access token');
          }
      
          return await response.json();
        } catch (error) {
          console.error(error);
        }
      }
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23

      其中 domain 表示 OAuth 服务提供商的域名,refreshToken 就是之前获取到的 refresh_token。请求成功后,服务端会返回新的访问令牌。

      至此,我们完成了用户登录和认证的流程。用户第一次登录的时候,我们需要请求用户授权,获得授权码,再向 OAuth 服务提供商请求访问令牌。用户访问下次需要访问我们的应用时,我们就不需要再去请求用户授权,而是直接使用已有的访问令牌,并校验其有效性。如果访问令牌已经失效,则需要更新访问令牌。

      4.具体代码实例和详细解释说明

      下面,我们以一个简单的 React 应用来演示如何实现基于 OAuth 的用户认证。本例仅演示基本功能,但足够说明问题。

      安装依赖

      首先,我们需要安装几个依赖包:react、react-dom、@auth0/auth0-spa-js。

      npm install react react-dom @auth0/auth0-spa-js

        引入 Auth0 SDK

        然后,我们需要引用 Auth0 的 JavaScript SDK 文件,该文件是我们用来与 Auth0 服务交互的主要文件。

        <script src="https://cdn.auth0.com/auth0-spa-js/1.14/auth0-spa-js.production.js"></script>

          创建 Auth0 实例

          接着,我们创建一个 Auth0 实例。

          // authConfig.js
          export default {
            domain: 'your_domain', // 替换为你的 Auth0 域名前缀
            client_id: 'your_client_id', // 替换为你的 Auth0 应用 ID
            redirect_uri: `${window.location.origin}/callback` // 回调页路径,通常应该和您在 Auth0 控制台中配置的保持一致
          };
          
          // index.js
          import authConfig from './authConfig';
          
          const auth0 = createAuth0Client({
           ...authConfig,
          });
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12

          redirect_uri 是应用登录成功后会重定向回您的应用的路径。通常应该设置为和您在 Auth0 控制台中配置的一致。

          登录组件

          用户需要登录才能访问我们的应用,因此我们需要提供登录功能。下面是一个登录组件的例子。

          import React, { useState } from'react';
          
          const Login = () => {
            const [isLoading, setIsLoading] = useState(false);
          
            const handleLogin = async event => {
              event.preventDefault();
              setIsLoading(true);
              await auth0.loginWithRedirect({});
              setIsLoading(false);
            };
          
            return isLoading? (
              <div className="loader">loading...</div>
            ) : (
              <button onClick={handleLogin}>Log in</button>
            );
          };
          
          export default Login;
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
          • 16
          • 17
          • 18
          • 19

          这里的 handleLogin 方法会触发登录操作。

          注销组件

          用户登录后,需要提供注销功能。下面是一个注销组件的例子。

          import React, { useState } from'react';
          
          const Logout = () => {
            const [isLoading, setIsLoading] = useState(false);
          
            const handleLogout = async event => {
              event.preventDefault();
              setIsLoading(true);
              await auth0.logout({
                returnTo: `${window.location.origin}`,
                client_id: process.env.REACT_APP_AUTH0_CLIENT_ID || '' // optional parameter to logout from all devices
              });
              setIsLoading(false);
            };
          
            return isLoading? (
              <div className="loader">loading...</div>
            ) : (
              <button onClick={handleLogout}>Log out</button>
            );
          };
          
          export default Logout;
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
          • 16
          • 17
          • 18
          • 19
          • 20
          • 21
          • 22

          这里的 handleLogout 方法会触发注销操作。returnTo 参数可以设置为登出后返回的页面。

          AuthProvider 组件

          我们需要封装一个 AuthProvider 组件,它负责管理用户登录状态,包括登录和退出。下面是一个示例。

          import React, { useEffect, useState } from'react';
          import auth0 from '@auth0/auth0-spa-js';
          
          const AuthProvider = ({ children }) => {
            const [isAuthenticated, setIsAuthenticated] = useState();
            const [isLoading, setIsLoading] = useState(true);
            const [error, setError] = useState();
          
            useEffect(() => {
              let isMounted = true;
          
              const initAuth = async () => {
                try {
                  const authenticated = await auth0.isAuthenticated();
          
                  if (authenticated && isMounted) {
                    setIsAuthenticated(true);
                  } else if (isMounted) {
                    setIsAuthenticated(false);
                  }
                } catch (e) {
                  setError(e);
                }
          
                setIsLoading(false);
              };
          
              initAuth();
          
              return () => {
                isMounted = false;
              };
            }, []);
          
            const login = async () => {
              try {
                await auth0.loginWithRedirect({});
              } catch (e) {
                setError(e);
              }
            };
          
            const logout = () => {
              auth0.logout({
                returnTo: `${window.location.origin}`,
                client_id: process.env.REACT_APP_AUTH0_CLIENT_ID || '', // optional parameter to logout from all devices
              });
              setIsAuthenticated(false);
            };
          
            return!isLoading && error === undefined? (
              <AuthContext.Provider value={{ isAuthenticated, login, logout }}>
                {children}
              </AuthContext.Provider>
            ) : null;
          };
          
          export default AuthProvider;
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
          • 16
          • 17
          • 18
          • 19
          • 20
          • 21
          • 22
          • 23
          • 24
          • 25
          • 26
          • 27
          • 28
          • 29
          • 30
          • 31
          • 32
          • 33
          • 34
          • 35
          • 36
          • 37
          • 38
          • 39
          • 40
          • 41
          • 42
          • 43
          • 44
          • 45
          • 46
          • 47
          • 48
          • 49
          • 50
          • 51
          • 52
          • 53
          • 54
          • 55
          • 56
          • 57

          这里的 useEffect 会在组件渲染时执行,判断当前用户是否已登录,并初始化组件状态。setIsAuthenticated 会设为 true 或 false。

          login 方法会触发登录操作,logout 方法会触发注销操作,并清空用户登录状态。

          AuthContext 会存放用户登录状态,包括 isAuthenticated、login 和 logout 方法。

          ProtectedRoute 组件

          ProtectedRoute 组件是一个高阶组件,用于保护特定的路由。下面是一个示例。

          import React from'react';
          import { useLocation } from'react-router-dom';
          import { useSelector } from'react-redux';
          import { Redirect } from'react-router-dom';
          
          const ProtectedRoute = ({ component: Component,...rest }) => {
            const location = useLocation();
            const user = useSelector(state => state.userReducer.user);
          
            const renderComponent = props => {
              if (!user?.sub) {
                return <Redirect to="/login" />;
              }
          
              return <Component {...props} />;
            };
          
            return (
              <Route {...rest} render={renderComponent} />
            );
          };
          
          export default ProtectedRoute;
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
          • 16
          • 17
          • 18
          • 19
          • 20
          • 21
          • 22

          ProtectedRoute 接收 component 属性,它代表受保护的组件。useLocation hook 会获取当前路由位置, useSelector 会获取 Redux store 中的用户数据。

          如果当前用户未登录,则渲染 Redirect 组件,重定向到登录页面。如果用户已登录,则渲染受保护的组件。

          登录回调页面

          登录成功后,Auth0 会重定向到登录回调页面,我们需要监听回调页面的跳转,并解析访问令牌。下面是一个示例。

          import React, { useEffect } from'react';
          import { useDispatch } from'react-redux';
          import auth0 from '@auth0/auth0-spa-js';
          import history from '../history';
          import { setUser } from '../../store/actions/userActions';
          
          const Callback = () => {
            const dispatch = useDispatch();
          
            useEffect(() => {
              const fn = async () => {
                const query = window.location.search;
                const urlParams = new URLSearchParams(query);
                const accessToken = urlParams.get('access_token');
          
                if (!accessToken) {
                  return history.push('/');
                }
          
                try {
                  const decodedToken = jwtDecode(accessToken);
                  localStorage.setItem('access_token', accessToken);
          
                  if (decodedToken['https://example.com/roles']!== 'admin') {
                    alert('You do not have permission to access this page.');
                    return history.push('/');
                  }
          
                  const userInfo = await auth0.getUserInfo();
                  dispatch(setUser(userInfo));
                  history.push('/home');
                } catch (e) {
                  console.log(e);
                  return history.push('/');
                }
              };
          
              fn();
            }, [dispatch]);
          
            return <div>Loading...</div>;
          };
          
          export default Callback;
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
          • 16
          • 17
          • 18
          • 19
          • 20
          • 21
          • 22
          • 23
          • 24
          • 25
          • 26
          • 27
          • 28
          • 29
          • 30
          • 31
          • 32
          • 33
          • 34
          • 35
          • 36
          • 37
          • 38
          • 39
          • 40
          • 41
          • 42
          • 43

          这里的 useEffect 会在组件渲染时执行,并解析查询字符串中的 access_token。如果 access_token 不存在,则渲染首页。如果 access_token 存在,则尝试解码 access_token,并存储在本地缓存中。如果 decodedToken 没有 admin 角色,则渲染首页。如果 decodedToken 有 admin 角色,则获取用户信息,并设置 Redux 的 userReducer 状态,最后重定向到主页。

          5.未来发展趋势与挑战

          虽然 OAuth 和 JWT 在用户认证领域扮演着举足轻重的角色,但基于 OAuth 和 JWT 的用户认证仍然是不够的。当前仍然有很多潜在的攻击面,比如 CSRF 漏洞、跨站请求伪造、Session 劫持等等。为了更加安全地保障用户数据的安全,我们需要进一步提升用户隐私和用户数据安全。

          另外,基于 OAuth 和 JWT 的用户认证还不能完全解决物理单点登录的问题。当用户把设备分离出网络时,即使他们同属一个网络,他们也无法共享登录状态。这就需要我们考虑更加复杂的单点登录解决方案,比如双因子认证、多因子认证等。

          声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/513647
          推荐阅读
          相关标签
            

          闽ICP备14008679号