为啥 Spring Security getAuthentication() 在服务器上返回 null
Posted
技术标签:
【中文标题】为啥 Spring Security getAuthentication() 在服务器上返回 null【英文标题】:Why Spring Security getAuthentication() returns null on server为什么 Spring Security getAuthentication() 在服务器上返回 null 【发布时间】:2021-12-23 19:29:33 【问题描述】:我有一个 Web 应用程序并使用 Spring Security 进行身份验证。 所有页面都需要认证,但登录页面不需要认证。 在我的情况下,我在本地环境登录后成功访问了其他需要身份验证的页面。但是我在服务器环境中失败了。因为 getAuthentication() 返回 null。我不知道为什么 getAuthentication() 在服务器环境中返回 null。
登录后移动到 /foo 时(本地日志)
[2021-11-11 15:20:05:32506][http-nio-8080-exec-7] DEBUG org.springframework.security.authentication.dao.DaoAuthenticationProvider.createSuccessAuthentication - Authenticated user
[2021-11-11 15:20:05:32507][http-nio-8080-exec-7] DEBUG org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter.successfulAuthentication - Set SecurityContextHolder to UsernamePasswordAuthenticationToken [Principal=LoginUser(loginInfo=com.bar.entity.LoginInfoEntity@12234790), Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=null], Granted Authorities=[]]
[2021-11-11 15:20:06:33756][http-nio-8080-exec-7] WARN org.apache.catalina.util.SessionIdGeneratorBase.log - Creation of SecureRandom instance for session ID generation using [SHA1PRNG] took [1,246] milliseconds.
[2021-11-11 15:20:06:33760][http-nio-8080-exec-7] DEBUG org.springframework.security.web.context.HttpSessionSecurityContextRepository.saveContext - Stored SecurityContextImpl [Authentication=UsernamePasswordAuthenticationToken [Principal=LoginUser(loginInfo=com.bar.entity.LoginInfoEntity@12234790), Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=null], Granted Authorities=[]]] to HttpSession [org.apache.catalina.session.StandardSessionFacade@16178716]
[2021-11-11 15:20:06:33760][http-nio-8080-exec-7] DEBUG org.springframework.security.web.context.HttpSessionSecurityContextRepository.saveContext - Stored SecurityContextImpl [Authentication=UsernamePasswordAuthenticationToken [Principal=LoginUser(loginInfo=com.bar.entity.LoginInfoEntity@12234790), Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=null], Granted Authorities=[]]] to HttpSession [org.apache.catalina.session.StandardSessionFacade@16178716]
[2021-11-11 15:20:06:33760][http-nio-8080-exec-7] DEBUG org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter - Cleared SecurityContextHolder to complete request
--move to foo
[2021-11-11 15:20:06:33769][http-nio-8080-exec-8] DEBUG org.springframework.security.web.FilterChainProxy.doFilterInternal - Securing GET /foo
[2021-11-11 15:20:06:33769][http-nio-8080-exec-8] DEBUG org.springframework.security.web.context.HttpSessionSecurityContextRepository.readSecurityContextFromSession - Retrieved SecurityContextImpl [Authentication=UsernamePasswordAuthenticationToken [Principal=LoginUser(loginInfo=com.bar.entity.LoginInfoEntity@12234790), Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=null], Granted Authorities=[]]]
[2021-11-11 15:20:06:33769][http-nio-8080-exec-8] DEBUG org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter - Set SecurityContextHolder to SecurityContextImpl [Authentication=UsernamePasswordAuthenticationToken [Principal=LoginUser(loginInfo=com.bar.entity.LoginInfoEntity@12234790), Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=null], Granted Authorities=[]]]
[2021-11-11 15:20:06:33769][http-nio-8080-exec-8] DEBUG org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping.getHandler - Mapped to com.bar.view.controller.FooController#initalize()
--success
[2021-11-11 15:20:06:33770][http-nio-8080-exec-8] DEBUG org.springframework.security.web.access.intercept.FilterSecurityInterceptor.beforeInvocation - Authorized filter invocation [GET /foo] with attributes [authenticated]
登录后移动到 /foo 时(服务器日志)。抱歉,日志中没有方法名称
[2021-11-11 06:17:15:126723][foo] DEBUG org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter - Set SecurityContextHolder to UsernamePasswordAuthenticationToken [Principal=LoginUser(loginInfo=com.bar.entity.LoginInfoEntity@708afefc), Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=39.110.214.222, SessionId=null], Granted Authorities=[]]
[2021-11-11 06:17:15:126740][foo] DEBUG org.springframework.security.web.context.HttpSessionSecurityContextRepository - Stored SecurityContextImpl [Authentication=UsernamePasswordAuthenticationToken [Principal=LoginUser(loginInfo=com.bar.entity.LoginInfoEntity@708afefc), Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=39.110.214.222, SessionId=null], Granted Authorities=[]]] to HttpSession [org.apache.catalina.session.StandardSessionFacade@4d6cfed4]
[2021-11-11 06:17:15:126740][foo] DEBUG org.springframework.security.web.context.HttpSessionSecurityContextRepository - Stored SecurityContextImpl [Authentication=UsernamePasswordAuthenticationToken [Principal=LoginUser(loginInfo=com.bar.entity.LoginInfoEntity@708afefc), Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=39.110.214.222, SessionId=null], Granted Authorities=[]]] to HttpSession [org.apache.catalina.session.StandardSessionFacade@4d6cfed4]
[2021-11-11 06:17:15:126741][foo] DEBUG org.springframework.security.web.context.SecurityContextPersistenceFilter - Cleared SecurityContextHolder to complete request
-- move to foo
[2021-11-11 06:17:15:126766][foo] DEBUG org.springframework.security.web.FilterChainProxy - Securing GET /foo
[2021-11-11 06:17:15:126766][foo] DEBUG org.springframework.security.web.context.SecurityContextPersistenceFilter - Set SecurityContextHolder to empty SecurityContext
[2021-11-11 06:17:15:126766][foo] DEBUG org.springframework.security.web.authentication.AnonymousAuthenticationFilter - Set SecurityContextHolder to anonymous SecurityContext
[2021-11-11 06:17:15:126767][foo] DEBUG org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping - Mapped to com.bar.view.controller.RobotSelectController#initalize()
-- fail
[2021-11-11 06:17:15:126780][foo] DEBUG org.springframework.security.web.access.intercept.FilterSecurityInterceptor - Failed to authorize filter invocation [GET /foo] with attributes [authenticated]
我的源代码
我只有 Config 和 Spring Security
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter
@Override
public void configure(WebSecurity web) throws Exception
web
.ignoring()
.antMatchers("/images/**", "/js/**", "/css/**");
@Override
protected void configure(HttpSecurity http) throws Exception
http
.authorizeRequests()
.mvcMatchers(loginPage).permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage(loginPage)
.loginProcessingUrl(loginSuccessUrl)
.successHandler(new AuthenticationSuccessHandler()
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException
//add Session
request.getSession().setMaxInactiveInterval(180000);
//move page
response.sendRedirect(fooPage);
);
登录实现UserDetailsService
@Service
public class Login implements UserDetailsService
private final LoginInfoRepository loginRepo;
@Autowired
public Login(LoginInfoRepository loginRepo)
this.loginRepo = loginRepo;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException
LoginInfoEntity entity = loginRepo.findById(username)
.orElseThrow(() -> new UsernameNotFoundException("not exist user"));
UserDetails userDetails = new LoginUser(entity);
return userDetails;
loginUser 用于会话信息
public class LoginUser extends User
@SuppressWarnings("unchecked")
public LoginUser(LoginInfoEntity loginInfo)
super(loginInfo.getUserId(), loginInfo.getPassword(),
loginInfo.isAvailable(), true, true, true, Collections.EMPTY_SET);
感谢您阅读。
【问题讨论】:
【参考方案1】:[2021-11-11 06:17:15:126766][foo] 调试 org.springframework.security.web.authentication.AnonymousAuthenticationFilter - 将 SecurityContextHolder 设置为匿名 SecurityContext
这是理解问题的关键。 当您进行重定向时,您正在重定向用户,是的。 但是,他的会话信息 (cookie) 或其他一些东西不是这个重定向请求的一部分。
因此,从/foo
端点的角度来看,它没有获得任何凭据信息,并将请求视为匿名。
如果你能摆脱这段代码......
successHandler(.. do redirect ...)
不要像这样进行重定向,而是像this那样做,它可能会解决问题。
通过defaultSuccessUrl
重定向
http.formLogin().defaultSuccessUrl("/success.html", true);
这能解决您的问题吗?在 cmets 中告诉我。
您还可以启用 HTTP 请求跟踪,并准确查看带有哪些标头和有效负载的请求将到达 /foo
端点,这将更容易诊断问题。
您是否使用 cookie 或 JWT 进行身份验证?在 cmets 中告诉我。
【讨论】:
感谢您的回答。我没有使用 cookie 或 JWT 进行身份验证。代码就是全部。 defaultSuccessUrl 谢谢。我很担心因为我尝试在登录后通过直接输入xxxx/foo 移动页面,但我失败了。无论如何我会尝试你的答案。 @Carmel 告诉我它是否有效 我试过了。谢谢它运作良好。但我需要「request.getSession().setMaxInactiveInterval(180000)」,我应该更新 180000 或任何间隔。因为我应该使用 successHandler 而不是 defaultSuccessUrl。 你可以使用这个http对象以另一种方式设置,不需要依赖servlet API【参考方案2】:我解决了这个问题。谢谢亚瑟·克莱佐维奇。 原因是 contextpath 没有重定向。我认为本地环境等于服务器环境。但服务器环境有 contextpath 因为我没有为 contextpath 设置。 它会导致 url 涉及 contextpath。
例如)
成功在本地登录后获取会话 - http://99.99.99/foo
登录服务器后获取会话失败 - http://99.99.99/foo(question's url)
成功登录服务器后获取会话 -http://99.99.99/contextpath/foo
有几种方法可以删除 contextpath 的 url。我选择了最简单的方法更改战争名称文件
foo.war -> root.war
Spring-security 运行良好后,无需修改代码
【讨论】:
以上是关于为啥 Spring Security getAuthentication() 在服务器上返回 null的主要内容,如果未能解决你的问题,请参考以下文章
Spring Security:为啥 Authentication 正在扩展 Principal?
为啥 Spring Security 使用默认的预认证检查?
为啥 Spring Security 身份验证会导致 CORS 错误
为啥 Spring Security 的 BindAuthenticator 需要用户读取权限?