Jay*_*Jay 11 java spring spring-security spring-boot spring-session
我最近升级到 Spring Security 6,发现使用 JS 或curl 的基本身份验证进行身份验证不再有效,但使用 Java 的 HttpClient 进行基本身份验证确实有效。我的目标是能够使用所有方法进行身份验证。
\n该应用程序使用 Java 17、Spring Security 6 和 Spring Session 3。它有一个“登录”端点,这只是一个方便的端点,预计将通过基本身份验证进行命中并创建会话,并且它返回一个 User 对象。会话 ID 应用于对其他端点的后续请求。
\n卷曲命令如下所示:
\n curl -kv --user admin:admin "https://localhost:9000/login"\nRun Code Online (Sandbox Code Playgroud)\nVS HttpClient 的配置如下并调用 HttpClient.get(loginUrl)
\nHttpClient.newBuilder()\n\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0.connectTimeout(Duration.ofSeconds(300))\n\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0.cookieHandler(new CookieManager())\n\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0.authenticator(new BasicAuthenticator(username, password))\n\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0.sslContext(createSsl())\n\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0.build();\n\npublic class BasicAuthenticator extends Authenticator {\n\n\xc2\xa0\xc2\xa0\xc2\xa0private PasswordAuthentication authentication;\n\n\xc2\xa0\xc2\xa0\xc2\xa0public BasicAuthenticator(String username, String password) {\n\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0authentication = new PasswordAuthentication(username, password.toCharArray());\n\xc2\xa0\xc2\xa0\xc2\xa0}\n\n\xc2\xa0\xc2\xa0\xc2\xa0@Override\n\xc2\xa0\xc2\xa0\xc2\xa0public PasswordAuthentication getPasswordAuthentication() {\n\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0return authentication;\n\xc2\xa0\xc2\xa0\xc2\xa0}\n}\nRun Code Online (Sandbox Code Playgroud)\n安全配置是下面的块...在升级到 SpringSecurity 6 时,我添加了 requireExplicitSave() 方法,我对此表示怀疑,因为我的麻烦在于保存会话,但添加的代码应该具有使用旧功能的 spring security 。
\nhttp\n\xc2\xa0\xc2\xa0\xc2\xa0.securityContext( securityContext -> securityContext.requireExplicitSave(false))\n\xc2\xa0\xc2\xa0\xc2\xa0.authorizeHttpRequests((authz) -> authz\n\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0.requestMatchers(openEndpoints).permitAll()\n\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0.anyRequest().authenticated()\n\xc2\xa0\xc2\xa0\xc2\xa0)\n\xc2\xa0\xc2\xa0\xc2\xa0.httpBasic()\n\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0.and()\n\xc2\xa0\xc2\xa0\xc2\xa0.csrf()\n\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0.disable()\n\xc2\xa0\xc2\xa0\xc2\xa0.exceptionHandling()\n\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0.accessDeniedHandler((req, resp, e) -> e.printStackTrace() )\n\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0.and()\n\xc2\xa0\xc2\xa0\xc2\xa0.logout()\n\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0.invalidateHttpSession(true)\n\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0.clearAuthentication(true);\nRun Code Online (Sandbox Code Playgroud)\n我打开了请求日志记录、安全日志记录和 SQL 日志记录。SQL 都是相同的,并且基本身份验证请求始终针对所有场景进行身份验证。标头不同,但我看不到 HttpClient 预检调用的标头,并且在我看到的标头中,我不知道为什么身份验证或会话创建适用于一组标头,但不适用于另一组标头。
\n问题的核心似乎是来自 HttpClient 的登录请求以创建会话结束,而来自 curl 的请求则不然。请注意,使用curl 时服务器日志中的最大区别是“无法创建会话,因为响应已提交。无法存储SecurityContext。” 然而,即使单步执行 Spring 安全代码,我也无法判断是什么导致了差异。\xc2\xa0
\n请参阅此处的日志:
\n卷曲
\n2022-12-14T16:38:07.594-05:00 DEBUG 92726 --- [nio-9000-exec-1] o.s.security.web.FilterChainProxy : Securing GET /login\n2022-12-14T16:38:07.597-05:00 DEBUG 92726 --- [nio-9000-exec-1] s.s.w.c.SecurityContextPersistenceFilter : Set SecurityContextHolder to empty SecurityContext\n2022-12-14T16:38:07.704-05:00 DEBUG 92726 --- [nio-9000-exec-1] org.hibernate.SQL : select u1_0.id,u1_0.display_name,u1_0.email,u1_0.enabled,u1_0.password,u1_0.registration_time,r1_0.user_id,r1_0.role_id,u1_0.username from app_user u1_0 join user_role r1_0 on u1_0.id=r1_0.user_id where u1_0.username=?\n2022-12-14T16:38:07.797-05:00 DEBUG 92726 --- [nio-9000-exec-1] o.s.s.a.dao.DaoAuthenticationProvider : Authenticated user\n2022-12-14T16:38:07.799-05:00 DEBUG 92726 --- [nio-9000-exec-1] o.s.s.w.a.www.BasicAuthenticationFilter : Set SecurityContextHolder to UsernamePasswordAuthenticationToken [Principal=org.springframework.security.core.userdetails.User [Username=admin, Password=[PROTECTED], Enabled=true, AccountNonExpired=true, credentialsNonExpired=true, AccountNonLocked=true, Granted Authorities=[ROLE_ADMIN, ROLE_USER]], Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=127.0.0.1, SessionId=null], Granted Authorities=[ROLE_ADMIN, ROLE_USER]]\n2022-12-14T16:38:07.801-05:00 DEBUG 92726 --- [nio-9000-exec-1] o.s.security.web.FilterChainProxy : Secured GET /login\n2022-12-14T16:38:07.805-05:00 DEBUG 92726 --- [nio-9000-exec-1] o.s.w.f.CommonsRequestLoggingFilter : Before request [GET /login, headers=[host:"localhost:9000", authorization:"Basic YWRtaW46YWRtaW4=", user-agent:"curl/7.84.0", accept:"*/*"]]\n2022-12-14T16:38:07.816-05:00 DEBUG 92726 --- [nio-9000-exec-1] horizationManagerBeforeMethodInterceptor : Authorizing method invocation ReflectiveMethodInvocation: public com.seebie.dto.User com.seebie.server.controller.UserController.login(java.security.Principal); target is of class [com.seebie.server.controller.UserController]\n2022-12-14T16:38:07.822-05:00 DEBUG 92726 --- [nio-9000-exec-1] horizationManagerBeforeMethodInterceptor : Authorized method invocation ReflectiveMethodInvocation: public com.seebie.dto.User com.seebie.server.controller.UserController.login(java.security.Principal); target is of class [com.seebie.server.controller.UserController]\n2022-12-14T16:38:07.826-05:00 DEBUG 92726 --- [nio-9000-exec-1] org.hibernate.SQL : select u1_0.id,u1_0.display_name,u1_0.email,u1_0.enabled,u1_0.password,u1_0.registration_time,u1_0.username from app_user u1_0 where u1_0.username=?\n2022-12-14T16:38:07.832-05:00 DEBUG 92726 --- [nio-9000-exec-1] org.hibernate.SQL : select r1_0.user_id,r1_0.role_id from user_role r1_0 where r1_0.user_id=?\n2022-12-14T16:38:07.836-05:00 DEBUG 92726 --- [nio-9000-exec-1] org.hibernate.SQL : select a1_0.user_id,a1_0.id,a1_0.city,a1_0.line1,a1_0.state,a1_0.zip from address a1_0 where a1_0.user_id=?\n2022-12-14T16:38:07.840-05:00 DEBUG 92726 --- [nio-9000-exec-1] org.hibernate.SQL : select s1_0.principal_name,s1_0.primary_id,s1_0.session_id from spring_session s1_0 where s1_0.principal_name=?\n2022-12-14T16:38:07.871-05:00 DEBUG 92726 --- [nio-9000-exec-1] o.s.w.f.CommonsRequestLoggingFilter : REQUEST DATA : GET /login, headers=[host:"localhost:9000", authorization:"Basic YWRtaW46YWRtaW4=", user-agent:"curl/7.84.0", accept:"*/*"]]\n2022-12-14T16:38:07.873-05:00 WARN 92726 --- [nio-9000-exec-1] w.c.HttpSessionSecurityContextRepository : Failed to create a session, as response has been committed. Unable to store SecurityContext.\n2022-12-14T16:38:07.873-05:00 DEBUG 92726 --- [nio-9000-exec-1] s.s.w.c.SecurityContextPersistenceFilter : Cleared SecurityContextHolder to complete request\nRun Code Online (Sandbox Code Playgroud)\nHttp客户端
\n2022-12-14T06:31:28.390-05:00 DEBUG 85610 --- [o-auto-1-exec-1] o.s.security.web.FilterChainProxy : Securing GET /login\n2022-12-14T06:31:28.420-05:00 DEBUG 85610 --- [o-auto-1-exec-1] s.s.w.c.SecurityContextPersistenceFilter : Set SecurityContextHolder to empty SecurityContext\n2022-12-14T06:31:28.913-05:00 DEBUG 85610 --- [o-auto-1-exec-1] org.hibernate.SQL : select u1_0.id,u1_0.display_name,u1_0.email,u1_0.enabled,u1_0.password,u1_0.registration_time,r1_0.user_id,r1_0.role_id,u1_0.username from app_user u1_0 join user_role r1_0 on u1_0.id=r1_0.user_id where u1_0.username=?\n2022-12-14T06:31:29.102-05:00 DEBUG 85610 --- [o-auto-1-exec-1] o.s.s.a.dao.DaoAuthenticationProvider : Authenticated user\n2022-12-14T06:31:29.103-05:00 DEBUG 85610 --- [o-auto-1-exec-1] o.s.s.w.a.www.BasicAuthenticationFilter : Set SecurityContextHolder to UsernamePasswordAuthenticationToken [Principal=org.springframework.security.core.userdetails.User [Username=admin, Password=[PROTECTED], Enabled=true, AccountNonExpired=true, credentialsNonExpired=true, AccountNonLocked=true, Granted Authorities=[ROLE_ADMIN, ROLE_USER]], Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=127.0.0.1, SessionId=19ab0971-5fb3-47fd-a4f9-cdde1ad24883], Granted Authorities=[ROLE_ADMIN, ROLE_USER]]\n2022-12-14T06:31:29.108-05:00 DEBUG 85610 --- [o-auto-1-exec-1] o.s.security.web.FilterChainProxy : Secured GET /login\n2022-12-14T06:31:29.136-05:00 DEBUG 85610 --- [o-auto-1-exec-1] o.s.w.f.CommonsRequestLoggingFilter : Before request [GET /login, headers=[authorization:"Basic YWRtaW46YWRtaW4=", content-length:"0", host:"localhost:64723", user-agent:"Java-http-client/17.0.2", cookie:"SESSION=MTlhYjA5NzEtNWZiMy00N2ZkLWE0ZjktY2RkZTFhZDI0ODgz", Content-Type:"application/json;charset=UTF-8"]]\n2022-12-14T06:31:29.274-05:00 DEBUG 85610 --- [o-auto-1-exec-1] horizationManagerBeforeMethodInterceptor : Authorizing method invocation ReflectiveMethodInvocation: public com.seebie.dto.User com.seebie.server.controller.UserController.login(java.security.Principal); target is of class [com.seebie.server.controller.UserController]\n2022-12-14T06:31:29.332-05:00 DEBUG 85610 --- [o-auto-1-exec-1] horizationManagerBeforeMethodInterceptor : Authorized method invocation ReflectiveMethodInvocation: public com.seebie.dto.User com.seebie.server.controller.UserController.login(java.security.Principal); target is of class [com.seebie.server.controller.UserController]\n2022-12-14T06:31:29.373-05:00 DEBUG 85610 --- [o-auto-1-exec-1] org.hibernate.SQL : select u1_0.id,u1_0.display_name,u1_0.email,u1_0.enabled,u1_0.password,u1_0.registration_time,u1_0.username from app_user u1_0 where u1_0.username=?\n2022-12-14T06:31:29.392-05:00 DEBUG 85610 --- [o-auto-1-exec-1] org.hibernate.SQL : select r1_0.user_id,r1_0.role_id from user_role r1_0 where r1_0.user_id=?\n2022-12-14T06:31:29.409-05:00 DEBUG 85610 --- [o-auto-1-exec-1] org.hibernate.SQL : select a1_0.user_id,a1_0.id,a1_0.city,a1_0.line1,a1_0.state,a1_0.zip from address a1_0 where a1_0.user_id=?\n2022-12-14T06:31:29.413-05:00 DEBUG 85610 --- [o-auto-1-exec-1] org.hibernate.SQL : select s1_0.principal_name,s1_0.primary_id,s1_0.session_id from spring_session s1_0 where s1_0.principal_name=?\n2022-12-14T06:31:29.678-05:00 DEBUG 85610 --- [o-auto-1-exec-1] o.s.w.f.CommonsRequestLoggingFilter : REQUEST DATA : GET /login, headers=[authorization:"Basic YWRtaW46YWRtaW4=", content-length:"0", host:"localhost:64723", user-agent:"Java-http-client/17.0.2", cookie:"SESSION=MTlhYjA5NzEtNWZiMy00N2ZkLWE0ZjktY2RkZTFhZDI0ODgz", Content-Type:"application/json;charset=UTF-8"]]\n2022-12-14T06:31:29.680-05:00 DEBUG 85610 --- [o-auto-1-exec-1] w.c.HttpSessionSecurityContextRepository : Stored SecurityContextImpl [Authentication=UsernamePasswordAuthenticationToken [Principal=org.springframework.security.core.userdetails.User [Username=admin, Password=[PROTECTED], Enabled=true, AccountNonExpired=true, credentialsNonExpired=true, AccountNonLocked=true, Granted Authorities=[ROLE_ADMIN, ROLE_USER]], Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=127.0.0.1, SessionId=19ab0971-5fb3-47fd-a4f9-cdde1ad24883], Granted Authorities=[ROLE_ADMIN, ROLE_USER]]] to HttpSession [org.springframework.session.web.http.SessionRepositoryFilter$SessionRepositoryRequestWrapper$HttpSessionWrapper@7ba784f2]\n2022-12-14T06:31:29.680-05:00 DEBUG 85610 --- [o-auto-1-exec-1] s.s.w.c.SecurityContextPersistenceFilter : Cleared SecurityContextHolder to complete request\nRun Code Online (Sandbox Code Playgroud)\n请注意 HttpClient 调用标头中的会话...我认为 HttpClient 会进行预检身份验证调用,该调用会获取 401,然后使用凭据进行“真正的”调用,至少在 Java 11 中是这样的。
\n我认为,如果我了解这些调用的发出/处理方式的差异,导致一种技术起作用,但另一种技术不起作用,我就能够解决这个问题。所以这确实是一个问题:在使用 Java 17 的 HttpClient 与curl 时,spring security 6(以及 spring session)处理会话创建有什么区别?
\n[更新]对于读到这里的人来说:该行为实际上是 Spring Security 的预期行为。完整的讨论和解释在我打开的春季安全问题中完整的讨论和解释在我在这里
\n好吧,如果我们不打算调查为什么预检请求有意义(IMO,这似乎是一个错误),那么 Spring 6 中更改的内容的解释如下:
正如会话管理迁移中提到的,现在 Spring默认情况下不启用SecurityContextPersistenceFilter ,但是在 Spring 5 中, SecurityContextPersistenceFilter负责保存SecurityContext(http session并因此创建它),除非明确禁用它。现在,为了返回您想要的以前的行为,您需要SecurityContextRepository通过以下方式进行设置:
http.securityContext(securityContext -> securityContext.
securityContextRepository(new HttpSessionSecurityContextRepository())
)
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
5524 次 |
| 最近记录: |