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

Spring Security中Authentication获取为null的问题求助

问题描述

我尝试在Controller类中获取已登录用户的Authentication,通过Thymeleaf的th:replace指令在home.html中加载对应的uprojects.htm片段,但发现获取到的Authentication始终为null,无法解决该问题。


相关代码

控制器类(/home映射包含用户认证状态检查)

private UserService userService;

Authentication auth = SecurityContextHolder.getContext().getAuthentication();
@RequestMapping("/home")
public String displayHomePage (Model model){
    if(auth != null && !(auth instanceof AnonymousAuthenticationToken) && auth.isAuthenticated()){
        model.addAttribute("template", "uprojects");
    } else {
        model.addAttribute("template", "login");
    }
    return "home";
}
@RequestMapping("/nfeatures")
public String displayNFeaturesPage (Model model){
    model.addAttribute("template","nfeatures");
    return "home";
}

@RequestMapping("/pricing")
public String displayPricingPage (Model model){
    model.addAttribute("template","pricing");
    return "home";
}
@GetMapping("/registration")
public String registrationForm(Model model) {
    UserDto user = new UserDto();
    model.addAttribute("user", user);
    model.addAttribute("template","registration");
    return "home";
}
@PostMapping("/registration")
public String registration(
        @Valid @ModelAttribute("user") UserDto userDto,
        BindingResult result,
        Model model) {
    User existingUser = userService.findUserByEmail(userDto.getEmail());

    if (existingUser != null)
        result.rejectValue("email", null,
                "User already registered !!!");

    if (result.hasErrors()) {
        model.addAttribute("user", userDto);
        return "/registration";
    }

    userService.saveUser(userDto);
    return "redirect:/registration?success";
}

Spring Security 6配置类

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public static PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }
    @Bean
    SecurityFilterChain defaultSecurityFilterChain (HttpSecurity http) throws Exception{
        http.authorizeHttpRequests((requests) -> requests
                .requestMatchers("/registration/**").permitAll()
                .requestMatchers("/home/**").permitAll()
                        .requestMatchers("/fragments/**").permitAll()
                        .requestMatchers("/nfeatures/**").permitAll()
                        .requestMatchers("/pricing/**").permitAll()
                        .requestMatchers("/assets/**").permitAll()
                .requestMatchers("/user/**", "/uprojects/**").hasAnyRole("USER", "ADMIN")
                .requestMatchers("/admin/**").hasAnyRole("ADMIN")
                .anyRequest().authenticated()
        )
                .formLogin((form) -> form
                        .loginPage("/home")
                        .loginProcessingUrl("/login")
                        .defaultSuccessUrl("/home")
                        .permitAll()
                )
                .logout((logout) -> logout.permitAll())
                .exceptionHandling().accessDeniedPage("/access-denied");
        return http.build();
    }
}

应用主入口home.html页面

<!doctype html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
      xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head>
    <meta charset="utf-8">
    <meta content="width=device-width, initial-scale=1" name="viewport">
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet"
          integrity="sha384-GLhlTQ8iRABdZLl6O3oVMWSktQOp6b7In1Zl3/Jr59b6EGGoI1aFkw7cmDA6j6gD" crossorigin="anonymous">
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js"
            integrity="sha384-w76AqPfDkMBDXo30jS1Sgez6pr3x5MlQ1ZAGC+nuZB+EYdgRZgiwxhTBTkF7CXvN" crossorigin="anonymous"></script>
    <title>Login</title>
</head>
<body>

<div class="container">
    <div th:replace="fragments/header :: header"></div>
    <div th:replace="${template} :: ${template}"></div>
</div>
<footer th:replace="fragments/footer :: footer"></footer>
</body>
</html>

解决方案

核心问题:Authentication变量初始化位置错误

你把Authentication auth = SecurityContextHolder.getContext().getAuthentication();定义为Controller的成员变量,这意味着这段代码只会在Controller类实例化时执行一次。而Controller默认是单例的,所以这个auth变量会一直保留实例化时的初始值(此时还没有用户登录,自然是null),后续请求不会重新获取最新的认证信息。

修复方案1:将获取Authentication的逻辑移到方法内部

修改displayHomePage方法,把获取认证信息的代码放到方法里,确保每次请求都能拿到最新的Authentication:

@RequestMapping("/home")
public String displayHomePage (Model model){
    Authentication auth = SecurityContextHolder.getContext().getAuthentication();
    if(auth != null && !(auth instanceof AnonymousAuthenticationToken) && auth.isAuthenticated()){
        model.addAttribute("template", "uprojects");
    } else {
        model.addAttribute("template", "login");
    }
    return "home";
}

修复方案2:直接通过方法参数注入Authentication(更简洁)

Spring支持直接在Controller方法参数中注入当前用户的Authentication,无需手动调用SecurityContextHolder:

@RequestMapping("/home")
public String displayHomePage (Model model, Authentication auth){
    if(auth != null && !(auth instanceof AnonymousAuthenticationToken) && auth.isAuthenticated()){
        model.addAttribute("template", "uprojects");
    } else {
        model.addAttribute("template", "login");
    }
    return "home";
}

额外检查项

  1. 确保Spring Security的SecurityContextHolder默认存储策略是MODE_INHERITABLETHREADLOCAL或MODE_THREADLOCAL(默认是前者),如果自定义了存储策略,可能导致无法正确获取会话中的认证信息。
  2. 登录成功后,Spring会自动将Authentication存入SecurityContext,你可以在登录成功后打印日志确认认证信息是否正确存入。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 21:37:54