SpringSecurity WithSecurityContext MockMvc OAuth2始终未经授权

时间:2016-06-01 15:48:30

标签: spring-security spring-boot spring-security-oauth2 spring-test spring-oauth2

我已按照以下链接尝试测试OAuth2 @PreAuthorise(hasAnyRole(' ADMIN',' TEST'),但我无法进行任何测试通过甚至认证。

当我尝试使用admin(或任何角色)访问终点时,它将永远不会正确进行身份验证。我错过了一些明显的东西,似乎我拥有的东西就像在例子中一样。我还尝试了具有OAuth特定身份验证的WithSecurityContext Factory的另一种替代方案,但仍然没有运气。任何帮助将不胜感激。

https://stackoverflow.com/a/31679649/2594130http://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
    }
}

我的BookmarkServiceApplication

@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和引导集成测试](https://stackoverflow.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;
    }

}

2 个答案:

答案 0 :(得分:2)

问题是OAuth2AuthenticationProcessingFilter如果被标记为无状态,将清除SecurityContext。要解决此问题,请将其配置为允许在外部填充状态(即stateless = false)。

答案 1 :(得分:1)

添加更多信息如何将无状态设置为false:

您的ResourceServerConfigurerAdapter中的

执行以下操作:

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        resources.stateless(false);
    }

对我有用。