文章目录
-
- [OpenID Connect (OIDC)](#OpenID Connect (OIDC))
-
- [3种 授权模式](#3种 授权模式)
- [【服务端】express 集成OIDC](#【服务端】express 集成OIDC)
- [【前端】react 集成OIDC](#【前端】react 集成OIDC)
-
- [oidc-client-js库 原生集成](#oidc-client-js库 原生集成)
- [react-oidc-context 库](#react-oidc-context 库)
OAuth 2.0 协议主要用于资源授权。
OpenID Connect (OIDC)
开放身份连接,是基于 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 前端登录,
2 后端返回授权码(密钥) ,【前端需要安全存储密钥】
3 授权码换token ,ID Token、Access Token,【可以反复刷新token】
4 前端保存并携带Token
- 隐式流程
1 前端登录,
2 后端返回token ,ID Token、Access Token,
3 前端保存并携带Token
适用于 浏览器前端 无法安全存储密钥 - 混合流程
【服务端】express 集成OIDC
express-openid-connect 库
- 应用配置
js
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()
对指定接口进行 身份验证
js
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
库
sh
npm install oidc-client-js
- 初始化 OIDC 客户端实例
js
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
js
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
ts
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 />);
- 登录逻辑
ts
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对象,然后得到userconst auth = useAuth();
const token = auth.user?.access_token;
非组件获取user信息
非组件不能使用hook,只能从userStore
配置中获取 user信息
ts
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);
}