2 回答
TA贡献1906条经验 获得超3个赞
在您的要求中,由于没有角色(不同的客户端具有不同的访问级别),因此不需要 UserDetailService。
APIKeyFilter 足以与 X509 和 API 密钥配合使用。
考虑APIKeyFilter
扩展X509AuthenticationFilter
,如果存在没有有效证书的请求,则过滤器链将被破坏,并且将发送403
/的错误响应。 如果证书有效,则过滤器链继续并进行身份验证。而验证我们所拥有的只有来自身份验证对象的两个方法 - - 。其中主题是 (EMAIL=, CN=, OU=, O=, L=, ST=, C=) (APIKeyFilter 应配置为返回主体和凭证对象) 您可以使用主体(您的 API 密钥)来验证 api 密钥由客户发送。您 可以使用凭据(证书主题)作为增强功能来单独识别每个客户端,如果需要,您可以为不同的客户端授予不同的权限。Forbidden
getPrincipal()
header:"x-api-key"
getCredential()
certificate subject
回顾您的要求
1. API V1 - 仅当证书和 API 密钥有效时才可访问。
2. 其他API - 无限制
TA贡献1775条经验 获得超11个赞
public class APIKeyFilter extends X509AuthenticationFilter
{
private String principalRequestHeader;
public APIKeyFilter(String principalRequestHeader)
{
this.principalRequestHeader = principalRequestHeader;
}
@Override
protected Object getPreAuthenticatedPrincipal(HttpServletRequest request)
{
return request.getHeader(principalRequestHeader);
}
@Override
protected Object getPreAuthenticatedCredentials(HttpServletRequest request)
{
X509Certificate[] certs = (X509Certificate[]) request
.getAttribute("javax.servlet.request.X509Certificate");
if(certs.length > 0)
{
return certs[0].getSubjectDN();
}
return super.getPreAuthenticatedCredentials(request);
}
}
@Configuration
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
private static final String API_KEY_HEADER = "x-api-key";
private String apiKey = "SomeKey1234567890";
@Override
protected void configure(HttpSecurity http) throws Exception
{
APIKeyFilter filter = new APIKeyFilter(API_KEY_HEADER);
filter.setAuthenticationManager(authentication -> {
if(authentication.getPrincipal() == null) // required if you configure http
{
throw new BadCredentialsException("Access Denied.");
}
String apiKey = (String) authentication.getPrincipal();
if (authentication.getPrincipal() != null && this.apiKey.equals(apiKey))
{
authentication.setAuthenticated(true);
return authentication;
}
else
{
throw new BadCredentialsException("Access Denied.");
}
});
http.antMatcher("/v1/**")
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.addFilter(filter)
.authorizeRequests()
.anyRequest()
.authenticated();
}
@Bean
public PasswordEncoder passwordEncoder()
{
return new BCryptPasswordEncoder();
}
}
验证 API 响应
https - 用于数据加密(服务器向客户端发送的 ssl 证书)
X509 - 用于客户端识别(使用服务器 ssl 证书生成的 ssl 证书,但不同客户端不同)
API key - 用于安全检查的共享密钥。
出于验证目的,假设您有 3 个版本,如下所示
@RestController
public class HelloController
{
@RequestMapping(path = "/v1/hello")
public String helloV1()
{
return "HELLO Version 1";
}
@RequestMapping(path = "/v0.9/hello")
public String helloV0Dot9()
{
return "HELLO Version 0.9";
}
@RequestMapping(path = "/v0.8/hello")
public String helloV0Dot8()
{
return "HELLO Version 0.8";
}
}
下面给出不同情况下的响应。
CASE 1.a 版本 1,标头中包含有效的 X509 和 API 密钥
curl -ik --cert pavel.crt --key myPrivateKey.pem -H "x-api-key:SomeKey1234567890" "https://localhost:8443/v1/hello"
回复
HTTP/1.1 200
HELLO Version 1
CASE 1.b 版本 1 仅适用于 X509(无 API 密钥)
curl -ik --cert pavel.crt --key myPrivateKey.pem "https://localhost:8443/v1/hello"
回复
HTTP/1.1 403
{"timestamp":"2019-09-13T11:53:29.269+0000","status":403,"error":"Forbidden","message":"Access Denied","path":"/v1/hello"}
注意:
在您的情况下,有两种类型的证书
i。带有 X509 的客户端证书
ii:如果客户端不包含证书,则将使用服务器中使用的数据交换证书,即不带 X509 的证书
2. 版本 X 不带 X509,标头中不带 API 密钥。
curl "https://localhost:8443/v0.9/hello"
如果服务器证书是自签名证书(没有 CA 即证书颁发机构,证书无效)
curl performs SSL certificate verification by default, using a "bundle"
of Certificate Authority (CA) public keys (CA certs). If the default
bundle file isn't adequate, you can specify an alternate file
using the --cacert option.
If this HTTPS server uses a certificate signed by a CA represented in
the bundle, the certificate verification probably failed due to a
problem with the certificate (it might be expired, or the name might
not match the domain name in the URL).
If you'd like to turn off curl's verification of the certificate, use
the -k (or --insecure) option.
如果服务器 SSL 证书有效(CA 认证)则
curl "https://localhost:8443/v0.9/hello"
你好版本0.9
curl "https://localhost:8443/v0.8/hello"
你好版本0.8
注意:如果您在开发环境中没有 CA 认证的 SSL 证书,请测试 Hack
使用服务器证书(.crt)和serverPrivateKey(.pem 文件)以及请求,如下所示
curl -ik --cert server.crt --key serverPrivateKey.pem "https://localhost:8443/v0.9/hello"
这也可以在 Mozilla 中进行验证(对于自签名证书),并且可以在 google chrome 中进行相同的验证(如果 CA 认证的 SSL)
给出的屏幕截图,在第一次访问期间
添加服务器发送的证书后。
添加回答
举报