前端react集成OIDC

OAuth 2.0 协议主要用于资源授权。

OpenID Connect (OIDC)

https://openid.net/specs/openid-connect-core-1_0.html

开放身份连接,是基于 OAuth 2.0协议的一个扩展。通过扩展身份层,来实现去中心化的身份验证服务。
它允许客户端验证用户身份,并获取一些基本用户信息。

使用 OIDC,应用程序可以简化身份验证和授权流程
实现单点登录或先鉴权用户再返回资源

  • 主要包括以下角色:
    OpenID Provider(OP): 指授权服务器,负责签发 Id Token。Authing 是 OpenID Provider。
    End User(EU):资源所有者,即用户;
    ID Token:包含 EU 身份认证信息的 JWT 格式数据,是用户的身份凭证;

  • 主要特点包括:
    身份验证: OIDC 允许客户端应用程序验证用户的身份,确保只有授权的用户才能访问应用程序。
    用户信息: OIDC 提供了一种标准化的方式来获取有关已验证用户的基本信息,如用户名、电子邮件地址等。
    单点登录 (SSO): OIDC 支持单点登录,使用户只需登录一次就可以访问多个应用程序。
    安全性: OIDC 建立在 OAuth 2.0 的安全基础之上,提供了更强的安全性和隐私保护。

3种 授权模式

  1. 授权码流程
    1 前端登录,
    2 后端返回授权码(密钥),【前端需要安全存储密钥】
    3 授权码换token,ID Token、Access Token,【可以反复刷新token】
    4 前端保存并携带Token
    在这里插入图片描述
  2. 隐式流程
    1 前端登录,
    2 后端返回token,ID Token、Access Token,
    3 前端保存并携带Token
    在这里插入图片描述
    适用于 浏览器前端 无法安全存储密钥
  3. 混合流程

【服务端】express 集成OIDC

express-openid-connect 库

  • 应用配置
const { auth } = require('express-openid-connect');

  const app = express();
  
  app.use(
    auth({
      issuerBaseURL: 'OIDC url',
      baseURL: '授权登录成功后回调 url',
      clientID: 'AUTH_CLIENT_ID',
      secret: 'AUTH_CLIENT_SECRET',
      idpLogout: true,
      session: {
        store: redisStore,
        name: '',
        cookie: {
          domain: process.env.COOKIE_DOMAIN,
        },
      },
    })
  );
  • requiresAuth() 对指定接口进行 身份验证
var router = require('express').Router();
const { requiresAuth } = require('express-openid-connect');

app.get('/profile', requiresAuth(), (req, res) => {
  res.send(`hello ${req.oidc.user.name}`);
});

【前端】react 集成OIDC

oidc-client-js库 原生集成

  • 安装 oidc-client-js / oidc-client-ts
npm install oidc-client-js
  • 初始化 OIDC 客户端实例
import { UserManager, WebStorageStateStore } from 'oidc-client-js';

// 配置 userManager
const userManager = new UserManager({
  authority: 'OIDC 提供商的 URL',
  client_id: ' OIDC 提供商处注册的唯一 client_id',
  redirect_uri: 'OIDC 认证后回调url',
  // 认证信息存储到 localStorage
  userStore: new WebStorageStateStore({ store: window.localStorage })
});

function Root() {
  const [pageLoaded, setLoaded] = useState(false);
  
  // 在应用程序启动时初始化 OIDC 客户端
  useEffect(() => {
    userManager.getUser().then((user) => {
      if (user && !user.expired) {
        // 如果已经登录,将 user 对象存储在组件状态中
        setUser(user);
      } else {
      
        // OIDC 登录
        userManager.signinRedirect();
      }
      setLoaded(true);
    });
  }, []);
  • 使用 OIDC 认证的 token
import { userManager } from './oidc-config';

async function fetchData() {
   const user = await userManager.getUser();
   if (user && !user.expired) {
     // 如果已登录,在请求头中附带 access_token
     const response = await axios.get('/api/data', {
       headers: {
         'Authorization': `Bearer ${user.access_token}`
       }
    }
  }
}

react-oidc-context 库

react-oidc-context封装了oidc-client-ts,使用更简单

  • AuthProvider 集成 OIDC
import { AuthProvider } from 'react-oidc-context';
import { WebStorageStateStore } from 'oidc-client-ts';

const oidcConfig = {
  authority: 'OIDC 提供商的 URL',
  client_id: ' OIDC 提供商处注册的唯一 client_id',
  redirect_uri: 'OIDC 认证后回调url',
  client_secret: 'client 密钥',
  automaticSilentRenew: true,
  loadUserInfo: true,
  // 认证信息存储到 localStorage
  userStore: new WebStorageStateStore({
    store: localStorage,
  }),
  onSigninCallback: (): void => {
    window.history.replaceState({}, document.title, window.location.pathname);
  },
};

function Root() {
  const [pageLoaded, setLoaded] = useState(false);
  return (
    <React.StrictMode>
      {!pageLoaded ? <PageLoadingSpinner /> : null}
      
      <!-- 在外层使用 AuthProvider 集成OIDC -->
      <AuthProvider {...oidcConfig}>
          <RouterProvider router={routes(setLoaded)} />
      </AuthProvider>
      
    </React.StrictMode>
  );
}

ReactDOM.createRoot(document.getElementById('root')!).render(<Root />);
  • 登录逻辑
  const auth = useAuth();
  
  useEffect(() => {
    if (
      !hasAuthParams() &&
      !auth.isAuthenticated &&
      !auth.activeNavigator &&
      !auth.isLoading
    ) {
    
      // OIDC 登录
      auth.signinRedirect();
    }
  }, [auth]);
  
  // 登录成功后跳转 page
  if (auth.isAuthenticated) {
    return <>{props.children}</>;
  }
  
  // 登录报错处理
  if (auth.error) {
    console.log(auth.error);
  }

对应的http请求

- 跳转到登录页
{{authority}}/.well-known/openid-configuration
- 获取token
{{authority}}/protocol/openid-connect/token
- 获取user info
{{authority}}/protocol/openid-connect/userinfo
  • 组件Component 内获取登录信息
    组件内可以使用 const auth = useAuth(); 获取auth对象,然后得到user
const auth = useAuth();
const token = auth.user?.access_token;
非组件获取user信息

非组件不能使用hook,只能从userStore配置中获取 user信息

import { User } from 'oidc-client-ts';

function getUser() {
  // 之前把信息存储在了localStorage,就从localStorage获取
  const oidcStorage = localStorage.getItem(
    `oidc.user:${AUTH_URL}:${AUTH_CLIENT_ID}`
  );
  if (!oidcStorage) {
    return null;
  }
  return User.fromStorageString(oidcStorage);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xyc1211

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值