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

如何使用ReactJS、OAuth2.0与Spring Boot管理会话及登出失效?

React/React Native + Spring Boot OAuth2:会话管理与登出实现

一、后端(Spring Boot OAuth2)处理逻辑

1. 会话管理核心

基于OAuth2授权码模式,后端需维护两类会话数据:

  • 用户与Google的授权关系(通过access/refresh token)
  • 后端自身的用户会话(若依赖HttpSession,或通过TokenStore存储token记录)

2. 登出实现步骤

(1)撤销Google端授权

用户登出时,需主动调用Google的授权撤销接口,避免用户下次登录直接跳过授权流程:

// 在自定义LogoutHandler中执行撤销逻辑
String revokeUrl = "https://accounts.google.com/o/oauth2/revoke?token=" + accessToken;
HttpURLConnection connection = (HttpURLConnection) new URL(revokeUrl).openConnection();
connection.setRequestMethod("POST");
connection.getResponseCode(); // 触发授权撤销请求

(2)清理后端会话数据

  • 若使用TokenStore(如JdbcTokenStore/InMemoryTokenStore),删除该用户的access/refresh token记录:
    tokenStore.removeAccessToken(accessToken);
    tokenStore.removeRefreshToken(refreshToken);
    
  • 若依赖HttpSession,调用session.invalidate()销毁会话。

(3)配置Spring Security登出端点

自定义登出接口,适配前后端的响应需求:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private ClientRegistrationRepository clientRegistrationRepository;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests().anyRequest().authenticated()
            .and()
            .oauth2Login() // 已配置的Google登录逻辑
            .and()
            .logout()
                .logoutUrl("/api/logout") // 自定义登出接口地址
                .addLogoutHandler(new OAuth2LogoutHandler(clientRegistrationRepository))
                .logoutSuccessHandler((req, res, auth) -> {
                    res.setStatus(HttpStatus.OK.value());
                    res.getWriter().write("{\"msg\":\"登出成功\"}");
                });
    }

    // 自定义OAuth2登出处理器,处理Google授权撤销
    static class OAuth2LogoutHandler implements LogoutHandler {
        private final ClientRegistrationRepository repo;

        public OAuth2LogoutHandler(ClientRegistrationRepository repo) {
            this.repo = repo;
        }

        @Override
        public void logout(HttpServletRequest req, HttpServletResponse res, Authentication auth) {
            if (auth instanceof OAuth2AuthenticationToken) {
                OAuth2AuthenticationToken oauthToken = (OAuth2AuthenticationToken) auth;
                ClientRegistration client = repo.findByRegistrationId(oauthToken.getAuthorizedClientRegistrationId());
                if (client != null && "google".equals(client.getRegistrationId())) {
                    String accessToken = oauthToken.getPrincipal().getAttribute("access_token");
                    if (accessToken != null) {
                        try {
                            URL url = new URL("https://accounts.google.com/o/oauth2/revoke?token=" + accessToken);
                            ((HttpURLConnection) url.openConnection()).getResponseCode();
                        } catch (IOException e) {
                            // 日志记录异常即可,不阻塞登出流程
                        }
                    }
                }
            }
        }
    }
}

二、前端会话管理与登出

1. Web端(React)

(1)会话存储选择

  • 需持久化登录状态:用localStorage存储access token、用户信息(注意XSS风险,可配合HttpOnly cookie优化)
  • 关闭浏览器即失效:用sessionStorage

(2)全局状态管理

用Context API或Redux维护登录状态,方便组件共享:

// AuthContext.jsx
import { createContext, useContext, useState } from 'react';

const AuthContext = createContext();

export const AuthProvider = ({ children }) => {
    const [user, setUser] = useState(() => {
        const savedUser = localStorage.getItem('userInfo');
        return savedUser ? JSON.parse(savedUser) : null;
    });

    const logout = () => setUser(null);

    return (
        <AuthContext.Provider value={{ user, logout }}>
            {children}
        </AuthContext.Provider>
    );
};

export const useAuth = () => useContext(AuthContext);

(3)登出逻辑

// LogoutButton.jsx
import { useAuth } from './AuthContext';

const LogoutButton = () => {
    const { logout } = useAuth();

    const handleLogout = async () => {
        try {
            await fetch('/api/logout', {
                method: 'POST',
                credentials: 'include' // 若用HttpSession需携带cookie
            });
            // 清理本地存储
            localStorage.removeItem('accessToken');
            localStorage.removeItem('userInfo');
            // 重置状态并跳转登录页
            logout();
            window.location.href = '/login';
        } catch (err) {
            console.error('登出失败:', err);
        }
    };

    return <button onClick={handleLogout}>登出</button>;
};

export default LogoutButton;

2. 移动端(React Native)

(1)会话存储选择

用react-native-keychain(加密存储,比AsyncStorage更安全)保存token,避免明文泄露:

npm install react-native-keychain

(2)登出逻辑

// LogoutButton.jsx
import React from 'react';
import { Button, Alert } from 'react-native';
import Keychain from 'react-native-keychain';
import { useAuth } from './AuthContext';
import { useNavigation } from '@react-navigation/native';

const LogoutButton = () => {
    const { logout } = useAuth();
    const navigation = useNavigation();

    const handleLogout = async () => {
        try {
            const credentials = await Keychain.getGenericPassword();
            if (credentials) {
                // 调用后端登出接口
                await fetch('https://你的后端域名/api/logout', {
                    method: 'POST',
                    headers: {
                        'Authorization': `Bearer ${credentials.password}`
                    }
                });
                // 清理加密存储
                await Keychain.resetGenericPassword();
                // 重置状态并导航到登录页
                logout();
                navigation.navigate('Login');
            }
        } catch (err) {
            Alert.alert('错误', '登出失败,请重试');
        }
    };

    return <Button title="登出" onPress={handleLogout} />;
};

export default LogoutButton;

三、关键注意事项

  • 安全防护:Web端优先用HttpOnly、Secure的cookie存储token,避免XSS窃取;移动端必须用加密存储方案。
  • 会话过期处理:前端请求接口时若收到401错误,自动尝试用refresh token刷新access token,失败则跳转登录页。
  • 跨域配置:后端需配置CORS,允许前端域名访问/api/logout接口,并允许携带credentials(若用cookie)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 23:12:08