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"; }
额外检查项
- 确保Spring Security的
SecurityContextHolder默认存储策略是MODE_INHERITABLETHREADLOCAL或MODE_THREADLOCAL(默认是前者),如果自定义了存储策略,可能导致无法正确获取会话中的认证信息。 - 登录成功后,Spring会自动将Authentication存入SecurityContext,你可以在登录成功后打印日志确认认证信息是否正确存入。
内容的提问来源于stack exchange,提问作者SERKAN AYDOGDU
相关产品推荐
相关产品推荐

