Spring测试和安全性:如何模拟身份验证?

188
我正在尝试找出如何单元测试我的控制器URL是否得到了适当的安全保护。以防万一有人更改了设置并意外地删除了安全保护。
我的控制器方法如下:
@RequestMapping("/api/v1/resource/test") 
@Secured("ROLE_USER")
public @ResonseBody String test() {
    return "test";
}

我这样设置了一个WebTestEnvironment:

import javax.annotation.Resource;
import javax.naming.NamingException;
import javax.sql.DataSource;

import org.junit.Before;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.FilterChainProxy;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration({ 
        "file:src/main/webapp/WEB-INF/spring/security.xml",
        "file:src/main/webapp/WEB-INF/spring/applicationContext.xml",
        "file:src/main/webapp/WEB-INF/spring/servlet-context.xml" })
public class WebappTestEnvironment2 {

    @Resource
    private FilterChainProxy springSecurityFilterChain;

    @Autowired
    @Qualifier("databaseUserService")
    protected UserDetailsService userDetailsService;

    @Autowired
    private WebApplicationContext wac;

    @Autowired
    protected DataSource dataSource;

    protected MockMvc mockMvc;

    protected final Logger logger = LoggerFactory.getLogger(this.getClass());

    protected UsernamePasswordAuthenticationToken getPrincipal(String username) {

        UserDetails user = this.userDetailsService.loadUserByUsername(username);

        UsernamePasswordAuthenticationToken authentication = 
                new UsernamePasswordAuthenticationToken(
                        user, 
                        user.getPassword(), 
                        user.getAuthorities());

        return authentication;
    }

    @Before
    public void setupMockMvc() throws NamingException {

        // setup mock MVC
        this.mockMvc = MockMvcBuilders
                .webAppContextSetup(this.wac)
                .addFilters(this.springSecurityFilterChain)
                .build();
    }
}

在我的实际测试中,我尝试做这样的事情:
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import org.junit.Test;
import org.springframework.mock.web.MockHttpSession;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;

import eu.ubicon.webapp.test.WebappTestEnvironment;

public class CopyOfClaimTest extends WebappTestEnvironment {

    @Test
    public void signedIn() throws Exception {

        UsernamePasswordAuthenticationToken principal = 
                this.getPrincipal("test1");

        SecurityContextHolder.getContext().setAuthentication(principal);        

        super.mockMvc
            .perform(
                    get("/api/v1/resource/test")
//                    .principal(principal)
                    .session(session))
            .andExpect(status().isOk());
    }

}

我在这里找到了这个:

然而,仔细观察会发现这只有助于在功能级别上测试服务时不发送实际请求到URL,但在我的情况下,会抛出“访问被拒绝”异常。
org.springframework.security.access.AccessDeniedException: Access is denied
    at org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:83) ~[spring-security-core-3.1.3.RELEASE.jar:3.1.3.RELEASE]
    at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:206) ~[spring-security-core-3.1.3.RELEASE.jar:3.1.3.RELEASE]
    at org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor.invoke(MethodSecurityInterceptor.java:60) ~[spring-security-core-3.1.3.RELEASE.jar:3.1.3.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) ~[spring-aop-3.2.1.RELEASE.jar:3.2.1.RELEASE]
        ...

以下两个日志消息值得注意,基本上表明没有用户进行身份验证,这表明设置Principal未起作用,或者被覆盖了。
14:20:34.454 [main] DEBUG o.s.s.a.i.a.MethodSecurityInterceptor - Secure object: ReflectiveMethodInvocation: public java.util.List test.TestController.test(); target is of class [test.TestController]; Attributes: [ROLE_USER]
14:20:34.454 [main] DEBUG o.s.s.a.i.a.MethodSecurityInterceptor - Previously Authenticated: org.springframework.security.authentication.AnonymousAuthenticationToken@9055e4a6: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@957e: RemoteIpAddress: 127.0.0.1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS

1
您的公司名称eu.ubicon显示在您的导入中,这不是一个安全风险吗? - Kyle Bridenstine
5
你好,感谢您的评论!但我不明白为什么会这样。毕竟这是一个开源软件。如果你有兴趣的话,可以查看https://bitbucket.org/ubicon/ubicon(或者https://bitbucket.org/dmir_wue/everyaware获取最新的派生版本)。如果我漏掉了什么,请告诉我。 - Martin Becker
请查看此解决方案(答案适用于Spring 4):https://dev59.com/wmYq5IYBdhLWcg3wpyNE#47069613 - Nagy Attila
10个回答

187

