【发布时间】:2021-07-30 02:12:21
【问题描述】:
我有一个问题:我正在尝试基于两个 HEADER 实现身份验证,一个 HMAC-SHA256 使用商家的共享密钥和一个商家 ID 签名。 我想要的所有以 /api/payment/** 开头的路径都以这种方式检查。
当我在 POST /fcabpo/api/payment/hmac-validation 中调用我的服务时 似乎验证正确,因为调用了扩展 AbstractAuthenticationProcessingFilter 的我的类的方法successAuthentication gas bene,但似乎调用被转发到GET到上下文根/fcabpo,所以我收到了404。
我编写了扩展 AuthenticationProvider 的类 HMACAuthenticationProvider
@Component
@Slf4j
public class HMACAuthenticationProvider implements AuthenticationProvider {
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
HMACAuthenticationToken hmacAuthenticationToken = (HMACAuthenticationToken)authentication;
String merchandId = hmacAuthenticationToken.getPrincipalMerchantId();
HMACSha256Credential hmacSha256Credential = hmacAuthenticationToken.getHmacSha256Credential();
// TODO check the hmac
List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
grantedAuthorities.add(new SimpleGrantedAuthority("MERCHANT"));
Authentication auth = new UsernamePasswordAuthenticationToken(merchandId, hmacSha256Credential.getHmacSha256(), grantedAuthorities);
return auth;
}
@Override
public boolean supports(Class<?> authentication) {
return HMACAuthenticationToken.class.equals(authentication);
}
}
我还写了一个扩展 AbstractAuthenticationProcessingFilter 的 HMACRestSecurityFilter
@Slf4j
public class HMACRestSecurityFilter extends AbstractAuthenticationProcessingFilter {
public static final String HEADER_KEY_MERCHANT_ID = "merchantId";
public static final String HMAC_SHA256 = "HMAC-SHA256";
public static final String EPOCH_TIME = "EPOCH-TIME";
public HMACRestSecurityFilter(RequestMatcher requiresAuthenticationRequestMatcher) {
super(requiresAuthenticationRequestMatcher);
}
public HMACRestSecurityFilter(String defaultFilterProcessesUrl) {
super(defaultFilterProcessesUrl);
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
MultiReadHttpServletRequest multiReadHttpServletRequest = ServletUtil.wrapRequest(request);
String merchantId = multiReadHttpServletRequest.getHeader(HEADER_KEY_MERCHANT_ID);
String hmacSha256 = multiReadHttpServletRequest.getHeader(HMAC_SHA256);
String epochTimeStr = multiReadHttpServletRequest.getHeader(EPOCH_TIME);
if (StringUtils.isBlank(merchantId) || StringUtils.isBlank(hmacSha256) || StringUtils.isBlank(epochTimeStr)) {
unsuccessfulAuthentication(multiReadHttpServletRequest, response, new BadHMACAuthRequestException("Authentication attempt failed! Request missing mandatory headers."));
}
long epochTime = 0;
try {
epochTime = Long.parseLong(epochTimeStr);
} catch (NumberFormatException e) {
unsuccessfulAuthentication(multiReadHttpServletRequest, response, new BadHMACAuthRequestException("Authentication attempt failed! Request missing mandatory headers."));
}
HMACSha256Credential hmacSha256Credential = new HMACSha256Credential();
byte[] content = multiReadHttpServletRequest.getInputStreamCopy();
String contentString = new String(content, multiReadHttpServletRequest.getCharacterEncoding());
hmacSha256Credential.setBody(contentString);
hmacSha256Credential.setEpochTime(epochTime);
hmacSha256Credential.setHmacSha256(hmacSha256);
hmacSha256Credential.setHttpMethod(multiReadHttpServletRequest.getMethod());
hmacSha256Credential.setUri(multiReadHttpServletRequest.getRequestURI());
HMACAuthenticationToken hmacAuthenticationToken = new HMACAuthenticationToken(merchantId, hmacSha256Credential);
return hmacAuthenticationToken;
}
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest)req;
HttpServletResponse response = (HttpServletResponse)res;
MultiReadHttpServletRequest multiReadHttpServletRequest = ServletUtil.wrapRequest(request);
super.doFilter(multiReadHttpServletRequest, response, chain);
}
@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
super.successfulAuthentication(request, response, chain, authResult);
}
@Override
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
super.unsuccessfulAuthentication(request, response, failed);
}
}
和扩展 AbstractAuthenticationToken 的 HMACAuthenticationToken 以及扩展 WebSecurityConfigurerAdapter 以配置路径 e 过滤器的类
@Configuration
public class ActuatorSecurity extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic().and().authorizeRequests().antMatchers("/api/payment/**").hasAnyAuthority("MERCHANT").and().addFilterBefore(new HMACRestSecurityFilter("/api/payment/**"),
UsernamePasswordAuthenticationFilter.class);
http.authorizeRequests().anyRequest().permitAll().and().csrf().disable();
}
}
我不知道这可能是什么问题,也找不到任何有用的东西可以在网上解决。
【问题讨论】:
-
请启用spring security调试日志,然后发布你的完整日志、启动日志和来自请求的日志,这样我们就可以看到启动时加载了什么,以及它如何记录你的传入请求。
标签: java spring spring-boot authentication spring-security