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

如何让Spring应用在用户登录后保持登录状态直至点击登出?

Hey there! 针对你在Spring应用里实现登录状态持久化的需求,咱们直接用Spring Security来搞定就好——这是Spring生态里处理身份认证和会话管理的标准方案,既安全又省心。下面给你一步步拆解实现步骤:

核心思路

登录成功后,服务器会为用户创建一个会话(Session),并通过Cookie把会话ID返回给浏览器。之后浏览器每次请求都会带上这个Cookie,服务器就能识别出当前请求属于哪个登录用户,直到用户主动登出或者会话超时。

1. 先引入Spring Security依赖

如果用Maven,在pom.xml里加这段:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

Gradle的话就在build.gradle里加:

implementation 'org.springframework.boot:spring-boot-starter-security'

2. 配置Spring Security实现会话保持

创建一个配置类,比如SecurityConfig,用最新的SecurityFilterChain方式(旧的WebSecurityConfigurerAdapter已经过时啦):

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.config.http.SessionCreationPolicy;

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    private final UserDetailsService userDetailsService;

    public SecurityConfig(UserDetailsService userDetailsService) {
        this.userDetailsService = userDetailsService;
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            // 配置哪些请求需要认证,哪些不需要
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/register", "/login", "/css/**", "/js/**").permitAll() // 注册、登录页面和静态资源允许匿名访问
                .anyRequest().authenticated() // 其他所有请求都需要登录
            )
            // 配置登录逻辑
            .formLogin(form -> form
                .loginPage("/login") // 指定你的自定义登录页面路径
                .defaultSuccessUrl("/home", true) // 登录成功后跳转到首页
                .permitAll()
            )
            // 配置登出逻辑
            .logout(logout -> logout
                .logoutUrl("/logout") // 登出接口路径
                .logoutSuccessUrl("/login?logout") // 登出成功后跳转到登录页,带logout参数提示用户
                .invalidateHttpSession(true) // 登出时销毁会话
                .deleteCookies("JSESSIONID") // 删除会话Cookie
                .permitAll()
            )
            // 配置会话管理
            .sessionManagement(session -> session
                .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED) // 按需创建会话(默认值,明确写出来更清晰)
                .invalidSessionUrl("/login?invalid") // 会话失效时跳转到登录页
                .maximumSessions(1) // 同一用户只能同时登录1次(可选,根据需求调整)
                .expiredUrl("/login?expired") // 会话过期跳转页面
            )
            // 可选:开启"记住我"功能,用户关闭浏览器后下次打开仍保持登录
            .rememberMe(remember -> remember
                .key("your-unique-secret-key") // 自定义密钥,要保密
                .tokenValiditySeconds(86400 * 7) // 记住我有效期7天
                .userDetailsService(userDetailsService)
            );

        return http.build();
    }

    // 密码加密器:注册时要用这个加密密码,Spring Security登录时会自动解密比对
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

3. 实现UserDetailsService从MongoDB获取用户

你需要实现UserDetailsService接口,让Spring Security能从MongoDB里查询用户信息:

import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

@Service
public class CustomUserDetailsService implements UserDetailsService {

    private final UserRepository userRepository; // 你的MongoDB用户Repository

    public CustomUserDetailsService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        // 从MongoDB查询用户
        UserEntity user = userRepository.findByUsername(username)
                .orElseThrow(() -> new UsernameNotFoundException("用户不存在: " + username));

        // 把用户信息转换成Spring Security需要的UserDetails对象
        return User.withUsername(user.getUsername())
                .password(user.getPassword()) // 这里的密码必须是加密后的
                .roles("USER") // 根据你的用户角色配置,比如普通用户、管理员等
                .build();
    }
}

4. 前端配合实现登出按钮

在你的首页添加一个登出按钮,用POST请求提交到/logout(Spring Security默认的登出端点,也可以在配置里自定义):

<form action="/logout" method="post">
    <!-- Spring Security默认开启CSRF保护,需要带上CSRF令牌 -->
    <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}">
    <button type="submit">登出</button>
</form>

如果想用GET请求,需要在SecurityConfig里添加配置:.logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout", "GET")),但POST更安全,推荐用POST方式。

关键细节提醒

  • 密码必须加密存储:注册用户的时候,一定要用BCryptPasswordEncoder加密密码再存入MongoDB,不然Spring Security登录时会验证失败。
  • 会话安全:Spring Security默认会把会话Cookie设置为HttpOnly,防止XSS攻击;同时默认开启CSRF保护,避免跨站请求伪造。
  • 会话超时配置:可以在application.properties里设置server.servlet.session.timeout=30m,指定会话30分钟超时,超时后用户需要重新登录。

这样配置完之后,用户登录成功后就会保持登录状态,直到点击登出按钮或者会话超时,完全符合你的需求~

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 08:07:56