在寻找易于使用且具有灵活性的答案时,我发现了Spring Security Reference,并意识到它提供了接近完美的解决方案。针对测试,AOP解决方案通常是最好的选择,Spring通过@WithMockUser@WithUserDetails@WithSecurityContext提供了这些功能,在此工件中:

<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-test</artifactId>
    <version>4.2.2.RELEASE</version>
    <scope>test</scope>
</dependency>

在大多数情况下,@WithUserDetails 提供了我所需的灵活性和功能。

@WithUserDetails 是如何工作的?

基本上,你只需要创建一个自定义的 UserDetailsService,其中包含你想要测试的所有可能的用户配置文件。例如:

@TestConfiguration
public class SpringSecurityWebAuxTestConfig {

    @Bean
    @Primary
    public UserDetailsService userDetailsService() {
        User basicUser = new UserImpl("Basic User", "user@company.com", "password");
        UserActive basicActiveUser = new UserActive(basicUser, Arrays.asList(
                new SimpleGrantedAuthority("ROLE_USER"),
                new SimpleGrantedAuthority("PERM_FOO_READ")
        ));

        User managerUser = new UserImpl("Manager User", "manager@company.com", "password");
        UserActive managerActiveUser = new UserActive(managerUser, Arrays.asList(
                new SimpleGrantedAuthority("ROLE_MANAGER"),
                new SimpleGrantedAuthority("PERM_FOO_READ"),
                new SimpleGrantedAuthority("PERM_FOO_WRITE"),
                new SimpleGrantedAuthority("PERM_FOO_MANAGE")
        ));

        return new InMemoryUserDetailsManager(Arrays.asList(
                basicActiveUser, managerActiveUser
        ));
    }
}

现在我们已经准备好了用户,因此想象一下我们想测试对这个控制器函数的访问控制:

@RestController
@RequestMapping("/foo")
public class FooController {

    @Secured("ROLE_MANAGER")
    @GetMapping("/salute")
    public String saluteYourManager(@AuthenticationPrincipal User activeUser)
    {
        return String.format("Hi %s. Foo salutes you!", activeUser.getUsername());
    }
}

这里我们有一个映射到路由/foo/salute获取映射函数,我们使用@Secured注释测试基于角色的安全性,虽然您也可以测试@PreAuthorize@PostAuthorize

让我们创建两个测试,一个用于检查有效用户是否可以查看此问候响应,另一个用于检查是否实际上被禁止。

@RunWith(SpringRunner.class)
@SpringBootTest(
        webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
        classes = SpringSecurityWebAuxTestConfig.class
)
@AutoConfigureMockMvc
public class WebApplicationSecurityTest {

    @Autowired
    private MockMvc mockMvc;

    @Test
    @WithUserDetails("manager@company.com")
    public void givenManagerUser_whenGetFooSalute_thenOk() throws Exception
    {
        mockMvc.perform(MockMvcRequestBuilders.get("/foo/salute")
                .accept(MediaType.ALL))
                .andExpect(status().isOk())
                .andExpect(content().string(containsString("manager@company.com")));
    }

    @Test
    @WithUserDetails("user@company.com")
    public void givenBasicUser_whenGetFooSalute_thenForbidden() throws Exception
    {
        mockMvc.perform(MockMvcRequestBuilders.get("/foo/salute")
                .accept(MediaType.ALL))
                .andExpect(status().isForbidden());
    }
}

如您所见,我们导入了SpringSecurityWebAuxTestConfig以为我们的用户提供测试。每个测试用例都可以使用简单的注解来使用它,从而减少代码和复杂性。

更好地使用@WithMockUser实现更简单的基于角色的安全性

正如您所见,@WithUserDetails拥有大多数应用程序所需的所有灵活性。它允许您使用任何GrantedAuthority(如角色或权限)的自定义用户。但是,如果您只使用角色进行工作,则测试可能会更加容易,并且您可以避免构建自定义UserDetailsService。在这种情况下,请使用一个简单的组合用户、密码和角色来指定@WithMockUser

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@WithSecurityContext(
    factory = WithMockUserSecurityContextFactory.class
)
public @interface WithMockUser {
    String value() default "user";

    String username() default "";

    String[] roles() default {"USER"};

    String password() default "password";
}

注解为一个基本用户定义默认值。在我们的情况下,我们测试的路由只需要已认证的用户是经理,因此我们可以停止使用SpringSecurityWebAuxTestConfig并且这样做。

@Test
@WithMockUser(roles = "MANAGER")
public void givenManagerUser_whenGetFooSalute_thenOk() throws Exception
{
    mockMvc.perform(MockMvcRequestBuilders.get("/foo/salute")
            .accept(MediaType.ALL))
            .andExpect(status().isOk())
            .andExpect(content().string(containsString("user")));
}

