Java 弹簧靴。HMAC认证。如何添加自定义AuthenticationProvider和身份验证筛选器?

Java 弹簧靴。HMAC认证。如何添加自定义AuthenticationProvider和身份验证筛选器?,java,authentication,spring-security,spring-boot,hmac,Java,Authentication,Spring Security,Spring Boot,Hmac,为了实现HMAC身份验证,我制作了自己的过滤器、提供者和令牌。 RestSecurityFilter: public class RestSecurityFilter extends AbstractAuthenticationProcessingFilter { private final Logger LOG = LoggerFactory.getLogger(RestSecurityFilter.class); private AuthenticationManager authenti

为了实现HMAC身份验证,我制作了自己的过滤器、提供者和令牌。 RestSecurityFilter:

public class RestSecurityFilter extends AbstractAuthenticationProcessingFilter {
private final Logger LOG = LoggerFactory.getLogger(RestSecurityFilter.class);

private AuthenticationManager authenticationManager;

public RestSecurityFilter(String defaultFilterProcessesUrl) {
    super(defaultFilterProcessesUrl);
}

public RestSecurityFilter(RequestMatcher requiresAuthenticationRequestMatcher) {
    super(requiresAuthenticationRequestMatcher);
}

@Override
public Authentication attemptAuthentication(HttpServletRequest req, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
    AuthenticationRequestWrapper request = new AuthenticationRequestWrapper(req);

    // Get authorization headers
    String signature = request.getHeader("Signature");
    String principal = request.getHeader("API-Key");
    String timestamp = request.getHeader("timestamp");
    if ((signature == null) || (principal == null) || (timestamp == null))
    unsuccessfulAuthentication(request, response, new BadHMACAuthRequestException("Authentication attempt failed! Request missing mandatory headers."));


    // a rest credential is composed by request data to sign and the signature
    RestCredentials credentials = new RestCredentials(HMACUtils.calculateContentToSign(request), signature);

    // Create an authentication token
    return new RestToken(principal, credentials, Long.parseLong(timestamp));
}

@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
    LOG.debug("Filter request: " + req.toString());
    HttpServletRequest request = (HttpServletRequest) req;
    HttpServletResponse response = (HttpServletResponse) res;

    chain.doFilter(request, response);

    Authentication authResult;

    try {
        authResult = attemptAuthentication(request, response);
        if (authResult == null)
            unsuccessfulAuthentication(request, response, new BadHMACAuthRequestException("Authentication attempt failed !"));

    } catch (InternalAuthenticationServiceException failed) {
        LOG.error("An internal error occurred while trying to authenticate the user.", failed);
        unsuccessfulAuthentication(request, response, failed);
    } catch (AuthenticationException failed) {
        // Authentication failed
        unsuccessfulAuthentication(request, response, failed);
    }
}
}
身份验证提供程序:

@Component
public class RestAuthenticationProvider implements AuthenticationProvider {
private final Logger LOG = LoggerFactory.getLogger(RestAuthenticationProvider.class);

private ApiKeysService apiKeysService;

@Autowired
public void setApiKeysService(ApiKeysService apiKeysService) {
    this.apiKeysService = apiKeysService;
}

@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
    RestToken restToken = (RestToken) authentication;

    // api key (aka username)
    String principal = restToken.getPrincipal();

    LOG.info("Authenticating api key: '" + principal + "'");

    // check request time, 60000 is one minute
    long interval = Clock.systemUTC().millis() - restToken.getTimestamp();
    if ((interval < 0) && (interval > 60000))
        throw new BadHMACAuthRequestException("Auth Failed: old request.");

    // hashed blob
    RestCredentials credentials = restToken.getCredentials();

    // get secret access key from api key
    ApiKey apiKey = apiKeysService.getKeyByName(principal).orElseThrow(() -> new NotFoundException("Key not found for: '" + principal + "'"));
    String secret = apiKey.getApiKey();

    // calculate the hmac of content with secret key
    String hmac = HMACUtils.calculateHMAC(secret, credentials.getRequestData());
    LOG.debug("Api Key '{}', calculated hmac '{}'");

    // check if signatures match
    if (!credentials.getSignature().equals(hmac)) {
        throw new BadHMACAuthRequestException("Auth Failed: invalid HMAC signature.");
    }

    return new RestToken(principal, credentials, restToken.getTimestamp(), apiKeysService.getPermissions(apiKey));
}

@Override
public boolean supports(Class<?> authentication) {
    return RestToken.class.equals(authentication);

}
}
@组件
公共类重新验证提供程序实现AuthenticationProvider{
私有最终记录器LOG=LoggerFactory.getLogger(RestAuthenticationProvider.class);
私人APIKEYSERVICE APIKEYSERVICE;
@自动连线
公共void setApikeyService(ApikeyService ApikeyService){
this.apikesservice=apikesservice;
}
@凌驾
公共身份验证(身份验证)引发AuthenticationException{
RestToken RestToken=(RestToken)身份验证;
//api密钥(又名用户名)
字符串主体=restToken.getPrincipal();
LOG.info(“验证api密钥:“+principal+””);
//检查请求时间,60000是一分钟
长间隔=Clock.systemUTC().millis()-restToken.getTimestamp();
如果((间隔<0)和&(间隔>60000))
抛出新的BadHMACAuthRequestException(“身份验证失败:旧请求”);
//散列斑点
RestCredentials=restToken.getCredentials();
//从api密钥获取秘密访问密钥
ApiKey ApiKey=APIKEYSERVICE.getKeyByName(主体).OrelsThrow(()->new NotFoundException(“找不到的键:“+principal+””));
字符串secret=apiKey.getApiKey();
//使用密钥计算内容的hmac
字符串hmac=HMACUtils.calculateHMAC(secret,credentials.getRequestData());
debug(“Api键'{}',计算hmac'{}'”;
//检查签名是否匹配
如果(!credentials.getSignature().equals(hmac)){
抛出新的BadHMACAuthRequestException(“身份验证失败:无效的HMAC签名”);
}
返回新的RestToken(主体、凭据、RestToken.getTimestamp()、ApikesService.getPermissions(apiKey));
}
@凌驾
公共布尔支持(类身份验证){
返回RestToken.class.equals(身份验证);
}
}

我不知道如何配置WebSecurityConfig以使用我的筛选器和身份验证提供程序对每个请求进行身份验证。我假设我需要创建@Bean来初始化
RestSecurityFilter
。另外,JavaDoc for
AbstractAuthenticationProcessingFilter
说我需要使用
authenticationManager
属性。我将感谢与自定义过滤器,供应商和令牌工作的解决方案

我不熟悉Spring Boot,但我看到了您对我的问题的评论

在传统的SpringSecurityXML配置中,您可以这样指定自定义的RestSecurityFilter

<http use-expressions="true" create-session="stateless" authentication-manager-ref="authenticationManager" entry-point-ref="restAuthenticationEntryPoint">
       <custom-filter ref="restSecurityFilter" position="FORM_LOGIN_FILTER" />
</http>

更多信息