我正在使用spring security来实现程序化的手动用户登录。我有一个场景,我积极建立了用户的身份,并希望登录他们。我不知道他们的密码,因此不能使用常规登录代码路径将表单提交到url,哪个弹簧通过servlet Filter
拦截,完成它的所有auth +会话魔术。春季安全手册登录最佳实践
我已经搜查,似乎大多数人创建自己的Authentication
对象,然后告诉Spring通过有关:
PreAuthenticatedAuthenticationToken authentication = new PreAuthenticatedAuthenticationToken(user, "", user.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authentication);
事实上,这个工程。 Spring甚至会将它放入会话中,以使后续http请求保持其身份验证状态。
但是,我觉得这是一个肮脏的黑客。我将介绍一些细节,我希望会给使用setAuthentication()
控制器内实现手动登录相关的问题的具体例子:
举一个想法,我的配置是:
httpSecurity
.authorizeRequests()
.antMatchers("/test/**").permitAll()
.antMatchers("/admin/**", "/api/admin/**").hasRole("USER_SUPER_ADMIN")
.and()
.formLogin()
.loginPage("/sign-in?sp")
.loginProcessingUrl("/api/auth/sign-in")
.successHandler(createLoginSuccessHandler())
.failureHandler(createLoginFailureHandler())
.permitAll()
.and()
.logout()
.logoutUrl("/api/auth/sign-out")
.logoutSuccessHandler(createLogoutSuccessHandler())
.and()
.sessionManagement()
.maximumSessions(1)
.maxSessionsPreventsLogin(true)
.sessionRegistry(sessionRegistry)
;
关键在上述各点:
- 我使用自定义成功和失败的处理程序表单登录
- 我想配置每用户
最大并发会话的行为 - 我想保持弹簧的默认会话固定保护(根据不断变化的会话ID登录)。
- 我想使用会话注册表
- ...更多,我选择配置它。
我翻遍了代码,看看spring如何处理表单登录。正如预期的那样,Spring使用我的HttpSecurity
配置告诉它在我使用表单登录时执行的所有操作。但是,当我通过SecurityContextHolder.getContext().setAuthentication()
进行我自己的自定义/手动登录时,它没有任何作用。这是因为Spring在servlet Filter
中完成了auth + session的所有内容,而我的编程代码无法真正调用Filter。现在,我可以尝试自己添加缺少的行为,并复制其代码:我看到过滤器使用:ConcurrentSessionControlAuthenticationStrategy
,ChangeSessionIdAuthenticationStrategy
和RegisterSessionAuthenticationStrategy
。我可以自己创建这些对象,配置它们,并在我的自定义登录后调用它们。但是,以这种方式复制它有点蹩脚。此外,还有其他一些我缺少的行为 - 我注意到当使用表单登录代码路径时,该弹簧触发了一些登录事件,这些登录事件在我执行自定义登录时不会触发。而且可能还有其他我缺少或不理解的东西。整个过程非常复杂。
所以,我觉得我从错误的方式接近这一点。 我是否应该使用不同的策略,以避免跳过像Spring那样的东西?也许我应该尝试让自己的AuthenticationProvider
完成此自定义登录?
*为了澄清,我的代码或多或少的作品。但是,我觉得我用一个糟糕的策略来实现它,因为我必须编写代码来复制春天为我做的很多东西。此外,我的代码并没有完美地复制春天做的事情,这让我不知道会产生什么负面影响。必须有更好的方式来以编程方式实现登录。
这似乎过于宽泛。您是否可以减少内容以提供有关该问题的相关信息并发布[MCVE](https://stackoverflow.com/help/mcve)以进行复制。 –
@dur我的自定义(手动)登录在其初始帐户创建后立即使用。我给他们发电子邮件并让他们点击一个链接来验证他们的帐户。点击链接后,我登录他们,而不再让他们再次输入凭据。由于在这种情况下我没有密码,因此我不能使用任何“正常”的春季认证机制,例如“ActiveDirectoryLdapAuthenticationProvider”。如果他们忘记密码,我也会做同样的事情,允许他们有限的帐户访问权限,而不必更改/重置密码,这对我的使用情况非常重要。 – goat
@dur,更进一步,*即使我确实有密码*,但我真的没有办法让春天为我做所有这些事情,因为春天它在servlet'Filter'中很神奇,很好在执行我的应用程序代码之前(例如在控制器中)。我需要通过编程方式进行建立登录状态的调用,以及我列出的所有关联会话内容。 – goat