我们正在使用Spring Boot构建RESTful Web服务。我们希望进行2级身份验证以保护端点。
首先,对于每个请求,我们要检查请求标头中是否指定了apiKey,如果没有,我们将拒绝该请求。如果请求具有apiKey,我们将使用某些请求的用户名/密码登录进行下一次身份验证。有公共终结点仅需要apiKey身份验证,而私有终结点仅需要apiKey身份验证,然后需要用户名/密码auth才能访问它们。
对于apiKey身份验证,我复制了代码here,我还可以找到许多有关用户名/密码身份验证的示例。
我的问题是:如何在WebSecurityConfigurerAdapter中进行Java配置以将它们组合在一起。
现在,我为这2个身份验证筛选器定义了2个扩展WebSecurityConfigurerAdapter的配置类,但是请求将仅通过其中之一,具体取决于我将哪个设置为@Order(1)。
谢谢。
答案 0 :(得分:0)
这个完整的答案由运行中的Spring Boot应用程序提供支持,并带有单元测试来确认。
如果您认为此答案有帮助,请对其进行投票。
简短的答案是您的安全配置看起来像这样
http
.sessionManagement()
.disable()
//application security
.authorizeRequests()
.anyRequest().hasAuthority("API_KEY")
.and()
.addFilterBefore(new ApiKeyFilter(), HeaderWriterFilter.class)
.addFilterAfter(new UserCredentialsFilter(), ApiKeyFilter.class)
.csrf().ignoringAntMatchers(
"/api-key-only",
"/dual-auth"
)
;
// @formatter:on
}
}
让我告诉您发生了什么事。我鼓励您查看我的样本,尤其是涵盖您许多情况的unit tests。
我们有两个安全级别 1.每个API必须由ApiKey保护 2.只有某些API必须由UserCredentials保护
在我的example project中,我选择了以下解决方案
我使用WebSecurityConfigurerAdapter来满足ApiKey要求
.authorizeRequests()
.anyRequest().hasAuthority("API_KEY")
我通过启用方法级别的安全性
@EnableGlobalMethodSecurity(prePostEnabled = true)
然后在我的控制器中要求它
@PreAuthorize("hasAuthority('USER_CREDENTIALS')")
public String twoLayersOfAuth() {
//only logic here
}
ApiKey过滤器非常简单
public class ApiKeyFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
final String authorization = request.getHeader("Authorization");
final String prefix = "ApiKey ";
if (hasText(authorization) && authorization.startsWith(prefix)) {
String key = authorization.substring(prefix.length());
if ("this-is-a-valid-key".equals(key)) {
RestAuthentication<SimpleGrantedAuthority> authentication = new RestAuthentication<>(
key,
Collections.singletonList(new SimpleGrantedAuthority("API_KEY"))
);
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
filterChain.doFilter(request, response);
}
}
,第二层身份验证甚至很简单(并且依赖于第一层身份执行)
public class UserCredentialsFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
final String userCredentials = request.getHeader("X-User-Credentials");
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if ("valid-user".equals(userCredentials) && authentication instanceof RestAuthentication) {
RestAuthentication<SimpleGrantedAuthority> restAuthentication =
(RestAuthentication<SimpleGrantedAuthority>)authentication;
restAuthentication.addAuthority(new SimpleGrantedAuthority("USER_CREDENTIALS"));
}
filterChain.doFilter(request, response);
}
}
请注意:没有身份验证或身份验证不足时,每个过滤器如何不关心会发生什么。这一切都为您服务。您的过滤器只需要验证正确的数据即可;
Spring,Spring Boot和Spring Security具有一些出色的测试工具。
我可以调用具有两种安全级别的仅api端点
mvc.perform(
post("/api-key-only")
.header("Authorization", "ApiKey this-is-a-valid-key")
.header("X-User-Credentials", "valid-user")
)
.andExpect(status().isOk())
.andExpect(authenticated()
.withAuthorities(
asList(
new SimpleGrantedAuthority("API_KEY"),
new SimpleGrantedAuthority("USER_CREDENTIALS")
)
)
)
.andExpect(content().string("API KEY ONLY"))
;
或者我可以通过第一级安全保护并被第二级拒绝
mvc.perform(
post("/dual-auth")
.header("Authorization", "ApiKey this-is-a-valid-key")
)
.andExpect(status().is4xxClientError())
.andExpect(authenticated()
.withAuthorities(
asList(
new SimpleGrantedAuthority("API_KEY")
)
)
)
;
当然,我们总有一条快乐的路
mvc.perform(
post("/dual-auth")
.header("Authorization", "ApiKey this-is-a-valid-key")
.header("X-User-Credentials", "valid-user")
)
.andExpect(status().isOk())
.andExpect(content().string("DUAL AUTH"))
.andExpect(authenticated()
.withAuthorities(
asList(
new SimpleGrantedAuthority("API_KEY"),
new SimpleGrantedAuthority("USER_CREDENTIALS")
)
)
)
;