注意现在我们得到的不再是用户manager@company.com,而是@WithMockUser提供的默认值:user; 然而这并不重要,因为我们真正关心的是他的角色:ROLE_MANAGER。

结论

如您所见,使用@WithUserDetails@WithMockUser等注释,我们可以在不构建与我们的架构疏离的类的情况下切换不同的经过身份验证的用户场景,以进行简单的测试。 我们还建议您查看@WithSecurityContext的工作原理,以获得更大的灵活性。


如何模拟多个用户?例如,第一个请求由“tom”发送,而第二个请求由“jerry”发送? - ch271828n
你可以创建一个函数,在其中使用Tom进行测试,并创建另一个具有相同逻辑的测试,然后使用Jerry进行测试。每个测试都会有特定的结果,因此会有不同的断言,如果测试失败,它将通过其名称告诉您哪个用户/角色未能正常工作。请记住,在请求中,用户只能是一个,因此在请求中指定多个用户是没有意义的。 - EliuX
抱歉,我的意思是这样一个示例场景:我们测试一下,Tom创建了一篇秘密文章,然后Jerry试图阅读它,但Jerry不应该看到它(因为它是秘密的)。所以在这种情况下,这是一个单元测试... - ch271828n
它看起来非常像答案中提供的“基本用户”和“管理用户”场景。关键概念是,我们不关心用户,而是关心他们的角色,但是这些测试中的每一个位于同一单元测试中,实际上代表了不同的查询。这些查询由不同角色的用户(使用不同角色)对同一端点完成。 - EliuX

103

自Spring 4.0+版本以来,最佳方案是使用@WithMockUser注解测试方法。

@Test
@WithMockUser(username = "user1", password = "pwd", roles = "USER")
public void mytest1() throws Exception {
    mockMvc.perform(get("/someApi"))
        .andExpect(status().isOk());
}

请记得将以下依赖项添加到您的项目中

'org.springframework.security:spring-security-test:4.2.3.RELEASE'

6
春天太棒了。谢谢。 - TuGordoBello
好的回答。此外 - 您不需要使用mockMvc,但是如果您正在使用springframework.data中的PagingAndSortingRepository,您可以直接调用存储库中带有EL @PreAuthorize(......)注释的方法。 - supertramp
1
谢谢,使用@WithMockUser(roles = "YOUR_ROLE")会更好。 - Leonardo Pinto
1
如何添加 orgId - Kulbhushan Singh
只是一个小提醒,您不必在Spring依赖项中显式添加版本。 - Abdullah Khaled
完美的答案解决了我的问题。 - bademba

62
原来,Spring Security 过滤器链中的 SecurityContextPersistenceFilter 总是重置了我设置的 SecurityContext,而我是通过调用 SecurityContextHolder.getContext().setAuthentication(principal)(或使用 .principal(principal) 方法)来设置它的。这个过滤器使用来自 SecurityContextRepository 的 SecurityContext 覆盖了我之前设置的那个。默认情况下,该存储库是 HttpSessionSecurityContextRepository。HttpSessionSecurityContextRepository 检查给定的 HttpRequest 并尝试访问相应的 HttpSession。如果存在,则尝试从 HttpSession 中读取 SecurityContext。如果失败,则存储库生成一个空的 SecurityContext。
因此,我的解决方案是在请求中传递一个包含 SecurityContext 的 HttpSession。
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import org.junit.Test;
import org.springframework.mock.web.MockHttpSession;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;

import eu.ubicon.webapp.test.WebappTestEnvironment;

public class Test extends WebappTestEnvironment {

    public static class MockSecurityContext implements SecurityContext {

        private static final long serialVersionUID = -1386535243513362694L;

        private Authentication authentication;

        public MockSecurityContext(Authentication authentication) {
            this.authentication = authentication;
        }

        @Override
        public Authentication getAuthentication() {
            return this.authentication;
        }

        @Override
        public void setAuthentication(Authentication authentication) {
            this.authentication = authentication;
        }
    }

    @Test
    public void signedIn() throws Exception {

        UsernamePasswordAuthenticationToken principal = 
                this.getPrincipal("test1");

        MockHttpSession session = new MockHttpSession();
        session.setAttribute(
                HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, 
                new MockSecurityContext(principal));


        super.mockMvc
            .perform(
                    get("/api/v1/resource/test")
                    .session(session))
            .andExpect(status().isOk());
    }
}

