001/**
002 * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com).
003 * <p>
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 * <p>
008 * http://www.apache.org/licenses/LICENSE-2.0
009 * <p>
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package io.jboot.support.jwt;
017
018import com.jfinal.core.Controller;
019import com.jfinal.kit.JsonKit;
020import com.jfinal.kit.LogKit;
021import com.jfinal.log.Log;
022import io.jboot.Jboot;
023import io.jboot.exception.JbootIllegalConfigException;
024import io.jboot.utils.StrUtil;
025import io.jsonwebtoken.*;
026
027import javax.crypto.SecretKey;
028import javax.crypto.spec.SecretKeySpec;
029import javax.xml.bind.DatatypeConverter;
030import java.util.Date;
031import java.util.HashMap;
032import java.util.Map;
033
034/**
035 * @author Michael Yang 杨福海 (fuhai999@gmail.com)
036 * @version V1.0
037 */
038public class JwtManager {
039
040    private static final JwtManager me = new JwtManager();
041    private static final Log LOG = Log.getLog(JwtManager.class);
042    public static final Map EMPTY_MAP = new HashMap();
043    private static JwtConfig config = Jboot.config(JwtConfig.class);
044
045
046    public static JwtManager me() {
047        return me;
048    }
049
050    public String getHttpHeaderName() {
051        return config.getHttpHeaderName();
052    }
053
054    public String getHttpParameterKey() {
055        return config.getHttpParameterKey();
056    }
057
058    /**
059     * 通过 Controller 解析 Map
060     *
061     * @param controller 控制器
062     * @return 所有 JWT 数据
063     */
064    public Map parseJwtToken(Controller controller) {
065
066        if (!config.isConfigOk()) {
067            LogKit.debug("Jwt secret not config well, please config jboot.web.jwt.secret in jboot.properties.");
068            return EMPTY_MAP;
069        }
070
071        String token = controller.getHeader(getHttpHeaderName());
072
073        if (StrUtil.isBlank(token) && StrUtil.isNotBlank(getHttpParameterKey())) {
074            token = controller.get(getHttpParameterKey());
075        }
076
077        return StrUtil.isBlank(token) ? EMPTY_MAP : parseJwtToken(token);
078    }
079
080
081    /**
082     * 解析 JWT Token 内容
083     *
084     * @param token 加密的 token
085     * @return 返回 JWT 的 MAP 数据
086     */
087    public Map parseJwtToken(String token) {
088        SecretKey secretKey = createSecretKey();
089        try {
090            Claims claims = Jwts.parser()
091                    .setSigningKey(secretKey)
092                    .parseClaimsJws(token).getBody();
093
094            String jsonString = claims.getSubject();
095            if (StrUtil.isNotBlank(jsonString)) {
096                return JsonKit.parse(jsonString, HashMap.class);
097            }
098        } catch (SignatureException | MalformedJwtException ex) {
099            // don't trust the JWT!
100            // jwt 签名错误或解析错误,可能是伪造的,不能相信
101            LOG.error("Do not trast the jwt. " + ex.getMessage());
102        } catch (ExpiredJwtException ex) {
103            // jwt 已经过期
104            LOG.error("Jwt is expired. " + ex.getMessage());
105        } catch (Exception ex) {
106            //其他错误
107            LOG.error("Jwt parseJwtToken error. " + ex.getMessage());
108        }
109
110        return EMPTY_MAP;
111    }
112
113
114    public String createJwtToken(Map map) {
115
116        if (!config.isConfigOk()) {
117            throw new JbootIllegalConfigException("Can not create jwt, please config jboot.web.jwt.secret in jboot.properties.");
118        }
119
120        SecretKey secretKey = createSecretKey();
121
122        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
123        long nowMillis = System.currentTimeMillis();
124        Date now = new Date(nowMillis);
125
126        //追加保存 JWT 的生成时间
127        map.put(JwtInterceptor.ISUUED_AT, nowMillis);
128        String subject = JsonKit.toJson(map);
129
130        JwtBuilder builder = Jwts.builder()
131                .setIssuedAt(now)
132                .setSubject(subject)
133                .signWith(signatureAlgorithm, secretKey);
134
135        if (config.getValidityPeriod() > 0) {
136            long expMillis = nowMillis + config.getValidityPeriod();
137            builder.setExpiration(new Date(expMillis));
138        }
139
140        return builder.compact();
141    }
142
143
144    private SecretKey createSecretKey() {
145        byte[] encodedKey = DatatypeConverter.parseBase64Binary(config.getSecret());
146        return new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
147    }
148
149    public static JwtConfig getConfig() {
150        return config;
151    }
152
153    public static void setConfig(JwtConfig config) {
154        JwtManager.config = config;
155    }
156}