diff --git a/backend/src/main/java/io/dataease/auth/config/F2CRealm.java b/backend/src/main/java/io/dataease/auth/config/F2CRealm.java index 1f0ee7e60d..c7a46650b6 100644 --- a/backend/src/main/java/io/dataease/auth/config/F2CRealm.java +++ b/backend/src/main/java/io/dataease/auth/config/F2CRealm.java @@ -2,18 +2,17 @@ package io.dataease.auth.config; import io.dataease.auth.api.dto.CurrentRoleDto; import io.dataease.auth.api.dto.CurrentUserDto; +import io.dataease.auth.entity.ASKToken; import io.dataease.auth.entity.JWTToken; import io.dataease.auth.entity.SysUserEntity; import io.dataease.auth.entity.TokenInfo; +import io.dataease.auth.handler.ApiKeyHandler; import io.dataease.auth.service.AuthUserService; import io.dataease.auth.util.JWTUtils; import io.dataease.commons.utils.BeanUtils; import io.dataease.commons.utils.LogUtil; import io.dataease.listener.util.CacheUtils; -import org.apache.shiro.authc.AuthenticationException; -import org.apache.shiro.authc.AuthenticationInfo; -import org.apache.shiro.authc.AuthenticationToken; -import org.apache.shiro.authc.SimpleAuthenticationInfo; +import org.apache.shiro.authc.*; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.authz.SimpleAuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; @@ -37,7 +36,7 @@ public class F2CRealm extends AuthorizingRealm { @Override public boolean supports(AuthenticationToken token) { - return token instanceof JWTToken; + return token instanceof JWTToken || token instanceof ASKToken; } //验证资源权限 @@ -56,12 +55,27 @@ public class F2CRealm extends AuthorizingRealm { @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken auth) throws AuthenticationException { + + if (auth instanceof ASKToken) { + + Object accessKey = auth.getPrincipal(); + Object signature = auth.getCredentials(); + Long userId = ApiKeyHandler.getUser(accessKey.toString(), signature.toString()); + + SysUserEntity userEntity = userWithId(userId); + CurrentUserDto currentUserDto = queryCacheUserDto(userEntity); + return new SimpleAuthenticationInfo(currentUserDto, signature, "f2cReam"); + } + + + try { CacheUtils.get("lic_info", "lic"); }catch (Exception e) { LogUtil.error(e); throw new AuthenticationException("lic error"); } + TokenInfo tokenInfo = null; String token = null; try { @@ -78,13 +92,14 @@ public class F2CRealm extends AuthorizingRealm { throw new AuthenticationException("token invalid"); } // 使用缓存 - SysUserEntity user = authUserService.getUserById(userId); + /*SysUserEntity user = authUserService.getUserById(userId); if (user == null) { throw new AuthenticationException("User didn't existed!"); } if (user.getEnabled()==0) { throw new AuthenticationException("User is valid!"); - } + }*/ + SysUserEntity user = userWithId(userId); String pass = null; try { pass = user.getPassword(); @@ -94,6 +109,29 @@ public class F2CRealm extends AuthorizingRealm { if (! JWTUtils.verify(token, tokenInfo, pass)) { throw new AuthenticationException("Username or password error"); } + /*// 使用缓存 + List currentRoleDtos = authUserService.roleInfos(user.getUserId()); + // 使用缓存 + List permissions = authUserService.permissions(user.getUserId()); + CurrentUserDto currentUserDto = BeanUtils.copyBean(new CurrentUserDto(), user); + currentUserDto.setRoles(currentRoleDtos); + currentUserDto.setPermissions(permissions);*/ + CurrentUserDto currentUserDto = queryCacheUserDto(user); + return new SimpleAuthenticationInfo(currentUserDto, token, "f2cReam"); + } + + public SysUserEntity userWithId(Long userId) { + SysUserEntity user = authUserService.getUserById(userId); + if (user == null) { + throw new AuthenticationException("User didn't existed!"); + } + if (user.getEnabled()==0) { + throw new AuthenticationException("User is valid!"); + } + return user; + } + + public CurrentUserDto queryCacheUserDto(SysUserEntity user) { // 使用缓存 List currentRoleDtos = authUserService.roleInfos(user.getUserId()); // 使用缓存 @@ -101,6 +139,6 @@ public class F2CRealm extends AuthorizingRealm { CurrentUserDto currentUserDto = BeanUtils.copyBean(new CurrentUserDto(), user); currentUserDto.setRoles(currentRoleDtos); currentUserDto.setPermissions(permissions); - return new SimpleAuthenticationInfo(currentUserDto, token, "f2cReam"); + return currentUserDto; } } diff --git a/backend/src/main/java/io/dataease/auth/entity/ASKToken.java b/backend/src/main/java/io/dataease/auth/entity/ASKToken.java new file mode 100644 index 0000000000..4112fe197e --- /dev/null +++ b/backend/src/main/java/io/dataease/auth/entity/ASKToken.java @@ -0,0 +1,25 @@ +package io.dataease.auth.entity; + +import org.apache.shiro.authc.AuthenticationToken; + +public class ASKToken implements AuthenticationToken { + + private String accessKey; + + private String signature; + + public ASKToken(String accessKey, String signature) { + this.accessKey = accessKey; + this.signature = signature; + } + + @Override + public Object getPrincipal() { + return accessKey; + } + + @Override + public Object getCredentials() { + return signature; + } +} diff --git a/backend/src/main/java/io/dataease/auth/filter/JWTFilter.java b/backend/src/main/java/io/dataease/auth/filter/JWTFilter.java index ba23292a7a..3077a338a5 100644 --- a/backend/src/main/java/io/dataease/auth/filter/JWTFilter.java +++ b/backend/src/main/java/io/dataease/auth/filter/JWTFilter.java @@ -1,8 +1,10 @@ package io.dataease.auth.filter; +import io.dataease.auth.entity.ASKToken; import io.dataease.auth.entity.JWTToken; import io.dataease.auth.entity.SysUserEntity; import io.dataease.auth.entity.TokenInfo; +import io.dataease.auth.handler.ApiKeyHandler; import io.dataease.auth.service.AuthUserService; import io.dataease.auth.util.JWTUtils; import io.dataease.commons.utils.CommonBeanFactory; @@ -48,6 +50,18 @@ public class JWTFilter extends BasicHttpAuthenticationFilter { @Override protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception { HttpServletRequest httpServletRequest = (HttpServletRequest) request; + + + if (ApiKeyHandler.isApiKeyCall(httpServletRequest)) { + // Long userId = ApiKeyHandler.getUser(httpServletRequest); + + ASKToken askToken = ApiKeyHandler.buildToken(httpServletRequest); + + // UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(userId.toString(), ApiKeyHandler.random); + getSubject(request, response).login(askToken); + return true; + } + String authorization = httpServletRequest.getHeader("Authorization"); if (StringUtils.startsWith(authorization, "Basic")) { return false; @@ -72,7 +86,10 @@ public class JWTFilter extends BasicHttpAuthenticationFilter { */ @Override protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) { - if (isLoginAttempt(request, response)) { + // 先判断是不是api调用 + HttpServletRequest hRequest = (HttpServletRequest) request; + + if (isLoginAttempt(request, response) || ApiKeyHandler.isApiKeyCall(hRequest)) { try { boolean loginSuccess = executeLogin(request, response); return loginSuccess; diff --git a/backend/src/main/java/io/dataease/auth/handler/ApiKeyHandler.java b/backend/src/main/java/io/dataease/auth/handler/ApiKeyHandler.java new file mode 100644 index 0000000000..bd0a0e35a4 --- /dev/null +++ b/backend/src/main/java/io/dataease/auth/handler/ApiKeyHandler.java @@ -0,0 +1,88 @@ +package io.dataease.auth.handler; + +import io.dataease.auth.entity.ASKToken; +import io.dataease.commons.utils.CodingUtil; +import io.dataease.plugins.config.SpringContextUtil; +import io.dataease.plugins.xpack.ukey.dto.request.XpackUkeyDto; +import io.dataease.plugins.xpack.ukey.service.UkeyXpackService; +import org.apache.commons.lang3.StringUtils; + +import javax.servlet.http.HttpServletRequest; +import java.util.UUID; + +public class ApiKeyHandler { + + public static final String API_ACCESS_KEY = "accessKey"; + + public static final String API_SIGNATURE = "signature"; + + + public static String random = UUID.randomUUID().toString() + UUID.randomUUID().toString(); + + public static Long getUser(HttpServletRequest request) { + if (request == null) { + return null; + } + return getUser(request.getHeader(API_ACCESS_KEY), request.getHeader(API_SIGNATURE)); + } + + public static ASKToken buildToken(HttpServletRequest request) { + if (request == null) { + return null; + } + String accessKey = request.getHeader(API_ACCESS_KEY); + String signature = request.getHeader(API_SIGNATURE); + ASKToken askToken = new ASKToken(accessKey, signature); + return askToken; + } + + public static Boolean isApiKeyCall(HttpServletRequest request) { + if (request == null) { + return false; + } + if (StringUtils.isBlank(request.getHeader(API_ACCESS_KEY)) || StringUtils.isBlank(request.getHeader(API_SIGNATURE))) { + return false; + } + return true; + } + + public static XpackUkeyDto ukey(String accessKey) { + UkeyXpackService ukeyXpackService = SpringContextUtil.getBean(UkeyXpackService.class); + XpackUkeyDto userKey = ukeyXpackService.getUserKey(accessKey); + return userKey; + } + + public static Long getUser(String accessKey, String signature) { + if (StringUtils.isBlank(accessKey) || StringUtils.isBlank(signature)) { + return null; + } + XpackUkeyDto userKey = ukey(accessKey); + if (userKey == null) { + throw new RuntimeException("invalid accessKey"); + } + String signatureDecrypt; + try { + signatureDecrypt = CodingUtil.aesDecrypt(signature, userKey.getSecretKey(), accessKey); + } catch (Throwable t) { + throw new RuntimeException("invalid signature"); + } + String[] signatureArray = StringUtils.split(StringUtils.trimToNull(signatureDecrypt), "|"); + if (signatureArray.length < 2) { + throw new RuntimeException("invalid signature"); + } + if (!StringUtils.equals(accessKey, signatureArray[0])) { + throw new RuntimeException("invalid signature"); + } + long signatureTime = 0l; + try { + signatureTime = Long.valueOf(signatureArray[signatureArray.length - 1]).longValue(); + } catch (Exception e) { + throw new RuntimeException(e); + } + if (Math.abs(System.currentTimeMillis() - signatureTime) > 1800000) { + //签名30分钟超时 + throw new RuntimeException("expired signature"); + } + return userKey.getUserId(); + } +} diff --git a/backend/src/main/java/io/dataease/commons/utils/CodingUtil.java b/backend/src/main/java/io/dataease/commons/utils/CodingUtil.java index d377fafcfd..4bdea0e469 100644 --- a/backend/src/main/java/io/dataease/commons/utils/CodingUtil.java +++ b/backend/src/main/java/io/dataease/commons/utils/CodingUtil.java @@ -7,6 +7,7 @@ import javax.crypto.*; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import java.security.MessageDigest; +import java.util.UUID; /** * 加密解密工具 @@ -153,6 +154,17 @@ public class CodingUtil { } } + /*public static String getSignature(String accessKey, String secretKey) throws Exception { + return aesEncrypt(accessKey + "|" + UUID.randomUUID().toString() + "|" + System.currentTimeMillis(), secretKey, accessKey); + } + + public static void main(String[] args) throws Exception{ + String accessKey = "gnPFmtAsdLhUEWPA"; + String secretKey = "TfK5FGUle0KRfJJJ"; + String signature = getSignature(accessKey, secretKey); + System.out.println(signature); + }*/ + public static String secretKey() { try { KeyGenerator keyGen = KeyGenerator.getInstance("AES"); diff --git a/backend/src/main/java/io/dataease/config/Knife4jConfiguration.java b/backend/src/main/java/io/dataease/config/Knife4jConfiguration.java index c8174f6e31..7a58bb54ee 100644 --- a/backend/src/main/java/io/dataease/config/Knife4jConfiguration.java +++ b/backend/src/main/java/io/dataease/config/Knife4jConfiguration.java @@ -77,7 +77,9 @@ public class Knife4jConfiguration { private Docket defaultApi(String groupName, String packageName) { List securitySchemes=new ArrayList<>(); - securitySchemes.add(apiKey()); + securitySchemes.add(accessKey()); + securitySchemes.add(signature()); + List securityContexts = new ArrayList<>(); securityContexts.add(securityContext()); @@ -100,8 +102,12 @@ public class Knife4jConfiguration { .build(); } - private ApiKey apiKey() { - return new ApiKey("Authorization", "Authorization", "header"); + private ApiKey accessKey() { + return new ApiKey("accessKey", "accessKey", "header"); + } + + private ApiKey signature() { + return new ApiKey("signature", "signature", "header"); } @@ -109,7 +115,12 @@ public class Knife4jConfiguration { AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything"); AuthorizationScope[] authorizationScopes = new AuthorizationScope[1]; authorizationScopes[0] = authorizationScope; - return CollectionUtil.newArrayList(new SecurityReference("Authorization", authorizationScopes)); + + List results = new ArrayList<>(); + results.add(new SecurityReference("accessKey", authorizationScopes)); + results.add(new SecurityReference("signature", authorizationScopes)); + + return results; } } diff --git a/backend/src/main/resources/application.properties b/backend/src/main/resources/application.properties index 14368c84d1..d4f4111a23 100644 --- a/backend/src/main/resources/application.properties +++ b/backend/src/main/resources/application.properties @@ -28,7 +28,7 @@ knife4j.enable=true knife4j.setting.enableFooter=false knife4j.setting.enableFooterCustom=false knife4j.setting.footerCustomContent=fit2cloud 1.0-b9 -knife4j.setting.enableSwaggerModels=false +#knife4j.setting.enableSwaggerModels=false knife4j.setting.enableDocumentManage=false knife4j.setting.enableSearch=false knife4j.setting.enableOpenApi=false