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}