SpringSecurity WithSecurityContext MockMvc OAuth2 总是未经授权
Posted
技术标签:
【中文标题】SpringSecurity WithSecurityContext MockMvc OAuth2 总是未经授权【英文标题】:SpringSecurity WithSecurityContext MockMvc OAuth2 always unauthorised 【发布时间】:2016-10-01 02:22:29 【问题描述】:例如,我已按照以下链接尝试测试 OAuth2 @PreAuthorise(hasAnyRole('ADMIN', 'TEST'),但我无法通过任何测试,甚至无法通过身份验证。
当我尝试使用管理员(或任何角色)访问端点时,它永远无法正确验证。我是否遗漏了一些明显的东西,似乎我拥有示例中的所有内容。我还尝试了使用 OAuth 特定身份验证的 WithSecurityContext Factory 的另一种替代方案,但仍然没有运气。任何帮助将不胜感激。
https://***.com/a/31679649/2594130 和 http://docs.spring.io/spring-security/site/docs/4.0.x/reference/htmlsingle/#test
我正在测试的控制器
@RestController
@RequestMapping("/bookmark/")
public class GroupBookmarkController
@Autowired
BookmarkService bookmarkService;
/**
* Get list of all bookmarks
*/
@RequestMapping(value = "groupId", method = RequestMethod.GET)
@PreAuthorize("hasAnyRole(['ADMIN', 'USER'])")
public ResponseEntity<List<Bookmark>> listAllGroupBookmarks(@PathVariable("groupId") String groupId) throws BookmarkNotFoundException
List<Bookmark> bookmarks = bookmarkService.findAllBookmarksByGroupId(groupId);
return new ResponseEntity<>(bookmarks, HttpStatus.OK);
...
我的测试课
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = BookmarkServiceApplication.class)
@WebAppConfiguration
public class BookmarkServiceApplicationTests
private MockMvc mockMvc;
@Autowired
private WebApplicationContext webApplicationContext;
@Before
public void loadData()
this.mockMvc = MockMvcBuilders
.webAppContextSetup(webApplicationContext)
.apply(springSecurity())
.alwaysDo(print())
.build();
@Test
@WithMockCustomUser(username = "test")
public void getBookmarkAuthorised() throws Exception
mockMvc.perform(get("/bookmark/nvjdbngkjlsdfngkjlfdsnlkgsd"))
.andExpect(status().is(HttpStatus.SC_OK));
// always 401 here
我的书签服务应用程序
@SpringBootApplication
@EnableResourceServer
public class BookmarkServiceApplication
public static void main(String[] args)
SpringApplication.run(BookmarkServiceApplication.class, args);
我的 WithSecurityContextFactory
public class WithMockCustomUserSecurityContextFactory implements WithSecurityContextFactory<WithMockCustomUser>
@Override
public SecurityContext createSecurityContext(WithMockCustomUser customUser)
SecurityContext context = SecurityContextHolder.createEmptyContext();
List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
grantedAuthorities.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
UserDetails principal = new User(customUser.username(), "password", true, true, true, true, grantedAuthorities);
Authentication authentication = new UsernamePasswordAuthenticationToken(
principal, principal.getPassword(), principal.getAuthorities());
context.setAuthentication(authentication);
return context;
我的 WithSecurityContext 注释
@Retention(RetentionPolicy.RUNTIME)
@WithSecurityContext(factory = WithMockCustomUserSecurityContextFactory.class)
public @interface WithMockCustomUser
String username() default "user";
String name() default "Test User";
根据@RobWinch 的回复
嗨@RobWinch 我已经用无状态标志尝试过你的建议,这有助于部分答案。但是,在您回答这个问题 [Spring OAuth and Boot Integration Test] (https://***.com/a/31679649/2594130) 时,您提到了
您不再需要担心是否在无状态模式下运行
为什么我仍然需要添加无状态的 false,这是一个错误还是我们使用它略有不同?
我需要做的另一件事是将 OAuth2Request 和 OAuth2Authentication 添加到 WithSecurityContextFactory,如下所示
public class WithMockCustomUserSecurityContextFactory implements WithSecurityContextFactory<WithMockOAuthUser>
@Override
public SecurityContext createSecurityContext(WithMockOAuthUser withClient)
// Get the username
String username = withClient.username();
if (username == null)
throw new IllegalArgumentException("Username cannot be null");
// Get the user roles
List<GrantedAuthority> authorities = new ArrayList<>();
for (String role : withClient.roles())
if (role.startsWith("ROLE_"))
throw new IllegalArgumentException("roles cannot start with ROLE_ Got " + role);
authorities.add(new SimpleGrantedAuthority("ROLE_" + role));
// Get the client id
String clientId = withClient.clientId();
// get the oauth scopes
String[] scopes = withClient.scope();
Set<String> scopeCollection = Sets.newSet(scopes);
// Create the UsernamePasswordAuthenticationToken
User principal = new User(username, withClient.password(), true, true, true, true, authorities);
Authentication authentication = new UsernamePasswordAuthenticationToken(principal, principal.getPassword(),
principal.getAuthorities());
// Create the authorization request and OAuth2Authentication object
OAuth2Request authRequest = new OAuth2Request(null, clientId, null, true, scopeCollection, null, null, null,
null);
OAuth2Authentication oAuth = new OAuth2Authentication(authRequest, authentication);
// Add the OAuth2Authentication object to the security context
SecurityContext context = SecurityContextHolder.createEmptyContext();
context.setAuthentication(oAuth);
return context;
【问题讨论】:
WithMockOAuthUser 的代码在哪里,它与 WithSecurityContextFactory 相关? @RobWinch 对不起,这是一个错字,实际上是 WithMockCustomUser 是一个错字,我在乱搞,同样的问题存在。我有另一个界面创建了 OAuth 身份验证而不是 UsernamePasswordAuthentication 您的安全配置是什么样的?具体来说,网络和方法安全配置是什么样的? @RobWinch 我实际上并没有默认运行的配置,这是一个微服务和通信,位于承载配置的身份验证服务的网关后面。这些测试是在 AuthService 的隔离中运行的,我应该还有配置吗? @revilo 如果我可能会问,您是否可以与您的测试环境共享存储库?我正在尝试测试 spring-security-oauth2,除了 401 之外我什么也得不到。你的帖子是最能帮助我理解的帖子。尚不存在使用 spring-security 和 spring-security-oauth2 的项目测试。这将是超级好。提前致谢 【参考方案1】:问题是OAuth2AuthenticationProcessingFilter如果被标记为无状态会清除SecurityContext。要解决此问题,请将其配置为允许在外部填充状态(即 stateless = false)。
【讨论】:
嗨@RobWinch 我已经用无状态标志尝试过你的建议,这有助于部分答案。但是,在您对这个问题 [Spring OAuth 和引导集成测试] (***.com/a/31679649/2594130) 的回复中,您提到 “您不再需要担心是否在无状态模式下运行” 为什么会这样需要我需要这个,这是一个错误还是我们使用它略有不同。我已经更新了答案,因为这里的字符用完了 Spring Security OAuth 处理无状态的方式与 Spring Security 不同,因此该语句不适用于 OAuth(很遗憾)。 在上面的例子中,究竟如何设置 that = false? @RobWinch 亲爱的 rob,我们已经尝试了几天来测试 spring-security-oauth2,我们也遇到了 401 错误。 Spring-security-oauth2 没有任何示例,我们需要一个。有没有办法提供示例或额外信息?【参考方案2】:添加更多关于如何将 stateless 设置为 false 的信息:
在您的 ResourceServerConfigurerAdapter 中执行以下操作:
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception
resources.stateless(false);
这对我有用。
【讨论】:
以上是关于SpringSecurity WithSecurityContext MockMvc OAuth2 总是未经授权的主要内容,如果未能解决你的问题,请参考以下文章