2
我们尚未为Spring Security添加官方支持。请参见https://jira.springsource.org/browse/SEC-2015。有关其外观的概述在https://github.com/SpringSource/spring-test-mvc/blob/master/src/test/java/org/springframework/test/web/server/samples/context/SpringSecurityTests.java中指定。 - Rob Winch
我认为创建一个身份验证对象并添加相应属性的会话并不是那么糟糕。你认为这是一个有效的“解决方法”吗?当然,直接支持将会很棒。看起来非常不错。感谢链接! - Martin Becker
很棒的解决方案。对我有用!只是在受保护方法getPrincipal()的命名上有一个小问题,我认为这有点误导。理想情况下,它应该被命名为getAuthentication()。同样,在您的signedIn()测试中,局部变量应该被命名为authauthentication而不是principal - Tanvir
“getPrincipal("test1")” 是什么?你能解释一下它在哪里吗?谢谢。 - user2992476
@user2992476 它可能会返回一个类型为UsernamePasswordAuthenticationToken的对象。或者,您可以创建GrantedAuthority并构造此对象。 - bluelurker
如果您拥有自己的UserDetailsService,那么模拟它或其内部状态(例如服务实现背后的存储库)会更方便吗?我不需要编写任何内容,只需从存储库中模拟出现有用户检索(加上在整个集成测试环境中的noop密码编码器设置)。 - newhouse

32
在pom.xml中添加:
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-test</artifactId>
        <version>4.0.0.RC2</version>
    </dependency>

使用org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors进行授权请求。请参见https://github.com/rwinch/spring-security-test-blog的示例用法(https://jira.spring.io/browse/SEC-2592)。

更新:

