前端react集成OIDC

发布于:2024-08-11 ⋅ 阅读:(179) ⋅ 点赞:(0)

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. 授权码流程
    前端登录,
    后端返回授权码(密钥),【前端需要安全存储密钥】
    授权码换ID Token、Access Token,【可以反复刷新token】
    前端保存并携带Token
    在这里插入图片描述
  2. 隐式流程
    前端登录,
    后端返回ID Token、Access Token,
    前端保存并携带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: 'bop.session',
        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 库

封装了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 {...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]);
  
  // 登录成功后跳转
  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
非组件获取user信息

组件内可以使用 const auth = useAuth(); 获取auth对象,然后得到user
非组件不能使用hook,只能从userStore配置中获取
user信息

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

function getUser() {
  const oidcStorage = localStorage.getItem(
    `oidc.user:${AUTH_URL}:${AUTH_CLIENT_ID}`
  );
  if (!oidcStorage) {
    return null;
  }
  return User.fromStorageString(oidcStorage);
}

网站公告

今日签到

点亮在社区的每一天
去签到