文章目录
- OpenID Connect (OIDC)
- 3种 授权模式
- 【服务端】express 集成OIDC
- 【前端】react 集成OIDC
- oidc-client-js库 原生集成
- react-oidc-context 库
- 非组件获取user信息
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种 授权模式
- 授权码流程
前端登录,
后端返回授权码(密钥),【前端需要安全存储密钥】
授权码换ID Token、Access Token,【可以反复刷新token】
前端保存并携带Token
- 隐式流程
前端登录,
后端返回ID Token、Access Token,
前端保存并携带Token
适用于 浏览器前端 无法安全存储密钥 - 混合流程
【服务端】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);
}