¶Ô±ÈÐÂÎļþ |
| | |
| | | /* |
| | | * Copyright (c) 2018-2028, Chill Zhuang All rights reserved. |
| | | * |
| | | * Redistribution and use in source and binary forms, with or without |
| | | * modification, are permitted provided that the following conditions are met: |
| | | * |
| | | * Redistributions of source code must retain the above copyright notice, |
| | | * this list of conditions and the following disclaimer. |
| | | * Redistributions in binary form must reproduce the above copyright |
| | | * notice, this list of conditions and the following disclaimer in the |
| | | * documentation and/or other materials provided with the distribution. |
| | | * Neither the name of the dreamlu.net developer nor the names of its |
| | | * contributors may be used to endorse or promote products derived from |
| | | * this software without specific prior written permission. |
| | | * Author: Chill åºéª (smallchill@163.com) |
| | | */ |
| | | package org.springblade.core.secure.utils; |
| | | |
| | | import io.jsonwebtoken.JwtBuilder; |
| | | import io.jsonwebtoken.Jwts; |
| | | import io.jsonwebtoken.SignatureAlgorithm; |
| | | import lombok.SneakyThrows; |
| | | import org.springblade.core.jwt.JwtUtil; |
| | | import org.springblade.core.jwt.props.JwtProperties; |
| | | import org.springblade.core.launch.constant.TokenConstant; |
| | | import org.springblade.core.secure.TokenInfo; |
| | | import org.springblade.core.secure.constant.SecureConstant; |
| | | import org.springblade.core.secure.exception.SecureException; |
| | | import org.springblade.core.secure.provider.IClientDetails; |
| | | import org.springblade.core.secure.provider.IClientDetailsService; |
| | | import org.springblade.core.tool.utils.*; |
| | | |
| | | import javax.crypto.spec.SecretKeySpec; |
| | | import java.security.Key; |
| | | import java.util.*; |
| | | |
| | | /** |
| | | * Secureå·¥å
·ç±» |
| | | * |
| | | * @author Chill |
| | | */ |
| | | public class SecureUtil extends AuthUtil { |
| | | private final static String CLIENT_ID = TokenConstant.CLIENT_ID; |
| | | |
| | | private static IClientDetailsService clientDetailsService; |
| | | |
| | | private static JwtProperties jwtProperties; |
| | | |
| | | /** |
| | | * è·å客æ·ç«¯æå¡ç±» |
| | | * |
| | | * @return clientDetailsService |
| | | */ |
| | | private static IClientDetailsService getClientDetailsService() { |
| | | if (clientDetailsService == null) { |
| | | clientDetailsService = SpringUtil.getBean(IClientDetailsService.class); |
| | | } |
| | | return clientDetailsService; |
| | | } |
| | | |
| | | /** |
| | | * è·åé
置类 |
| | | * |
| | | * @return jwtProperties |
| | | */ |
| | | private static JwtProperties getJwtProperties() { |
| | | if (jwtProperties == null) { |
| | | jwtProperties = SpringUtil.getBean(JwtProperties.class); |
| | | } |
| | | return jwtProperties; |
| | | } |
| | | |
| | | /** |
| | | * å建令ç |
| | | * |
| | | * @param user user |
| | | * @param audience audience |
| | | * @param issuer issuer |
| | | * @param tokenType tokenType |
| | | * @return jwt |
| | | */ |
| | | public static TokenInfo createJWT(Map<String, Object> user, String audience, String issuer, String tokenType) { |
| | | |
| | | String[] tokens = extractAndDecodeHeader(); |
| | | String clientId = tokens[0]; |
| | | String clientSecret = tokens[1]; |
| | | |
| | | // è·å客æ·ç«¯ä¿¡æ¯ |
| | | IClientDetails clientDetails = clientDetails(clientId); |
| | | |
| | | // æ ¡éªå®¢æ·ç«¯ä¿¡æ¯ |
| | | if (!validateClient(clientDetails, clientId, clientSecret)) { |
| | | throw new SecureException("客æ·ç«¯è®¤è¯å¤±è´¥, è¯·æ£æ¥è¯·æ±å¤´ [Authorization] ä¿¡æ¯"); |
| | | } |
| | | |
| | | SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256; |
| | | |
| | | long nowMillis = System.currentTimeMillis(); |
| | | Date now = new Date(nowMillis); |
| | | |
| | | //çæç¾åå¯é¥ |
| | | byte[] apiKeySecretBytes = Base64.getDecoder().decode(JwtUtil.getBase64Security()); |
| | | Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName()); |
| | | |
| | | //æ·»å ææJWTçç±» |
| | | JwtBuilder builder = Jwts.builder().setHeaderParam("typ", "JWT") |
| | | .setIssuer(issuer) |
| | | .setAudience(audience) |
| | | .signWith(signingKey); |
| | | |
| | | //设置JWTåæ° |
| | | user.forEach(builder::claim); |
| | | |
| | | //设置åºç¨id |
| | | builder.claim(CLIENT_ID, clientId); |
| | | |
| | | //æ·»å Tokenè¿ææ¶é´ |
| | | long expireMillis; |
| | | if (tokenType.equals(TokenConstant.ACCESS_TOKEN)) { |
| | | expireMillis = clientDetails.getAccessTokenValidity() * 1000L; |
| | | } else if (tokenType.equals(TokenConstant.REFRESH_TOKEN)) { |
| | | expireMillis = clientDetails.getRefreshTokenValidity() * 1000L; |
| | | } else { |
| | | expireMillis = getExpire(); |
| | | } |
| | | long expMillis = nowMillis + expireMillis; |
| | | Date exp = new Date(expMillis); |
| | | builder.setExpiration(exp).setNotBefore(now); |
| | | |
| | | //ç»è£
Tokenä¿¡æ¯ |
| | | TokenInfo tokenInfo = new TokenInfo(); |
| | | tokenInfo.setToken(builder.compact()); |
| | | tokenInfo.setExpire((int) (expireMillis / 1000L)); |
| | | |
| | | //Tokenç¶æé
ç½®, ä»
å¨çæAccessTokenæ¶åæ§è¡ |
| | | if (getJwtProperties().getState() && TokenConstant.ACCESS_TOKEN.equals(tokenType)) { |
| | | String tenantId = String.valueOf(user.get(TokenConstant.TENANT_ID)); |
| | | String userId = String.valueOf(user.get(TokenConstant.USER_ID)); |
| | | JwtUtil.addAccessToken(tenantId, userId, tokenInfo.getToken(), tokenInfo.getExpire()); |
| | | } |
| | | //Tokenç¶æé
ç½®, ä»
å¨çæRefreshTokenæ¶åæ§è¡ |
| | | if (getJwtProperties().getState() && getJwtProperties().getSingle() && TokenConstant.REFRESH_TOKEN.equals(tokenType)) { |
| | | String tenantId = String.valueOf(user.get(TokenConstant.TENANT_ID)); |
| | | String userId = String.valueOf(user.get(TokenConstant.USER_ID)); |
| | | JwtUtil.addRefreshToken(tenantId, userId, tokenInfo.getToken(), tokenInfo.getExpire()); |
| | | } |
| | | return tokenInfo; |
| | | } |
| | | |
| | | /** |
| | | * è·åè¿ææ¶é´(次æ¥åæ¨3ç¹) |
| | | * |
| | | * @return expire |
| | | */ |
| | | public static long getExpire() { |
| | | Calendar cal = Calendar.getInstance(); |
| | | cal.add(Calendar.DAY_OF_YEAR, 1); |
| | | cal.set(Calendar.HOUR_OF_DAY, 3); |
| | | cal.set(Calendar.SECOND, 0); |
| | | cal.set(Calendar.MINUTE, 0); |
| | | cal.set(Calendar.MILLISECOND, 0); |
| | | return cal.getTimeInMillis() - System.currentTimeMillis(); |
| | | } |
| | | |
| | | /** |
| | | * 客æ·ç«¯ä¿¡æ¯è§£ç |
| | | */ |
| | | @SneakyThrows |
| | | public static String[] extractAndDecodeHeader() { |
| | | // è·å请æ±å¤´å®¢æ·ç«¯ä¿¡æ¯ |
| | | String header = Objects.requireNonNull(WebUtil.getRequest()).getHeader(SecureConstant.BASIC_HEADER_KEY); |
| | | header = Func.toStr(header).replace(SecureConstant.BASIC_HEADER_PREFIX_EXT, SecureConstant.BASIC_HEADER_PREFIX); |
| | | if (!header.startsWith(SecureConstant.BASIC_HEADER_PREFIX)) { |
| | | throw new SecureException("æªè·åå°è¯·æ±å¤´[Authorization]çä¿¡æ¯"); |
| | | } |
| | | byte[] base64Token = header.substring(6).getBytes(Charsets.UTF_8_NAME); |
| | | |
| | | byte[] decoded; |
| | | try { |
| | | decoded = Base64.getDecoder().decode(base64Token); |
| | | } catch (IllegalArgumentException var7) { |
| | | throw new RuntimeException("客æ·ç«¯ä»¤çè§£æå¤±è´¥"); |
| | | } |
| | | |
| | | String token = new String(decoded, Charsets.UTF_8_NAME); |
| | | int index = token.indexOf(StringPool.COLON); |
| | | if (index == -1) { |
| | | throw new RuntimeException("客æ·ç«¯ä»¤çä¸åæ³"); |
| | | } else { |
| | | return new String[]{token.substring(0, index), token.substring(index + 1)}; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * è·å请æ±å¤´ä¸ç客æ·ç«¯id |
| | | */ |
| | | public static String getClientIdFromHeader() { |
| | | String[] tokens = extractAndDecodeHeader(); |
| | | assert tokens.length == 2; |
| | | return tokens[0]; |
| | | } |
| | | |
| | | /** |
| | | * è·å客æ·ç«¯ä¿¡æ¯ |
| | | * |
| | | * @param clientId 客æ·ç«¯id |
| | | * @return clientDetails |
| | | */ |
| | | private static IClientDetails clientDetails(String clientId) { |
| | | return getClientDetailsService().loadClientByClientId(clientId); |
| | | } |
| | | |
| | | /** |
| | | * æ ¡éªClient |
| | | * |
| | | * @param clientId 客æ·ç«¯id |
| | | * @param clientSecret 客æ·ç«¯å¯é¥ |
| | | * @return boolean |
| | | */ |
| | | private static boolean validateClient(IClientDetails clientDetails, String clientId, String clientSecret) { |
| | | if (clientDetails != null) { |
| | | return StringUtil.equals(clientId, clientDetails.getClientId()) && StringUtil.equals(clientSecret, clientDetails.getClientSecret()); |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | } |