我目前在 Next.js 前端和 NestJS 后端应用程序中工作,并使用 @aws-amplify/ui-react 设置登录组件:
import { Amplify } from 'aws-amplify';
import { withAuthenticator } from '@aws-amplify/ui-react';
import '@aws-amplify/ui-react/styles.css';
import awsExports from './aws-exports';
Amplify.configure(awsExports);
function App({ signOut, user }) {
return (
<>
<h1>Hello {user.username}</h1>
<button onClick={signOut}>Sign out</button>
</>
);
}
export default withAuthenticator(App);
问题是我如何获取 jwt 令牌并将其传递给 axios 标头?
const response: any = await axios({
method: method as Method,
url: `http://localhost:3001/dev/${query}`,
headers: {
Authorization: `Bearer ${jwtToken}`,
'Content-Type': 'application/json',
},
data,
})
我尝试从本地存储获取令牌,但由于池 ID 和用户的原因,密钥发生了变化。
不知道 Next.js 的确切语法,但这就是我在 Angular (TypeScript) 中所做的。
import { Component } from "@angular/core";
import { Auth } from "aws-amplify";
import { UserService } from "src/app/services/user.service";
@Component({
selector: "app-profile",
templateUrl: "./profile.component.html",
styleUrls: ["./profile.component.sass"],
})
export class ProfileComponent {
constructor(private userService: UserService) {
Auth.currentSession().then((response) => {
var idToken = response.getIdToken().getJwtToken();
this.userService.getUserInfo(idToken).subscribe((response) => {
console.log(response);
});
});
}
}
export class UserService {
constructor(private http: HttpClient) {}
public getUserInfo(accessToken: string): Observable<Weather[]> {
var headers_object = new HttpHeaders().set("Authorization", "Bearer" + accessToken);
return this.http.get<Weather[]>(
"https://xxxxx.execute-api.ap-south-1.amazonaws.com/prod/User",
{
headers: headers_object,
}
);
}
}
查看示例 PAM 应用程序。它使用 React 应用程序并使用 Cognito 对用户进行身份验证。这个应用程序不使用放大。它使用 React、Cloudscape Design System 和 AWS SDK 并向 API 网关端点发出请求:
正如您在此图中看到的,React 应用程序允许用户通过 Cognito 调用登录。此应用程序使用从 Cognito 返回的令牌。一旦您理解了这一点 - 您就可以将其应用到您的项目中。
按照本示例中的说明操作后,您将获得包含登录屏幕的完整应用程序:
登录后 - 您会看到该应用程序:
看这个例子中的代码:
反应代码在这里:
https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/resources/clients/react/elros-pam
后端示例在这里:
https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/javav2/usecases/pam_source_files
这个问题可以通过创建全局上下文来解决,该上下文将当前令牌保持在其状态。
最终结果是像这样访问令牌:
import { AuthenticatedSessionContext } from './AuthenticatedSessionContext';
export const Example = () => {
const { session } = useContext(AuthenticatedSessionContext);
const jwt = session.tokens.idToken.toString();
// use the jwt in axios, etc.
};
这就是实现。
注意,引用的
AuthenticatedStatusContext
组件可在 https://stackoverflow.com/a/78375940 获取
import { createContext, useContext, useEffect, useState } from 'react';
import { fetchAuthSession } from 'aws-amplify/auth'; // https://docs.amplify.aws/javascript/build-a-backend/auth/manage-user-session/
import moment from 'moment';
import { AuthenticatedStatusContext } from './AuthenticatedStatusContext';
const getRefreshMilliseconds = (session) => {
// refresh 1 minute before actual expiry
const expiration = moment(session.credentials.expiration).subtract(1, 'minutes');
return Math.max(0, expiration - moment());
};
const defaultForceRefresh = false;
const defaultSession = undefined;
const defaultRefreshMilliseconds = undefined;
export const AuthenticatedSessionContext = createContext();
export const AuthenticatedSession = (props) => {
const { children } = props;
const { isAuthenticated } = useContext(AuthenticatedStatusContext);
const [forceRefresh, setForceRefresh] = useState(defaultForceRefresh);
const [session, setSession] = useState(defaultSession);
const [refreshMilliseconds, setRefreshMilliseconds] = useState(defaultRefreshMilliseconds);
const refreshSession = () => (
fetchAuthSession({forceRefresh})
.then((_session) => {
setForceRefresh(true); // force refresh just before actual expiry
setSession(_session);
setRefreshMilliseconds(getRefreshMilliseconds(_session));
}).catch(console.error)
);
useEffect(() => {
if (isAuthenticated && !session) {
// initial session grab
refreshSession();
} else if (!isAuthenticated && session) {
// clear session when not authenticated
setForceRefresh(defaultForceRefresh);
setSession(defaultSession);
setRefreshMilliseconds(defaultRefreshMilliseconds);
}
}, [isAuthenticated, session]);
useEffect(() => {
// schedule next refresh
if (refreshMilliseconds !== defaultRefreshMilliseconds) {
const refreshTimeout = setTimeout(refreshSession, refreshMilliseconds);
return () => clearTimeout(refreshTimeout);
}
}, [refreshMilliseconds]);
return (
<AuthenticatedSessionContext.Provider value={{session}}>
{children}
</AuthenticatedSessionContext.Provider>
);
};