弹簧靴。 HMAC 认证。如何添加自定义 AuthenticationProvider 和 Authentication 过滤器?

Posted

技术标签:

【中文标题】弹簧靴。 HMAC 认证。如何添加自定义 AuthenticationProvider 和 Authentication 过滤器?【英文标题】:Spring Boot. HMAC authentication. How to add custom AuthenticationProvider and Authentication filter? 【发布时间】:2015-08-10 17:26:52 【问题描述】:

为了实现 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);



我不知道如何配置 WebSecurityConfig 以使用我的过滤器和身份验证提供程序对每个请求进行身份验证。我假设我需要创建@Bean 来初始化RestSecurityFilterAbstractAuthenticationProcessingFilter 的 JavaDoc 还说我需要 authenticationManager 属性。我会很感激使用自定义过滤器、提供程序和令牌的工作解决方案。

【问题讨论】:

【参考方案1】:

我对 Spring Boot 不熟悉,但是看到你对我的问题的评论How To Inject AuthenticationManager using Java Configuration in a Custom Filter

在传统的 Spring Security XML 配置中,您可以像这样指定自定义 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>

更多信息http://docs.spring.io/spring-security/site/docs/4.0.1.RELEASE/reference/htmlsingle/#ns-custom-filters

【讨论】:

以上是关于弹簧靴。 HMAC 认证。如何添加自定义 AuthenticationProvider 和 Authentication 过滤器?的主要内容,如果未能解决你的问题,请参考以下文章

如何在项目中集成弹簧靴

弹簧靴 |在类路径资源中定义名称为“entityManagerFactory”的 bean 创建错误

带sitemesh的弹簧靴

API网关Kong使用指南(四)—— hmac-auth插件配置

观察后如何在弹簧靴中重置仪表指标

在身份验证过滤器中添加自定义声明。在过滤器中获取用户 ID。弹簧靴