我们有一个Sping Boot 后端应用程序,它使用Spring Security和OAuth2 Resource Server实现的JWT身份验证。我们通过使用antMatchers()
限制对基于用户角色的URL模式的访问,并使用全局方法安全性限制对服务层方法的访问,从而保护了Web层。
我们的JWT标记由Auth 0提供,在Auth 0中,我们已配置为包括其他用户元数据作为自定义声明。这些声明值在@PreAuthorize
注解中进行验证,例如:
@PreAuthorize("authentication.principal.claims[@environment.getProperty('jwt.organizationIdClaim')] == @environment.getProperty('current.organizationId')")
public void deleteUser(String userId) {
此处的性质jwt.organizationIdClaim
和current.organizationId
是从www.example.com档案取得的自订性质application.properties。
当我尝试对上面的方法进行单元测试时,我得到了如下异常:org.springframework.security.authentication.AuthenticationCredentialsNotFoundException: An Authentication object was not found in the SecurityContext
单元测试代码:
@Test
void testDoNotAllowDeletingUsersWithRootRole() {
auth0ManagementAPIService.deleteUser("auth0|REDACTED");
assertTrue(true);
}
我尝试使用@WithMockUser
运行此测试,但这导致无法计算@PreAuthorize表达式:java.lang.IllegalArgumentException: Failed to evaluate expression 'authentication.principal.claims[@environment.getProperty('jwt.organizationIdClaim')] == @environment.getProperty('current.organizationId')'
是否有任何方法可以将这些自定义声明数据添加到这些单元测试的SecurityContext
中,以便@PreAuthorize表达式成功计算?
我们通过使用SecurityMockMvcRequestPostProcessors.jwt()
绕过了Web层中的JWT要求,也许有一些类似的东西可以用于@Service类方法?
我们使用的JWT的示例负载如下所示:
{
"https://dev.api.REDACTED.com/roles": [
"RESEARCHER",
"ADMIN",
"ROOT"
],
"https://dev.api.REDACTED.com/organizationId": "REDACTED",
"https://dev.api.REDACTED.com/userId": "REDACTED",
"iss": "https://dev.login.REDACTED.com/",
"sub": "auth0|REDACTED",
"aud": [
"https://dev.api.REDACTED.com",
"https://REDACTED.us.auth0.com/userinfo"
],
"iat": 1660626647,
"exp": 1660713047,
"azp": "REDACTED",
"scope": "openid profile email offline_access",
"permissions": [
...
]
}
这里的https://dev.api.REDACTED.com/roles
、https://dev.api.REDACTED.com/organizationId
和https://dev.api.REDACTED.com/userId
是自定义声明,我们需要在@预授权条件中验证其值。
主体对象的类型为Jwt,并使用自定义转换器按如下方式生成:
import org.springframework.core.convert.converter.Converter;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
import java.util.ArrayList;
import java.util.List;
public class CustomJwtAuthenticationConverter implements Converter<Jwt, AbstractAuthenticationToken> {
private final String rolesClaimName;
public CustomJwtAuthenticationConverter(String rolesClaimName) {
this.rolesClaimName = rolesClaimName;
}
@Override
public AbstractAuthenticationToken convert(Jwt jwt) {
// Get roles claim from the JWT token and add it to a granted authorities list.
List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
List<String> rolesList = jwt.getClaimAsStringList(rolesClaimName);
rolesList.forEach(r -> grantedAuthorities.add(new SimpleGrantedAuthority(r)));
return new JwtAuthenticationToken(jwt, grantedAuthorities, jwt.getSubject());
}
}
然后将其注入安全配置中的安全筛选器链:
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.oauth2ResourceServer().jwt().jwtAuthenticationConverter(customJwtGrantedAuthoritiesConverter());
http
.cors().and()
.authorizeRequests(
authorize -> authorize
.antMatchers("/swagger-ui/**", "/v3/api-docs/**").permitAll()
.antMatchers("/actuator", "/actuator/health").permitAll()
.antMatchers(HttpMethod.DELETE, "/users/**").hasAuthority(ROOT_ROLE)
.antMatchers("/users/**").hasAnyAuthority(ADMIN_ROLE, ROOT_ROLE)
.anyRequest().authenticated());
return http.build();
}
@Bean
public Converter<Jwt, AbstractAuthenticationToken> customJwtGrantedAuthoritiesConverter() {
return new CustomJwtAuthenticationConverter(rolesClaimName);
}
1条答案
按热度按时间o4tp2gmn1#
我有你需要的东西https://github.com/ch4mpy/spring-addons
示例here:
@OpenIdClaims
允许您配置标准OpenID声明(但不是强制性的)和您喜欢的任何私有声明。测试注解相对于请求后处理器的优势是它不需要MockMvc就能工作(当你想对安全的
@Component
进行单元测试时,它不是@Controller
,比如@Service
或@Repository
)。不幸的是,spring安全团队在我贡献OAuth2 MockMvc后处理器(和WebTestClient mutators)时对它不感兴趣。我启动上面链接的库的原因。PS
您可能会在other tutorials中找到有用的想法。
resource-server_with_oauthentication
可以为您节省相当多的配置代码,而resource-server_with_specialized_oauthentication
可以帮助您提高安全表达式的可读性。