blade-tool/blade-core-secure/src/main/java/org/springblade/core/secure/utils/SecureUtil.java

489 lines
13 KiB
Java

/**
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springblade.core.secure.utils;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.SneakyThrows;
import org.springblade.core.launch.constant.TokenConstant;
import org.springblade.core.secure.BladeUser;
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.props.BladeTokenProperties;
import org.springblade.core.secure.provider.IClientDetails;
import org.springblade.core.secure.provider.IClientDetailsService;
import org.springblade.core.tool.constant.RoleConstant;
import org.springblade.core.tool.utils.*;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import java.security.Key;
import java.util.*;
/**
* Secure工具类
*
* @author Chill
*/
public class SecureUtil {
private static final String BLADE_USER_REQUEST_ATTR = "_BLADE_USER_REQUEST_ATTR_";
private final static String HEADER = TokenConstant.HEADER;
private final static String BEARER = TokenConstant.BEARER;
private final static String ACCOUNT = TokenConstant.ACCOUNT;
private final static String USER_ID = TokenConstant.USER_ID;
private final static String ROLE_ID = TokenConstant.ROLE_ID;
private final static String DEPT_ID = TokenConstant.DEPT_ID;
private final static String USER_NAME = TokenConstant.USER_NAME;
private final static String ROLE_NAME = TokenConstant.ROLE_NAME;
private final static String TENANT_ID = TokenConstant.TENANT_ID;
private final static String CLIENT_ID = TokenConstant.CLIENT_ID;
private final static Integer AUTH_LENGTH = TokenConstant.AUTH_LENGTH;
private static IClientDetailsService CLIENT_DETAILS_SERVICE;
private static BladeTokenProperties TOKEN_PROPERTIES;
private static String BASE64_SECURITY;
/**
* 获取客户端服务类
*
* @return clientDetailsService
*/
private static IClientDetailsService getClientDetailsService() {
if (CLIENT_DETAILS_SERVICE == null) {
CLIENT_DETAILS_SERVICE = SpringUtil.getBean(IClientDetailsService.class);
}
return CLIENT_DETAILS_SERVICE;
}
/**
* 获取配置类
*
* @return jwtProperties
*/
private static BladeTokenProperties getTokenProperties() {
if (TOKEN_PROPERTIES == null) {
TOKEN_PROPERTIES = SpringUtil.getBean(BladeTokenProperties.class);
}
return TOKEN_PROPERTIES;
}
/**
* 获取Token签名
*
* @return String
*/
private static String getBase64Security() {
if (BASE64_SECURITY == null) {
BASE64_SECURITY = Base64.getEncoder().encodeToString(getTokenProperties().getSignKey().getBytes(Charsets.UTF_8));
}
return BASE64_SECURITY;
}
/**
* 获取用户信息
*
* @return BladeUser
*/
public static BladeUser getUser() {
HttpServletRequest request = WebUtil.getRequest();
if (request == null) {
return null;
}
// 优先从 request 中获取
Object bladeUser = request.getAttribute(BLADE_USER_REQUEST_ATTR);
if (bladeUser == null) {
bladeUser = getUser(request);
if (bladeUser != null) {
// 设置到 request 中
request.setAttribute(BLADE_USER_REQUEST_ATTR, bladeUser);
}
}
return (BladeUser) bladeUser;
}
/**
* 获取用户信息
*
* @param request request
* @return BladeUser
*/
public static BladeUser getUser(HttpServletRequest request) {
Claims claims = getClaims(request);
if (claims == null) {
return null;
}
String clientId = Func.toStr(claims.get(SecureUtil.CLIENT_ID));
Long userId = Func.toLong(claims.get(SecureUtil.USER_ID));
String tenantId = Func.toStr(claims.get(SecureUtil.TENANT_ID));
String roleId = Func.toStr(claims.get(SecureUtil.ROLE_ID));
String deptId = Func.toStr(claims.get(SecureUtil.DEPT_ID));
String account = Func.toStr(claims.get(SecureUtil.ACCOUNT));
String roleName = Func.toStr(claims.get(SecureUtil.ROLE_NAME));
String userName = Func.toStr(claims.get(SecureUtil.USER_NAME));
BladeUser bladeUser = new BladeUser();
bladeUser.setClientId(clientId);
bladeUser.setUserId(userId);
bladeUser.setTenantId(tenantId);
bladeUser.setAccount(account);
bladeUser.setRoleId(roleId);
bladeUser.setDeptId(deptId);
bladeUser.setRoleName(roleName);
bladeUser.setUserName(userName);
return bladeUser;
}
/**
* 是否为超管
*
* @return boolean
*/
public static boolean isAdministrator() {
return StringUtil.containsAny(getUserRole(), RoleConstant.ADMIN);
}
/**
* 获取用户id
*
* @return userId
*/
public static Long getUserId() {
BladeUser user = getUser();
return (null == user) ? -1 : user.getUserId();
}
/**
* 获取用户id
*
* @param request request
* @return userId
*/
public static Long getUserId(HttpServletRequest request) {
BladeUser user = getUser(request);
return (null == user) ? -1 : user.getUserId();
}
/**
* 获取用户账号
*
* @return userAccount
*/
public static String getUserAccount() {
BladeUser user = getUser();
return (null == user) ? StringPool.EMPTY : user.getAccount();
}
/**
* 获取用户账号
*
* @param request request
* @return userAccount
*/
public static String getUserAccount(HttpServletRequest request) {
BladeUser user = getUser(request);
return (null == user) ? StringPool.EMPTY : user.getAccount();
}
/**
* 获取用户名
*
* @return userName
*/
public static String getUserName() {
BladeUser user = getUser();
return (null == user) ? StringPool.EMPTY : user.getUserName();
}
/**
* 获取用户名
*
* @param request request
* @return userName
*/
public static String getUserName(HttpServletRequest request) {
BladeUser user = getUser(request);
return (null == user) ? StringPool.EMPTY : user.getUserName();
}
/**
* 获取用户角色
*
* @return userName
*/
public static String getUserRole() {
BladeUser user = getUser();
return (null == user) ? StringPool.EMPTY : user.getRoleName();
}
/**
* 获取用角色
*
* @param request request
* @return userName
*/
public static String getUserRole(HttpServletRequest request) {
BladeUser user = getUser(request);
return (null == user) ? StringPool.EMPTY : user.getRoleName();
}
/**
* 获取租户ID
*
* @return tenantId
*/
public static String getTenantId() {
BladeUser user = getUser();
return (null == user) ? StringPool.EMPTY : user.getTenantId();
}
/**
* 获取租户ID
*
* @param request request
* @return tenantId
*/
public static String getTenantId(HttpServletRequest request) {
BladeUser user = getUser(request);
return (null == user) ? StringPool.EMPTY : user.getTenantId();
}
/**
* 获取客户端id
*
* @return tenantId
*/
public static String getClientId() {
BladeUser user = getUser();
return (null == user) ? StringPool.EMPTY : user.getClientId();
}
/**
* 获取客户端id
*
* @param request request
* @return tenantId
*/
public static String getClientId(HttpServletRequest request) {
BladeUser user = getUser(request);
return (null == user) ? StringPool.EMPTY : user.getClientId();
}
/**
* 获取Claims
*
* @param request request
* @return Claims
*/
public static Claims getClaims(HttpServletRequest request) {
String auth = request.getHeader(SecureUtil.HEADER);
if (StringUtil.isNotBlank(auth) && auth.length() > AUTH_LENGTH) {
String headStr = auth.substring(0, 6).toLowerCase();
if (headStr.compareTo(SecureUtil.BEARER) == 0) {
auth = auth.substring(7);
return SecureUtil.parseJWT(auth);
}
} else {
String parameter = request.getParameter(SecureUtil.HEADER);
if (StringUtil.isNotBlank(parameter)) {
return SecureUtil.parseJWT(parameter);
}
}
return null;
}
/**
* 获取请求头
*
* @return header
*/
public static String getHeader() {
return getHeader(Objects.requireNonNull(WebUtil.getRequest()));
}
/**
* 获取请求头
*
* @param request request
* @return header
*/
public static String getHeader(HttpServletRequest request) {
return request.getHeader(HEADER);
}
/**
* 解析jsonWebToken
*
* @param jsonWebToken jsonWebToken
* @return Claims
*/
public static Claims parseJWT(String jsonWebToken) {
try {
return Jwts.parserBuilder()
.setSigningKey(Base64.getDecoder().decode(getBase64Security())).build()
.parseClaimsJws(jsonWebToken).getBody();
} catch (Exception ex) {
return null;
}
}
/**
* 创建令牌
*
* @param user user
* @param audience audience
* @param issuer issuer
* @param tokenType tokenType
* @return jwt
*/
public static TokenInfo createJWT(Map<String, String> user, String audience, String issuer, String tokenType) {
String[] tokens = extractAndDecodeHeader();
assert tokens.length == 2;
String clientId = tokens[0];
String clientSecret = tokens[1];
// 获取客户端信息
IClientDetails clientDetails = clientDetails(clientId);
// 校验客户端信息
if (!validateClient(clientDetails, clientId, clientSecret)) {
throw new SecureException("客户端认证失败!");
}
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
long nowMillis = System.currentTimeMillis();
Date now = new Date(nowMillis);
//生成签名密钥
byte[] apiKeySecretBytes = Base64.getDecoder().decode(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() * 1000;
} else if (tokenType.equals(TokenConstant.REFRESH_TOKEN)) {
expireMillis = clientDetails.getRefreshTokenValidity() * 1000;
} 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 / 1000);
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("No client information in request header");
}
byte[] base64Token = header.substring(6).getBytes(Charsets.UTF_8_NAME);
byte[] decoded;
try {
decoded = Base64.getDecoder().decode(base64Token);
} catch (IllegalArgumentException var7) {
throw new RuntimeException("Failed to decode basic authentication token");
}
String token = new String(decoded, Charsets.UTF_8_NAME);
int index = token.indexOf(StringPool.COLON);
if (index == -1) {
throw new RuntimeException("Invalid basic authentication token");
} 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;
}
}