Java 使用API密钥和密码保护Spring Boot API
我想保护SpringBootAPI,使其仅对具有有效API密钥和密码的客户端可访问。但是,由于所有数据都是匿名的,程序内部没有身份验证(使用用户名和密码的标准登录)。我试图实现的是,所有API请求只能用于特定的第三方前端 我找到了很多关于如何使用用户身份验证保护Spring引导API的文章。但我不需要用户身份验证。我所想的只是向我的客户机提供API密钥和机密,以便他能够访问端点Java 使用API密钥和密码保护Spring Boot API,java,spring,api,spring-boot,spring-security,Java,Spring,Api,Spring Boot,Spring Security,我想保护SpringBootAPI,使其仅对具有有效API密钥和密码的客户端可访问。但是,由于所有数据都是匿名的,程序内部没有身份验证(使用用户名和密码的标准登录)。我试图实现的是,所有API请求只能用于特定的第三方前端 我找到了很多关于如何使用用户身份验证保护Spring引导API的文章。但我不需要用户身份验证。我所想的只是向我的客户机提供API密钥和机密,以便他能够访问端点 你能建议我怎样才能做到这一点吗?谢谢大家! 创建一个过滤器,用于获取用于身份验证的任何标头 import org.sp
你能建议我怎样才能做到这一点吗?谢谢大家! 创建一个过滤器,用于获取用于身份验证的任何标头
import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter;
public class APIKeyAuthFilter extends AbstractPreAuthenticatedProcessingFilter {
private String principalRequestHeader;
public APIKeyAuthFilter(String principalRequestHeader) {
this.principalRequestHeader = principalRequestHeader;
}
@Override
protected Object getPreAuthenticatedPrincipal(HttpServletRequest request) {
return request.getHeader(principalRequestHeader);
}
@Override
protected Object getPreAuthenticatedCredentials(HttpServletRequest request) {
return "N/A";
}
}
在Web安全配置中配置筛选器
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
@Configuration
@EnableWebSecurity
@Order(1)
public class APISecurityConfig extends WebSecurityConfigurerAdapter {
@Value("${yourapp.http.auth-token-header-name}")
private String principalRequestHeader;
@Value("${yourapp.http.auth-token}")
private String principalRequestValue;
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
APIKeyAuthFilter filter = new APIKeyAuthFilter(principalRequestHeader);
filter.setAuthenticationManager(new AuthenticationManager() {
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String principal = (String) authentication.getPrincipal();
if (!principalRequestValue.equals(principal))
{
throw new BadCredentialsException("The API key was not found or not the expected value.");
}
authentication.setAuthenticated(true);
return authentication;
}
});
httpSecurity.
antMatcher("/api/**").
csrf().disable().
sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).
and().addFilter(filter).authorizeRequests().anyRequest().authenticated();
}
}
我意识到我在这个游戏上有点晚了,但我也设法获得了与Spring Boot配合使用的API密钥以及用户名/密码身份验证。我对使用
AbstractPreAuthenticatedProcessingFilter
的想法并不着迷,因为在阅读JavaDoc时,它似乎是对特定类的误用
最后,我创建了一个新的ApiKeyAuthenticationToken
类以及一个非常简单的原始servlet过滤器,以完成以下任务:
import java.util.Collection;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.Transient;
@Transient
public class ApiKeyAuthenticationToken extends AbstractAuthenticationToken {
private String apiKey;
public ApiKeyAuthenticationToken(String apiKey, Collection<? extends GrantedAuthority> authorities) {
super(authorities);
this.apiKey = apiKey;
setAuthenticated(true);
}
@Override
public Object getCredentials() {
return null;
}
@Override
public Object getPrincipal() {
return apiKey;
}
}
此时剩下的就是在链中的适当位置注入过滤器。在我的例子中,我希望在任何用户名/密码身份验证之前评估API密钥身份验证,以便在应用程序尝试重定向到登录页面之前对请求进行身份验证:
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf()
.disable()
.addFilterBefore(new ApiKeyAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
.authorizeRequests()
.anyRequest()
.fullyAuthenticated()
.and()
.formLogin();
}
我要说的另一件事是,您应该注意的是,您的API密钥验证请求不会在您的服务器上创建和放弃一堆
HttpSession
。来自@MarkOfHall的答案是正确的,我只想补充一点细节。获得代码后,需要将属性值添加到application.properties
文件中,如下所示:
yourapp.http.auth-token-header-name=X-API-KEY
yourapp.http.auth-token=abc123
在邮递员中设置身份验证值,如下所示:
yourapp.http.auth-token-header-name=X-API-KEY
yourapp.http.auth-token=abc123
您可以使用邮递员,但如果您使用cURL
请求将类似于以下内容:
$ curl -H "X-API-KEY: abc123" "http://localhost:8080/api/v1/property/1"
除非提供正确的密钥和值,否则应用程序将无法运行。唯一的区别是您将其称为API密钥而不是用户名,还是有其他区别?下面的答案是否解决了您的问题?您是如何管理您的用户和每个用户的api密钥的?这非常有用。我有一个应用程序,它需要同时支持用户名/密码和基于ApiKey的身份验证。我有用户名/密码工作,并在阅读您的帖子后,能够得到ApiKey工作。不幸的是,我似乎破坏了用户名/密码。我怀疑这是因为我的过滤器排序或我对用户名/密码和ApiKey身份验证使用了相同的AuthenticationManager。有什么建议吗?@PhillipStack您应该能够使用不同的身份验证管理器配置两个WebSecurity配置适配器。ala:如果我理解正确,APIKey不是私有的。任何使用客户端的人都可以打开开发人员控制台并检查标题内容。它是对的吗?@marcellorvalle通常,使用API密钥保护的API的客户端是另一个服务。如果您推断此API的客户端将是一个web浏览器,那么我建议您研究OAuth/JWT令牌以获得用户授权。难道不是相反吗?主体应该是“N/A”、“API密钥持有者”或类似的,然后凭证将是实际的API密钥?这对我来说非常有效。但是,建议在生产中使用吗?一般来说,API密钥不如OAuth安全。但它们更简单,这也是吸引力的一部分。这一权衡是否值得,取决于您的需求和应用程序的部署方式。我的特定应用程序是一个内部应用程序,不接受来自外部世界的连接,因此在我的情况下,权衡是值得的。但是,例如,我不会将API密钥作为唯一的安全机制部署到移动应用程序,因为应用程序的任何用户都可以获得该API密钥。