package com.estate.expert.common.auth; import com.alibaba.fastjson.JSONObject; import com.auth0.jwt.JWT; import com.auth0.jwt.algorithms.Algorithm; import com.estate.expert.common.exception.TokenExpiredException; import com.auth0.jwt.interfaces.DecodedJWT; import com.estate.expert.common.enums.AppEnv; import com.estate.expert.common.exception.BizException; import com.google.common.base.Strings; import com.google.common.io.BaseEncoding; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.ToString; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.core.annotation.Order; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.HandlerInterceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.lang.annotation.Annotation; import java.security.KeyFactory; import java.security.interfaces.RSAPublicKey; import java.security.spec.X509EncodedKeySpec; import java.util.Objects; /** * 认证拦截器 */ @Slf4j @Order(100) public class AuthInterceptor implements HandlerInterceptor { public final static String AUTH_PAYLOAD_KEY = "_auth_payload_"; public final static String CALLER_PAYLOAD_KEY = "_caller_payload_"; private Algorithm algorithm; private static final String AUTH_HEADER = "Authorization"; private static final String SUPPORT_AUTH = "Bearer"; public static final String ORIGINAL_CALLER_HEADER_NAME = "xg-original-caller"; public AuthInterceptor(String publicKey) throws Exception { Objects.requireNonNull(publicKey); RSAPublicKey rsaPublicKey = (RSAPublicKey) KeyFactory.getInstance("RSA") .generatePublic(new X509EncodedKeySpec(BaseEncoding.base64().decode(publicKey))); this.algorithm = Algorithm.RSA256(rsaPublicKey, null); } /** * 预处理 * @param request 请求 * @param response 响应 * @param handler 处理器 * @return 是否继续处理 */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { log.info("AuthInterceptor::preHandle::Start Recv ReqBody ==> {}", request.getRequestURI()); if (!(handler instanceof HandlerMethod)) { return true; } IgnoreAuth ignoreAuth = getAnnotation((HandlerMethod) handler, IgnoreAuth.class); if (ignoreAuth != null) { return true; } Token token = Token.from(request.getHeader(AUTH_HEADER)); if (!StringUtils.equals(SUPPORT_AUTH, token.getType())) { throw new BizException(AuthResultCode.API_AUTH_NOT_SUPPORTED); } try { DecodedJWT jwt = JWT.require(algorithm).build() .verify(token.getToken()); AuthCaller payload = AuthCaller.builder() .appId(jwt.getClaim("appId").asString()) .appEnv(AppEnv.valueOf(jwt.getClaim("appEnv").asString())) .ext(jwt.getClaim("ext").asString()) .build(); request.setAttribute(AUTH_PAYLOAD_KEY, payload); OriginalCaller caller; String originalCallerHeader = request.getHeader(ORIGINAL_CALLER_HEADER_NAME); if (!Strings.isNullOrEmpty(originalCallerHeader)) { caller = JSONObject.parseObject(originalCallerHeader, OriginalCaller.class); } else { caller = OriginalCaller.fromAuthCaller(payload); } request.setAttribute(CALLER_PAYLOAD_KEY, caller); } catch (TokenExpiredException e) { response.setStatus(TokenExpiredException.STATUS_CODE); throw new BizException(AuthResultCode.API_AUTH_TOKEN_EXPIRED); } catch (Exception e) { log.error("api auth, token is invalid. token = {}", token, e); throw new BizException(AuthResultCode.API_AUTH_TOKEN_INVALID); } return true; } /** * 获取注解 * @param handlerMethod 处理器方法 * @param clazz 注解类 * @return 注解 */ private T getAnnotation(HandlerMethod handlerMethod, Class clazz) { T annotation = handlerMethod.getMethodAnnotation(clazz); if (annotation != null) { return annotation; } annotation = AnnotationUtils.findAnnotation(handlerMethod.getBeanType(), clazz); return annotation; } @Data @Builder @AllArgsConstructor @ToString private static class Token { private static final transient String DELIMITER = StringUtils.SPACE; private String type; private String token; /** * 从字符串中获取Token * @param header 字符串 * @return Token */ public static Token from(String header) { return Token.builder() .type(StringUtils.substringBefore(header, DELIMITER)) .token(StringUtils.substringAfter(header, DELIMITER)) .build(); } } }