build: shiro api key config
This commit is contained in:
parent
d6c4645c06
commit
ce5fcf76a3
|
@ -1,6 +1,7 @@
|
|||
package io.metersphere.sdk.config;
|
||||
|
||||
|
||||
import io.metersphere.sdk.security.ApiKeyFilter;
|
||||
import io.metersphere.sdk.security.CsrfFilter;
|
||||
import io.metersphere.sdk.security.MsPermissionAnnotationMethodInterceptor;
|
||||
import io.metersphere.sdk.security.realm.LocalRealm;
|
||||
|
@ -39,17 +40,15 @@ public class ShiroConfig {
|
|||
shiroFilterFactoryBean.setUnauthorizedUrl("/403");
|
||||
shiroFilterFactoryBean.setSuccessUrl("/");
|
||||
|
||||
// shiroFilterFactoryBean.getFilters().put("apikey", new ApiKeyFilter());
|
||||
shiroFilterFactoryBean.getFilters().put("apikey", new ApiKeyFilter());
|
||||
shiroFilterFactoryBean.getFilters().put("csrf", new CsrfFilter());
|
||||
Map<String, String> filterChainDefinitionMap = shiroFilterFactoryBean.getFilterChainDefinitionMap();
|
||||
|
||||
filterChainDefinitionMap.putAll(FilterChainUtils.loadBaseFilterChain());
|
||||
// todo ignore csrf
|
||||
// filterChainDefinitionMap.putAll(FilterChainUtils.ignoreCsrfFilter());
|
||||
|
||||
filterChainDefinitionMap.put("/**", "csrf, authc");
|
||||
// todo
|
||||
// filterChainDefinitionMap.put("/**", "apikey, csrf, authc");
|
||||
filterChainDefinitionMap.putAll(FilterChainUtils.ignoreCsrfFilter());
|
||||
|
||||
filterChainDefinitionMap.put("/**", "apikey, csrf, authc");
|
||||
return shiroFilterFactoryBean;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
package io.metersphere.sdk.constants;
|
||||
|
||||
public enum ApiKeyConstants {
|
||||
ACTIVE, DISABLED
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
package io.metersphere.sdk.security;
|
||||
|
||||
|
||||
import io.metersphere.sdk.constants.SessionConstants;
|
||||
import jakarta.servlet.ServletRequest;
|
||||
import jakarta.servlet.ServletResponse;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.shiro.SecurityUtils;
|
||||
import org.apache.shiro.authc.UsernamePasswordToken;
|
||||
import org.apache.shiro.web.filter.authc.AnonymousFilter;
|
||||
import org.apache.shiro.web.util.WebUtils;
|
||||
|
||||
public class ApiKeyFilter extends AnonymousFilter {
|
||||
|
||||
@Override
|
||||
protected boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) {
|
||||
HttpServletRequest httpRequest = WebUtils.toHttp(request);
|
||||
// 不是apikey的通过
|
||||
if (!ApiKeyHandler.isApiKeyCall(httpRequest) && !SecurityUtils.getSubject().isAuthenticated()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// apikey 验证
|
||||
if (!SecurityUtils.getSubject().isAuthenticated()) {
|
||||
String userId = ApiKeyHandler.getUser(WebUtils.toHttp(request));
|
||||
if (StringUtils.isNotBlank(userId)) {
|
||||
SecurityUtils.getSubject().login(new UsernamePasswordToken(userId, "no_pass"));
|
||||
}
|
||||
}
|
||||
|
||||
if (!SecurityUtils.getSubject().isAuthenticated()) {
|
||||
((HttpServletResponse) response).setHeader(SessionConstants.AUTHENTICATION_STATUS, "invalid");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void postHandle(ServletRequest request, ServletResponse response) throws Exception {
|
||||
// apikey 退出
|
||||
if (ApiKeyHandler.isApiKeyCall(WebUtils.toHttp(request)) && SecurityUtils.getSubject().isAuthenticated()) {
|
||||
SecurityUtils.getSubject().logout();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
package io.metersphere.sdk.security;
|
||||
|
||||
import io.metersphere.sdk.service.UserKeyService;
|
||||
import io.metersphere.sdk.util.CodingUtil;
|
||||
import io.metersphere.sdk.util.CommonBeanFactory;
|
||||
import io.metersphere.system.domain.UserKey;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
public class ApiKeyHandler {
|
||||
|
||||
public static final String API_ACCESS_KEY = "accessKey";
|
||||
|
||||
public static final String API_SIGNATURE = "signature";
|
||||
|
||||
public static String getUser(HttpServletRequest request) {
|
||||
if (request == null) {
|
||||
return null;
|
||||
}
|
||||
return getUser(request.getHeader(API_ACCESS_KEY), request.getHeader(API_SIGNATURE));
|
||||
}
|
||||
|
||||
public static Boolean isApiKeyCall(HttpServletRequest request) {
|
||||
if (request == null) {
|
||||
return false;
|
||||
}
|
||||
return !StringUtils.isBlank(request.getHeader(API_ACCESS_KEY)) && !StringUtils.isBlank(request.getHeader(API_SIGNATURE));
|
||||
}
|
||||
|
||||
public static String getUser(String accessKey, String signature) {
|
||||
if (StringUtils.isBlank(accessKey) || StringUtils.isBlank(signature)) {
|
||||
return null;
|
||||
}
|
||||
UserKey userKey = CommonBeanFactory.getBean(UserKeyService.class).getUserKey(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;
|
||||
try {
|
||||
signatureTime = Long.parseLong(signatureArray[signatureArray.length - 1]);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
if (Math.abs(System.currentTimeMillis() - signatureTime) > 1800000) {
|
||||
//签名30分钟超时
|
||||
throw new RuntimeException("expired signature");
|
||||
}
|
||||
return userKey.getCreateUser();
|
||||
}
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
package io.metersphere.sdk.security;
|
||||
|
||||
import io.metersphere.sdk.constants.SessionConstants;
|
||||
import io.metersphere.sdk.dto.SessionUser;
|
||||
import io.metersphere.sdk.util.CodingUtil;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class SSOSessionHandler {
|
||||
|
||||
public static String random = UUID.randomUUID() + UUID.randomUUID().toString();
|
||||
|
||||
public static String validate(HttpServletRequest request) {
|
||||
try {
|
||||
String v = request.getHeader(SessionConstants.CSRF_TOKEN);
|
||||
if (StringUtils.isNotBlank(v)) {
|
||||
return validate(v);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// LogUtil.error("failed to validate", e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static String validate(String csrfToken) {
|
||||
csrfToken = CodingUtil.aesDecrypt(csrfToken, SessionUser.secret, SessionUser.iv);
|
||||
String[] signatureArray = StringUtils.split(StringUtils.trimToNull(csrfToken), "|");
|
||||
if (signatureArray.length != 4) {
|
||||
throw new RuntimeException("invalid token");
|
||||
}
|
||||
return signatureArray[0];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
package io.metersphere.sdk.service;
|
||||
|
||||
import io.metersphere.sdk.constants.ApiKeyConstants;
|
||||
import io.metersphere.sdk.exception.MSException;
|
||||
import io.metersphere.sdk.util.Translator;
|
||||
import io.metersphere.system.domain.UserKey;
|
||||
import io.metersphere.system.domain.UserKeyExample;
|
||||
import io.metersphere.system.mapper.UserKeyMapper;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.apache.commons.lang3.RandomStringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@Service
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public class UserKeyService {
|
||||
|
||||
@Resource
|
||||
private UserKeyMapper userKeyMapper;
|
||||
|
||||
@Resource
|
||||
private BaseUserService baseUserService;
|
||||
|
||||
public List<UserKey> getUserKeysInfo(String userId) {
|
||||
UserKeyExample userKeysExample = new UserKeyExample();
|
||||
userKeysExample.createCriteria().andCreateUserEqualTo(userId);
|
||||
userKeysExample.setOrderByClause("create_time");
|
||||
return userKeyMapper.selectByExample(userKeysExample);
|
||||
}
|
||||
|
||||
public UserKey generateUserKey(String userId) {
|
||||
if (baseUserService.getUserDTO(userId) == null) {
|
||||
MSException.throwException(Translator.get("user_not_exist") + userId);
|
||||
}
|
||||
UserKeyExample userKeysExample = new UserKeyExample();
|
||||
userKeysExample.createCriteria().andCreateUserEqualTo(userId);
|
||||
List<UserKey> userKeysList = userKeyMapper.selectByExample(userKeysExample);
|
||||
|
||||
if (!CollectionUtils.isEmpty(userKeysList) && userKeysList.size() >= 5) {
|
||||
MSException.throwException(Translator.get("user_apikey_limit"));
|
||||
}
|
||||
|
||||
UserKey userKeys = new UserKey();
|
||||
userKeys.setId(UUID.randomUUID().toString());
|
||||
userKeys.setCreateUser(userId);
|
||||
userKeys.setStatus(ApiKeyConstants.ACTIVE.name());
|
||||
userKeys.setAccessKey(RandomStringUtils.randomAlphanumeric(16));
|
||||
userKeys.setSecretKey(RandomStringUtils.randomAlphanumeric(16));
|
||||
userKeys.setCreateTime(System.currentTimeMillis());
|
||||
userKeyMapper.insert(userKeys);
|
||||
return userKeyMapper.selectByPrimaryKey(userKeys.getId());
|
||||
}
|
||||
|
||||
public void deleteUserKey(String id) {
|
||||
userKeyMapper.deleteByPrimaryKey(id);
|
||||
}
|
||||
|
||||
public void activeUserKey(String id) {
|
||||
UserKey userKeys = new UserKey();
|
||||
userKeys.setId(id);
|
||||
userKeys.setStatus(ApiKeyConstants.ACTIVE.name());
|
||||
userKeyMapper.updateByPrimaryKeySelective(userKeys);
|
||||
}
|
||||
|
||||
public void disableUserKey(String id) {
|
||||
UserKey userKeys = new UserKey();
|
||||
userKeys.setId(id);
|
||||
userKeys.setStatus(ApiKeyConstants.DISABLED.name());
|
||||
userKeyMapper.updateByPrimaryKeySelective(userKeys);
|
||||
}
|
||||
|
||||
public UserKey getUserKey(String accessKey) {
|
||||
UserKeyExample userKeyExample = new UserKeyExample();
|
||||
userKeyExample.createCriteria().andAccessKeyEqualTo(accessKey).andStatusEqualTo(ApiKeyConstants.ACTIVE.name());
|
||||
List<UserKey> userKeysList = userKeyMapper.selectByExample(userKeyExample);
|
||||
if (!CollectionUtils.isEmpty(userKeysList)) {
|
||||
return userKeysList.get(0);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue