You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Next.js+Redux-Observable的SSR认证流程实现问询(Cookie存令牌)

Got it, let's walk through implementing this authentication flow with Next.js, Redux-Observable, and SSR step by step. I’ll make sure each part aligns exactly with your requirements:

1. Define Auth Actions First

Start by creating clear action types and creators to handle the signin flow, token refresh, and state updates. Here’s how that might look:

// authActions.js
export const SIGNIN_REQUEST = 'auth/SIGNIN_REQUEST';
export const SIGNIN_SUCCESS = 'auth/SIGNIN_SUCCESS';
export const SIGNIN_FAILURE = 'auth/SIGNIN_FAILURE';
export const REFRESH_TOKEN_REQUEST = 'auth/REFRESH_TOKEN_REQUEST';

export const signinRequest = (credentials) => ({
  type: SIGNIN_REQUEST,
  payload: credentials, // e.g., { email, password }
});

export const signinSuccess = (authData) => ({
  type: SIGNIN_SUCCESS,
  payload: authData, // e.g., { token, user, refreshToken }
});

export const signinFailure = (error) => ({
  type: SIGNIN_FAILURE,
  payload: error,
});

export const refreshTokenRequest = (refreshToken) => ({
  type: REFRESH_TOKEN_REQUEST,
  payload: refreshToken,
});
2. Build the Core Signin Epic

This is where the main logic lives. We’ll use Redux-Observable to handle the async flow: calling the signin API, checking for expired tokens, and triggering a refresh if needed.

// authEpics.js
import { ofType } from 'redux-observable';
import { switchMap, catchError, map, tap } from 'rxjs/operators';
import { of } from 'rxjs';
import { signinSuccess, signinFailure, refreshTokenRequest } from './authActions';
import { setAuthCookie } from '../utils/cookieUtils'; // We'll create this utility next

export const signinEpic = (action$) =>
  action$.pipe(
    ofType(SIGNIN_REQUEST),
    switchMap(({ payload: credentials }) =>
      // Call your backend's auth/signin endpoint
      fetch('/api/auth/signin', {
        method: 'POST',
        body: JSON.stringify(credentials),
        headers: { 'Content-Type': 'application/json' },
      }).pipe(
        switchMap(async (response) => {
          if (!response.ok) {
            const error = await response.json();
            throw error;
          }
          return response.json();
        }),
        switchMap((authResponse) => {
          // Adjust this check to match your API's token expiration indicator
          if (authResponse.tokenExpired) {
            // Token is expired: trigger refresh with the returned refreshToken
            return of(refreshTokenRequest(authResponse.refreshToken));
          } else {
            // Token is valid: set cookie and dispatch success action
            return of(signinSuccess(authResponse)).pipe(
              tap(() => setAuthCookie(authResponse.token))
            );
          }
        }),
        catchError((error) => of(signinFailure(error)))
      )
    )
  );
3. Add the Refresh Token Epic

Now handle the token refresh flow: call the auth/refresh endpoint, update the cookie, and refresh the auth state.

export const refreshTokenEpic = (action$) =>
  action$.pipe(
    ofType(REFRESH_TOKEN_REQUEST),
    switchMap(({ payload: refreshToken }) =>
      fetch('/api/auth/refresh', {
        method: 'POST',
        body: JSON.stringify({ refreshToken }),
        headers: { 'Content-Type': 'application/json' },
      }).pipe(
        switchMap(async (response) => {
          if (!response.ok) {
            const error = await response.json();
            throw error;
          }
          return response.json();
        }),
        map((refreshResponse) => {
          // Set new token cookie and update auth state with fresh data
          setAuthCookie(refreshResponse.newToken);
          return signinSuccess(refreshResponse.newAuthData);
        }),
        catchError((error) => of(signinFailure(error)))
      )
    )
  );

Since we’re dealing with SSR, we need a utility that works both server-side (using Next.js's res object) and client-side (using document.cookie):

// cookieUtils.js
export const setAuthCookie = (token, res) => {
  // Configure cookie attributes based on your security needs
  const cookieOptions = [
    `authToken=${token}`,
    'Path=/',
    'HttpOnly', // Prevents client-side JS access (enhances security)
    'Secure', // Only send over HTTPS (enable in production)
    'SameSite=Strict',
  ].join('; ');

  if (typeof window === 'undefined') {
    // Server-side: use the response object to set cookie
    res.setHeader('Set-Cookie', cookieOptions);
  } else {
    // Client-side: set cookie directly
    document.cookie = cookieOptions;
  }
};
5. SSR-Specific Setup

When initializing your Redux store in Next.js, make sure to hydrate it with existing auth state from cookies (for server-rendered pages):

// store.js
import { createStore, applyMiddleware } from 'redux';
import { createEpicMiddleware } from 'redux-observable';
import { combineEpics } from 'redux-observable';
import authReducer from './authReducer';
import { signinEpic, refreshTokenEpic } from './authEpics';

const rootEpic = combineEpics(signinEpic, refreshTokenEpic);
const epicMiddleware = createEpicMiddleware();

export const initializeStore = (initialState = {}) => {
  const store = createStore(authReducer, initialState, applyMiddleware(epicMiddleware));
  epicMiddleware.run(rootEpic);
  return store;
};

Then, in a protected page’s getServerSideProps, you can hydrate the store with existing cookie data:

// pages/dashboard.js
import { initializeStore } from '../store';
import { signinSuccess } from '../authActions';

export async function getServerSideProps(context) {
  const { req, res } = context;
  const authToken = req.cookies.authToken;

  const store = initializeStore();
  if (authToken) {
    // Optional: Validate the token with your backend first before hydrating
    store.dispatch(signinSuccess({ token: authToken }));
  }

  return {
    props: { initialState: store.getState() },
  };
}
6. Connect to Your Login Component

Finally, wire up your login button to dispatch the signinRequest action:

// components/LoginForm.js
import { useDispatch } from 'react-redux';
import { signinRequest } from '../authActions';

export default function LoginForm() {
  const dispatch = useDispatch();

  const handleSubmit = (e) => {
    e.preventDefault();
    const credentials = {
      email: e.target.email.value,
      password: e.target.password.value,
    };
    dispatch(signinRequest(credentials));
  };

  return (
    <form onSubmit={handleSubmit}>
      <input type="email" name="email" placeholder="Email" />
      <input type="password" name="password" placeholder="Password" />
      <button type="submit">Login</button>
    </form>
  );
}

That’s the full flow! When the user clicks the login button, the signinRequest action triggers the epic, which handles the API calls, token expiration check, cookie setting, and state updates seamlessly—all while working with Next.js SSR.

内容的提问来源于stack exchange,提问作者littlechad

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 12:02:28