【问题标题】:How can I mock Authentication in a Spring Boot when using an Exernal Authentication Source使用外部身份验证源时如何在 Spring Boot 中模拟身份验证
【发布时间】:2020-06-09 12:47:05
【问题描述】:

我有一个基于 Spring Boot 的应用程序,我们使用来自外部提供商的 ISAM 身份验证。 我有一个 rest/json 端点 /actuator/health,它根据用户是否经过身份验证返回不同的数据。

如何在单元测试期间模拟身份验证以确保我的配置正确?

setup() 中,我尝试手动设置令牌,并覆盖 AuthorizationService 以返回 true。

@Before
public void setUp() throws Exception
{
   mockMvc = webAppContextSetup(wac).apply(springSecurity()).build();

   List roles = Arrays.asList(new SimpleGrantedAuthority("ROLE_USER"));

   UsernamePasswordAuthenticationToken auth =
           new UsernamePasswordAuthenticationToken("dave", "secret",
                                                   roles);

   if (!auth.isAuthenticated()) { fail("NOT AUTHENTICATED!"); }

   SecurityContextHolder.getContext().setAuthentication(auth);

   //fake logged in
   when(authorizationService.isCurrentUserAuthorized(anyString(),
                                                     anyString(),
                                                     ArgumentMatchers.any(ResourceType.class),
                                                     ArgumentMatchers.any(ActionType.class)))
     .thenReturn(true);
}

但是,当我跑步时

Authentication auth = SecurityContextHolder.getContext().getAuthentication();

if (!auth.isAuthenticated()) { fail("NOT AUTHENTICATED!"); }

UsernamePasswordAuthenticationToken authToken =
              (UsernamePasswordAuthenticationToken)auth;

mockMvc.perform(get("/health_secure")
                  .principal(auth)
                  .header("Authorization", "Bearer " + token))
       .andDo(print())
       .andExpect(status().isOk())
       .andExpect(forwardedUrl("/actuator/health"));

我明白了:

"error":"invalid_token","error_description":"Cannot convert access token to JSON"

【问题讨论】:

  • 您正在运行什么样的测试? @Controller 单元测试还是集成测试?

标签: java spring spring-boot spring-security mockmvc


【解决方案1】:

我的应用程序使用 ISAM/OAuth2 进行身份验证

我希望只是覆盖 AuthenticationManagerOAuth2AuthenticationManager

我找到了一个稍微低级的解决方案。

注意:我希望看到AuthenticationManager 级别的解决方案。

我不得不重写 ResourceServerTokenServices bean 来模拟我的假身份验证数据。

我在@Before 方法中添加了一个setAuth() 方法。

   //"constants" for fake auth values
   private static final String user = "MyUser";
   private static final String token = "MyTokenString";

   //TokenServices to validate/convert ISAM Bearer Token
   @MockBean ResourceServerTokenServices tokenServices;

   private Authentication setAuth() throws Exception
   {
      UsernamePasswordAuthenticationToken auth =
              new UsernamePasswordAuthenticationToken(user, token,
                                                      Collections.emptyList());

      if (!auth.isAuthenticated()) { fail("NOT AUTHENTICATED!"); }

      SecurityContextHolder.getContext().setAuthentication(auth);

      DefaultOAuth2AccessToken mockTokenImpl = new DefaultOAuth2AccessToken(user);
      mockTokenImpl.setScope(Collections.singleton("authenticate:applications"));
      mockTokenImpl.setTokenType("Bearer");

      when(tokenServices.readAccessToken(anyString())).thenReturn(mockTokenImpl);

      OAuth2Authentication oa2Auth = mock(OAuth2Authentication.class);
      when(oa2Auth.getPrincipal()).thenReturn(user);
      when(oa2Auth.getCredentials()).thenReturn(token);
      when(oa2Auth.getUserAuthentication()).thenReturn(auth);
      when(oa2Auth.isAuthenticated()).thenReturn(true);

      OAuth2Request oa2Req = mock(OAuth2Request.class);
      when(oa2Auth.getOAuth2Request()).thenReturn(oa2Req);

      when(tokenServices.loadAuthentication(anyString())).thenReturn(oa2Auth);

      return auth;
   }

此方法将tokenServices 配置为拥有所有必要的信息来伪造通过authenticate() 和Springs OAuth2AuthenticationManager 类的方式。

【讨论】:

    【解决方案2】:

    很好的答案here。由于 Spring Security 不会创建请求范围的 bean,因此请自己创建一个并将其注入到您需要的地方。这使您有机会自己模拟它。

    像这样?:

    @Service
    @RequiredArgsConstructor
    public class UserService {
    
        public Principal getPrincipal() {
            return SecurityContextHolder.getContext().getAuthentication();
        }
    
        public Optional<String> getCurrentLogin() {
            Principal principal = getPrincipal();
            if (principal == null)
                return Optional.empty();
            return Optional.ofNullable(principal.getName());
        }
    
    }
    

    【讨论】:

      猜你喜欢
      • 2015-08-09
      • 2017-11-02
      • 2020-10-29
      • 1970-01-01
      • 2018-02-27
      • 2017-04-12
      • 2017-07-30
      • 2019-05-22
      • 2013-02-18
      相关资源
      最近更新 更多