spring security 手动登录最佳实践 [英] spring security manual login best practice

查看:32
本文介绍了spring security 手动登录最佳实践的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用 spring security 来实现程序化的手动用户登录.我有一个场景,我已经确定了用户的身份,并希望让他们登录.我不知道他们的密码,因此无法使用常规登录代码路径,您将表单提交到 url,即通过 servlet Filter 拦截,执行所有的 auth+session 魔法.

I'm using spring security to implement a programmatic, manual user login. I have a scenario where I have positively established the user's identity, and wish to log them in. I don't know their password, and so can't use the regular login code path where you submit a form to a url, which spring intercepts via a servlet Filter, doing all of it's auth+session magic.

我搜索过,似乎大多数人创建了自己的 Authentication 对象,然后通过以下方式告诉 spring:

I've searched, and it seems most people create their own Authentication object, and then tell spring about via:

PreAuthenticatedAuthenticationToken authentication = new PreAuthenticatedAuthenticationToken(user, "", user.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authentication);

确实,这是有效的.Spring 甚至为我将其放入会话中,使后续 http 请求保持其身份验证状态.

Indeed, this works. Spring even puts it into the session for me, making subsequent http requests maintain their auth status.

然而,我觉得这是一个肮脏的黑客.我将提供一些细节,希望能给出与在控制器中使用 setAuthentication() 实现手动登录相关的问题的具体示例:

However, I feel like this is a dirty hack. I'll present some details that I hope will give concrete examples of the problems associated with using setAuthentication() inside a controller to achieve a manual login:

给出一个想法,我的配置是:

To give an idea, my config is:

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)
;

以上配置的要点:

  • 我为表单登录使用自定义成功和失败处理程序
  • 我想为每个用户的最大并发会话配置行为
  • 我想保持 spring 的默认会话固定保护(登录时更改会话 ID).
  • 我想使用会话注册表
  • ...更多这些会话/登录功能,如果我选择配置它.

我单步执行代码以了解 spring 如何处理表单登录.正如预期的那样,当我使用表单登录时,Spring 执行了我的 HttpSecurity 配置告诉它执行的所有会话/登录功能.但是,当我通过 SecurityContextHolder.getContext().setAuthentication() 进行自己的自定义/手动登录时,它不执行任何这些功能.这是因为 spring 在 servlet Filter 内完成了所有的会话/登录功能,而我的编程代码不能真正调用过滤器.现在,我可以尝试自己添加缺少的功能,复制他们的代码:我看到 spring 过滤器使用:ConcurrentSessionControlAuthenticationStrategyChangeSessionIdAuthenticationStrategyRegisterSessionAuthenticationStrategy.我可以自己创建这些对象,配置它们,并在我的自定义登录后调用它们.但是,复制所有弹簧代码真的很蹩脚.此外,我还缺少其他一些行为 - 我注意到在使用表单登录代码路径时,spring 会触发一些登录事件,这些事件在我进行自定义登录时不会被触发.可能还有其他我遗漏或不明白的东西.整个过程相当复杂,我觉得如果做得不好,很有可能引入错误,更何况如果我开始复制spring代码,库更新会很痛苦.

I stepped through the code to see how spring processes a form login. As expected, Spring does all the session/login functionalities that my HttpSecurity config told it to do when I use the form login. But, when I do my own custom/manual login via SecurityContextHolder.getContext().setAuthentication(), it does NONE of those functionalities. This is because spring does all of it's session/login functionalities stuff inside of a servlet Filter, and my programmatic code can't really call a Filter. Now, I can attempt to add the missing functionalities myself, duplicating their code: I see that the spring Filter uses: ConcurrentSessionControlAuthenticationStrategy, ChangeSessionIdAuthenticationStrategy, and RegisterSessionAuthenticationStrategy. I can create these objects myself, configure them, and call them after my custom login. But, that's really lame to duplicate all that spring code. Furthermore, there's still other behaviors I'm missing - I noticed that when using the form login code path, that spring triggers some login events which don't get triggered when I do my custom login. And there's probably other stuff that I'm missing or don't understand. The whole process is pretty complicated, and I feel like there's a high chance of introducing bugs if this isn't done right, not to mention that library updates would be a pain if I started duplicating spring code.

所以,我觉得我是从错误的方式来解决这个问题的.我是否应该使用不同的策略,这样我就不会绕过 spring 为我做的很多事情?也许我应该尝试使我自己的 AuthenticationProvider完成这个自定义登录?

So, I feel like I'm approaching this from the wrong way. Should I be using a different strategy, so that I'm not bypassing so much of the stuff that spring does for me? Maybe I should try to make my own AuthenticationProvider to accomplish this custom login?

*澄清一下,我的代码或多或少有效.但是,我觉得我使用了一个糟糕的策略来完成它,因为我不得不编写代码来复制 spring 为我所做的很多事情.此外,我的代码并没有完美地复制 spring 所做的事情,这让我想知道可能会产生什么负面影响.必须有更好的方式以编程方式实现登录.

*To clarify, my code more or less works. But, I feel like I accomplished it using a poor strategy because I had to write code duplicating a lot of stuff that spring does for me. Further, my code doesn't perfectly replicate what spring does, making me wonder what negative implications might result. There must be a better way to programatically achieve login.

推荐答案

对于自定义 Web 身份验证,您应该实现自定义身份验证过滤器的组合(例如 AbstractAuthenticationProcessingFilter 或只是 GenericFilterBean),自定义身份验证提供程序(AuthenticationProvider)或/和自定义身份验证令牌(AbstractAuthenticationToken).

For custom web authentication you should implement combination of a custom authentication filter (for example AbstractAuthenticationProcessingFilter or just GenericFilterBean), a custom authentication provider (AuthenticationProvider) or/and custom authentication token (AbstractAuthenticationToken).

例如,查看 Spring Security Kerberos 的来源.

For example, see source of Spring Security Kerberos.

另见:

这篇关于spring security 手动登录最佳实践的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