liujie
2026-01-21 2c72fce83175c05c9bdcb43814bc5e08d7fed047
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
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 extends Annotation> T getAnnotation(HandlerMethod handlerMethod, Class<T> 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();
        }
    }
}