001package top.cenze.utils;
002
003import cn.hutool.core.collection.CollectionUtil;
004import cn.hutool.core.util.ObjectUtil;
005import cn.hutool.core.util.StrUtil;
006import lombok.SneakyThrows;
007import lombok.extern.slf4j.Slf4j;
008import org.apache.commons.lang3.StringUtils;
009import org.apache.http.auth.AuthenticationException;
010import top.cenze.utils.crypt.HMacUtil;
011
012import javax.servlet.http.HttpServletRequest;
013import java.util.LinkedHashMap;
014import java.util.List;
015import java.util.regex.Matcher;
016import java.util.regex.Pattern;
017
018/**
019 * @desc: Token工具
020 * @author: chengze
021 * @createByDate: 2023/12/13 11:33
022 */
023@Slf4j
024public class TokenUtil {
025
026    /**
027     * 解析BearerToken(HttpServletRequest)
028     * bearer token是一种认证方式,是一种安全令牌,拥有 bearer token 的任何一方(被称为 "bearer"),可以以任何方式,和同样持有它的任何一方一样地使用它来访问受 OAuth 2.0保护的资源。
029     * 在java中,一般把bearer token放到请求头的Authorization中,我们可以通过正则来判断token的合法性,并返回头中的有效token
030     * https://blog.csdn.net/Gefangenes/article/details/130778194
031     * https://www.python100.com/html/90I8A2I7EMG0.html
032     * https://blog.csdn.net/weixin_42399342/article/details/112157011
033     * @param request
034     * @return
035     */
036    public static String resolveBearerTokenFromRequest(HttpServletRequest request) throws AuthenticationException {
037        String authorization = request.getHeader("Authorization");
038
039        return resolveBearerTokenFromAuthorization(authorization);
040    }
041
042    /**
043     * 解析BearerToken(Authorization)
044     * @param authorization
045     * @return
046     * @throws AuthenticationException
047     */
048    public static String resolveBearerTokenFromAuthorization(String authorization) throws AuthenticationException {
049        log.info("resolveBearerTokenFromAuthorization authorization: {}", authorization);
050        Pattern authorizationPattern = Pattern.compile("^Bearer (?<token>[a-zA-Z0-9-:._~+/]+=*)$", Pattern.CASE_INSENSITIVE);//<token>的值就是真实的表达式配置的值
051
052        if (!StringUtils.startsWithIgnoreCase(authorization, "bearer")) {
053            return null;
054        }
055
056        Matcher matcher = authorizationPattern.matcher(authorization);
057        if (!matcher.matches()) {
058            throw new AuthenticationException("Bearer token is malformed");
059        }
060
061        // 从上面的正则表达式中获取token
062        return matcher.group("token");
063    }
064
065    /**
066     * 生成HS256 JWT Token
067     * @return
068     */
069    public static String createHS256JWTToken(String appId, String appSecret) {
070        return createHS256JWTToken(appId, appSecret, null);
071    }
072
073    /**
074     * 生成HS256 JWT Token
075     * https://blog.csdn.net/qq_51705526/article/details/123939826?utm_medium=distribute.pc_relevant.none-task-blog-2~default~baidujs_baidulandingword~default-8-123939826-blog-122104594.235^v39^pc_relevant_3m_sort_dl_base1&spm=1001.2101.3001.4242.5&utm_relevant_index=11
076     * https://www.jb51.net/article/259817.htm
077     * @param appId
078     * @param appSecret
079     * @return
080     */
081    @SneakyThrows
082    public static String createHS256JWTToken(String appId, String appSecret, Long iat) {
083        log.info("createHS256JWTToken appId: {}, appSecret: {}, iat: {}", appId, appSecret, iat);
084        LinkedHashMap<String, Object> header = new LinkedHashMap<>();
085        header.put("alg", "HS256");
086        header.put("typ", "JWT");
087
088        if (ObjectUtil.isNull(iat)) {
089            iat = System.currentTimeMillis() / 1000;
090        }
091        LinkedHashMap<String, Object> payload = new LinkedHashMap<>();
092        payload.put("iat", iat);
093        payload.put("sub", appId);
094
095        String data = Base64Util.urlEncode(header) + "." + Base64Util.urlEncode(payload);
096        log.info("createHS256JWTToken data: {}", data);
097//        String data = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJjYWlwaSIsImlhdCI6MTUxNjIzOTAyMn0";
098
099//        Mac hmac = Mac.getInstance(HmacAlgorithm.HmacSHA256.getValue());
100//        SecretKeySpec secretKey = new SecretKeySpec(appSecret.getBytes("UTF-8"), HmacAlgorithm.HmacSHA256.getValue());
101//        hmac.init(secretKey);
102//        byte[] bytes = hmac.doFinal(data.getBytes("UTF-8"));
103//        return Base64.encodeUrlSafe(bytes);
104        return data + "." + HMacUtil.SHA256(data, appSecret);
105    }
106
107    /**
108     * 转BearerToken
109     * @param token
110     * @return
111     */
112    public static String toBearerToken(String token) {
113        return "Bearer " + token;
114    }
115
116    /**
117     * 验证token(鉴权)
118     * @param token
119     * @param appSecret
120     * @return
121     */
122    public static boolean validBearerToken(String token, String appSecret) {
123        log.info("validBearerToken token: {}, appSecret: {}", token, appSecret);
124        if (StrUtil.isEmpty(token) || StrUtil.isEmpty(appSecret)) {
125            return false;
126        }
127
128        token = token.replaceAll("Bearer ", "");
129        if (StrUtil.isEmpty(token)) {
130            return false;
131        }
132
133        List<String> split = StrUtil.split(token, ".");
134        if (CollectionUtil.isEmpty(split) || split.size() < 3) {
135            return false;
136        }
137
138        String sign = HMacUtil.SHA256(split.get(0) + "." + split.get(1), appSecret);
139        return split.get(2).equals(sign);
140    }
141}