4.0.0.RC2适用于spring-security 3.x。 对于spring-security 4,spring-security-test成为spring-security的一部分,版本号相同(http://docs.spring.io/spring-security/site/docs/4.0.x/reference/htmlsingle/#test)。

设置已更改:http://docs.spring.io/spring-security/site/docs/4.0.x/reference/htmlsingle/#test-mockmvc

public void setup() {
    mvc = MockMvcBuilders
            .webAppContextSetup(context)
            .apply(springSecurity())  
            .build();
}

基本认证示例:http://docs.spring.io/spring-security/site/docs/4.0.x/reference/htmlsingle/#testing-http-basic-authentication


1
这也解决了我在尝试通过登录安全过滤器登录时遇到404错误的问题。谢谢! - Ian Newland
1
你好,按照GKislin所述进行测试时,我遇到了以下错误:“身份验证失败,UserDetailsService返回null,这是接口合同违规”。请问有什么建议吗?最终的AuthenticationRequest auth = new AuthenticationRequest(); auth.setUsername(userId); auth.setPassword(password); mockMvc.perform(post("/api/auth/").content(json(auth)).contentType(MediaType.APPLICATION_JSON)); - Sanjeev

7

以下是一个示例,供想要使用Base64基本身份验证测试Spring MockMvc安全配置的人参考:

String basicDigestHeaderValue = "Basic " + new String(Base64.encodeBase64(("<username>:<password>").getBytes()));
this.mockMvc.perform(get("</get/url>").header("Authorization", basicDigestHeaderValue).accept(MediaType.APPLICATION_JSON)).andExpect(status().isOk());

Maven依赖

    <dependency>
        <groupId>commons-codec</groupId>
        <artifactId>commons-codec</artifactId>
        <version>1.3</version>
    </dependency>

4

简短回答:

@Autowired
private WebApplicationContext webApplicationContext;

@Autowired
private Filter springSecurityFilterChain;

@Before
public void setUp() throws Exception {
    final MockHttpServletRequestBuilder defaultRequestBuilder = get("/dummy-path");
    this.mockMvc = MockMvcBuilders.webAppContextSetup(this.webApplicationContext)
            .defaultRequest(defaultRequestBuilder)
            .alwaysDo(result -> setSessionBackOnRequestBuilder(defaultRequestBuilder, result.getRequest()))
            .apply(springSecurity(springSecurityFilterChain))
            .build();
}

private MockHttpServletRequest setSessionBackOnRequestBuilder(final MockHttpServletRequestBuilder requestBuilder,
                                                             final MockHttpServletRequest request) {
    requestBuilder.session((MockHttpSession) request.getSession());
    return request;
}

执行Spring Security测试中的formLogin后,您的每个请求都将自动以已登录用户的身份调用。
长答案:
请参考此解决方案(该答案适用于Spring 4):如何使用Spring 3.2新的MVC测试登录用户

2

避免在测试中使用SecurityContextHolder的选项:

  • 选项1:使用模拟 - 我的意思是使用某些模拟库来模拟SecurityContextHolder - 例如EasyMock
  • 选项2:在您的代码中包装调用SecurityContextHolder.get... - 例如,在SecurityServiceImpl中使用getCurrentPrincipal方法实现SecurityService接口,然后在您的测试中,您可以简单地创建一个模拟实现该接口,返回所需的主体,而无需访问SecurityContextHolder

嗯,也许我没有完全理解整个情况。我的问题是SecurityContextPersistenceFilter使用HttpSessionSecurityContextRepository中的SecurityContext替换SecurityContext,后者又从相应的HttpSession中读取SecurityContext。因此,使用会话的解决方案。关于对SecurityContextHolder的调用:我编辑了我的答案,所以不再使用对SecurityContextHolder的调用。但是也没有引入任何包装或额外的模拟库。你认为这是一个更好的解决方案吗? - Martin Becker
抱歉,我不太明白您要找什么,而且我无法提供比您想出的解决方案更好的答案 - 看起来这是一个不错的选择。 - Pavla Nováková
好的,谢谢。我现在会接受我的建议作为解决方案。 - Martin Becker

1

虽然回答有点晚,但这对我很有效,也可能对你有用。

在使用Spring Security和mockMvc时,你只需要像其他人提到的那样使用@WithMockUser注释。

Spring security还提供了另一个名为@WithAnonymousUser的注释,用于测试未经身份验证的请求。但是你应该小心。你可能期望得到401,但默认情况下我得到了403禁止错误。在实际场景中,当你运行实际服务时,它会被重定向,并最终得到正确的401响应代码。使用此注释进行匿名请求。

你也可以考虑省略注释并保持未经授权状态。但是,这通常会引发正确的异常(如AuthenticationException),但如果处理正确,则会得到正确的状态代码(如果你使用自定义处理程序)。我曾经因此得到500。所以在调试器中查找引发的异常,并检查是否正确处理并返回正确的状态代码。


1
当使用MockMvcBuilders.webAppContextSetup(wac).addFilters(...)而不是springSecurityFilterChain(更具体地说是SecurityContextPersistenceFilter)时,将接管并删除由 @WithMockUser 准备的 SecurityContext (非常愚蠢);这是因为 SecurityContextPersistenceFilter 尝试从找不到的 HttpSession “恢复” SecurityContext 。好吧,使用下面定义的简单的 AutoStoreSecurityContextHttpFilter 来处理将 @WithMockUser 的准备好的 SecurityContext 放入 HttpSession 中,以便稍后 SecurityContextPersistenceFilter 能够找到它。
@ContextConfiguration(...) // the issue doesn't occur when using @SpringBootTest
public class SomeTest {
    @Autowired
    private Filter springSecurityFilterChain;
    private MockMvc mockMvc;

    @BeforeEach
    void setup(WebApplicationContext wac) {
        this.mockMvc = MockMvcBuilders.webAppContextSetup(wac)
                .addFilters(new AutoStoreSecurityContextHttpFilter(), springSecurityFilterChain).build();
    }

    @WithMockUser
    @Test
    void allowAccessToAuthenticated() {
        ...
    }
}

// don't use this Filter in production because it's only intended for tests, to solve the 
// @WithMockUser & springSecurityFilterChain (more specifically SecurityContextPersistenceFilter) "misunderstandings"
public class AutoStoreSecurityContextHttpFilter extends HttpFilter {
    protected void doFilter(HttpServletRequest req, HttpServletResponse res, FilterChain chain) throws IOException, ServletException {
        req.getSession().setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, SecurityContextHolder.getContext());
        super.doFilter(req, res, chain);
    }
}

1
在您的测试包中创建一个名为TestUserDetailsImpl的类:
@Service
@Primary
@Profile("test")
public class TestUserDetailsImpl implements UserDetailsService {
    public static final String API_USER = "apiuser@example.com";

    private User getAdminUser() {
        User user = new User();
        user.setUsername(API_USER);

        SimpleGrantedAuthority role = new SimpleGrantedAuthority("ROLE_API_USER");
        user.setAuthorities(Collections.singletonList(role));

        return user;
    }

    @Override
    public UserDetails loadUserByUsername(String username) 
                                         throws UsernameNotFoundException {
        if (Objects.equals(username, ADMIN_USERNAME))
            return getAdminUser();
        throw new UsernameNotFoundException(username);
    }
}

Rest端点:

@GetMapping("/invoice")
@Secured("ROLE_API_USER")
public Page<InvoiceDTO> getInvoices(){
   ...
}

测试端点:

@Test
@WithUserDetails("apiuser@example.com")
public void testApi() throws Exception {
     ...
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接