使用Spring Security进行单元测试 [英] Unit testing with Spring Security

查看:445
本文介绍了使用Spring Security进行单元测试的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的公司一直在评估Spring MVC,以确定是否应该在我们的下一个项目中使用它。到目前为止,我喜欢我所看到的内容,现在我正在查看Spring Security模块,以确定它是否可以/应该使用。



我们的安全要求非常基本;用户只需提供用户名和密码即可访问网站的某些部分(例如获取有关其帐户的信息);并且网站上有一些页面(常见问题解答,支持等),应该允许匿名用户访问。



在我创建的原型中,我一直在Session中为经过身份验证的用户存储LoginCredentials对象(只包含用户名和密码);例如,某些控制器检查此对象是否在会话中以获取对登录用户名的引用。我正在寻找用Spring Security取代这个本土逻辑,这将有很好的好处,可以删除任何类型的我们如何跟踪登录用户?和我们如何验证用户?来自我的控制器/业务代码。



似乎Spring Security提供了一个(每个线程)上下文对象,可以从应用程序的任何位置访问用户名/主体信息...

  Object principal = SecurityContextHolder.getContext()。getAuthentication()。getPrincipal(); 

...这似乎非常不像Spring这样的对象是一个(全局)单例,一种方式。



我的问题是:如果这是在Spring Security中访问有关经过身份验证的用户的信息的标准方法,那么注入Authentication对象的可接受方式是什么进入SecurityContext,以便当单元测试需要经过身份验证的用户时,它可用于我的单元测试吗?



我是否需要在每个测试的初始化方法中进行连接case?

  protected void setUp()throws Exception {
...
SecurityContextHolder.getContext() .setAuthentication(
new UsernamePasswordAuthenticationToken(testUser.getLogin(),testUser.getPassword()));
...
}

这看起来过于冗长。有没有更简单的方法?



SecurityContextHolder 对象本身似乎非常类似Spring ...

解决方案

问题是Spring Security没有将Authentication对象作为容器中的bean提供,因此无法轻易地将其注入或自动装入在我们开始使用Spring Security之前,我们将在容器中创建一个会话范围的bean来存储Principal,将其注入AuthenticationService(然后将这个bean注入需要了解当前Principal的其他服务。



如果你正在实现自己的身份验证服务,你基本上可以做同样的事情:创建一个具有principal属性的会话范围的bean,将其注入您的身份验证服务,让auth服务在成功的身份验证中设置该属性,然后根据需要将auth服务提供给其他bean。



我不会牛逼感觉太糟糕了有关使用SecurityContextHolder中。虽然。我知道它是一个静态/ Singleton,并且Spring不鼓励使用这些东西,但是它们的实现需要根据环境进行适当的操作:在Servlet容器中进行会话作用,在JUnit测试中进行线程作用等。真正的限制因素当Singleton提供一种对不同环境不灵活的实现时。


My company has been evaluating Spring MVC to determine if we should use it in one of our next projects. So far I love what I've seen, and right now I'm taking a look at the Spring Security module to determine if it's something we can/should use.

Our security requirements are pretty basic; a user just needs to be able to provide a username and password to be able to access certain parts of the site (such as to get info about their account); and there are a handful of pages on the site (FAQs, Support, etc) where an anonymous user should be given access.

In the prototype I've been creating, I have been storing a "LoginCredentials" object (which just contains username and password) in Session for an authenticated user; some of the controllers check to see if this object is in session to get a reference to the logged-in username, for example. I'm looking to replace this home-grown logic with Spring Security instead, which would have the nice benefit of removing any sort of "how do we track logged in users?" and "how do we authenticate users?" from my controller/business code.

It seems like Spring Security provides a (per-thread) "context" object to be able to access the username/principal info from anywhere in your app...

Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();

... which seems very un-Spring like as this object is a (global) singleton, in a way.

My question is this: if this is the standard way to access information about the authenticated user in Spring Security, what is the accepted way to inject an Authentication object into the SecurityContext so that it is available for my unit tests when the unit tests require an authenticated user?

Do I need to wire this up in the initialization method of each test case?

protected void setUp() throws Exception {
    ...
    SecurityContextHolder.getContext().setAuthentication(
        new UsernamePasswordAuthenticationToken(testUser.getLogin(), testUser.getPassword()));
    ...
}

This seems overly verbose. Is there an easier way?

The SecurityContextHolder object itself seems very un-Spring-like...

解决方案

The problem is that Spring Security does not make the Authentication object available as a bean in the container, so there is no way to easily inject or autowire it out of the box.

Before we started to use Spring Security, we would create a session-scoped bean in the container to store the Principal, inject this into an "AuthenticationService" (singleton) and then inject this bean into other services that needed knowledge of the current Principal.

If you are implementing your own authentication service, you could basically do the same thing: create a session-scoped bean with a "principal" property, inject this into your authentication service, have the auth service set the property on successful auth, and then make the auth service available to other beans as you need it.

I wouldn't feel too bad about using SecurityContextHolder. though. I know that it's a static / Singleton and that Spring discourages using such things but their implementation takes care to behave appropriately depending on the environment: session-scoped in a Servlet container, thread-scoped in a JUnit test, etc. The real limiting factor of a Singleton is when it provides an implementation that is inflexible to different environments.

这篇关于使用Spring Security进行单元测试的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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