Compare commits
4 Commits
29a87bf1c4
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 2869994387 | |||
| 287b124f7e | |||
| ae4599cd9a | |||
| 10598a51cf |
@@ -8,7 +8,7 @@ import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 注解在方法上以获取对 EmoneyProtocol 的额外操作
|
||||
* 注解在方法上以获取对 Emoney Protocol 的额外操作
|
||||
* <p>
|
||||
* 目前方法必须是受 spring 管理类下的公共成员方法,且接受参数为单参数 JsonNode.
|
||||
* <p>
|
||||
@@ -21,18 +21,15 @@ import java.lang.annotation.Target;
|
||||
@Target(METHOD)
|
||||
public @interface ResponseDecodeExtension {
|
||||
/**
|
||||
* 指定的 protocolId
|
||||
* @return
|
||||
* @return 指定的 protocolId
|
||||
*/
|
||||
String protocolId();
|
||||
/**
|
||||
* inspect 的排序
|
||||
* @return
|
||||
* @return inspect 的排序
|
||||
*/
|
||||
int order() default -1;
|
||||
/**
|
||||
* 是否在模拟客户端请求和返回时启用, 默认 true
|
||||
* @return
|
||||
* @return 是否在模拟客户端请求和返回时启用, 默认 true
|
||||
*/
|
||||
boolean client() default true;
|
||||
}
|
||||
|
||||
@@ -8,10 +8,13 @@ import org.springframework.security.authentication.ProviderManager;
|
||||
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
||||
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer;
|
||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import quant.rich.emoney.service.AuthService;
|
||||
@@ -27,10 +30,10 @@ public class SecurityConfig {
|
||||
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.headers(headers -> headers
|
||||
.cacheControl(cache -> cache.disable())
|
||||
.frameOptions(f -> f.sameOrigin())
|
||||
.cacheControl(HeadersConfigurer.CacheControlConfig::disable)
|
||||
.frameOptions(HeadersConfigurer.FrameOptionsConfig::sameOrigin)
|
||||
)
|
||||
.csrf(csrf -> csrf.disable())
|
||||
.csrf(AbstractHttpConfigurer::disable)
|
||||
.authorizeHttpRequests(auth -> auth
|
||||
.requestMatchers("/favicon.ico").permitAll()
|
||||
.requestMatchers("/admin/*/login").permitAll()
|
||||
@@ -41,11 +44,8 @@ public class SecurityConfig {
|
||||
.requestMatchers("/img/**").permitAll()
|
||||
.anyRequest().authenticated()
|
||||
)
|
||||
.formLogin(form -> form // 开启表单登录,并指定登录页
|
||||
.loginPage("/admin/v1/login") // 指定登录页
|
||||
.loginProcessingUrl("/admin/v1/doLogin") // 处理登录请求的 URL
|
||||
.defaultSuccessUrl("/admin/v1/", false) // 登录成功后默认跳转
|
||||
.permitAll())
|
||||
.exceptionHandling(ex -> ex
|
||||
.authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/admin/v1/login")))
|
||||
.logout(logout -> logout
|
||||
.logoutUrl("/admin/v1/logout")
|
||||
.logoutSuccessUrl("/admin/v1/login")
|
||||
@@ -54,7 +54,6 @@ public class SecurityConfig {
|
||||
.sessionManagement(session -> session
|
||||
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
|
||||
);
|
||||
;
|
||||
|
||||
return http.build();
|
||||
}
|
||||
|
||||
@@ -50,19 +50,27 @@ public class ProxySettingControllerV1 extends UpdateBoolServiceController<ProxyS
|
||||
@PostMapping("/save")
|
||||
@ResponseBody
|
||||
public R<?> save(@RequestBody ProxySetting proxySetting) {
|
||||
return super.save(proxySetting);
|
||||
R<?> result = super.save(proxySetting);
|
||||
proxySettingService.clearIpInfoCache();
|
||||
return result;
|
||||
}
|
||||
|
||||
@PostMapping("/delete")
|
||||
@ResponseBody
|
||||
public R<?> delete(String id) {
|
||||
return super.delete(id);
|
||||
R<?> result = super.delete(id);
|
||||
proxySettingService.clearIpInfoCache();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected
|
||||
R<?> updateBool(String id, String field, Boolean value) {
|
||||
return super.updateBool(id, field, value);
|
||||
R<?> result = super.updateBool(id, field, value);
|
||||
if ("isDefault".equals(field)) {
|
||||
proxySettingService.clearIpInfoCache();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@PostMapping("/batchOp")
|
||||
@@ -80,8 +88,10 @@ public class ProxySettingControllerV1 extends UpdateBoolServiceController<ProxyS
|
||||
throw RException.badRequest("操作类型不能为空");
|
||||
}
|
||||
else if (ProxySettingBatchOp.DELETE == op) {
|
||||
return R.judge(
|
||||
R<?> result = R.judge(
|
||||
proxySettingService.removeBatchByIds(idArray));
|
||||
proxySettingService.clearIpInfoCache();
|
||||
return result;
|
||||
}
|
||||
|
||||
LambdaUpdateWrapper<ProxySetting> uw = new LambdaUpdateWrapper<>();
|
||||
|
||||
@@ -13,7 +13,6 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
@@ -53,7 +52,7 @@ public class RequestInfoControllerV1 extends UpdateBoolServiceController<Request
|
||||
|
||||
@PostMapping("/save")
|
||||
@ResponseBody
|
||||
public R<?> save(@RequestBody @NonNull RequestInfo requestInfo) {
|
||||
public R<?> save(@RequestBody @Valid @NonNull RequestInfo requestInfo) {
|
||||
return super.save(requestInfo);
|
||||
}
|
||||
|
||||
@@ -81,18 +80,11 @@ public class RequestInfoControllerV1 extends UpdateBoolServiceController<Request
|
||||
return R.judge(getThisService().removeByIds(idArray));
|
||||
}
|
||||
|
||||
LambdaUpdateWrapper<RequestInfo> uw = new LambdaUpdateWrapper<>();
|
||||
uw.in(RequestInfo::getId, idArray);
|
||||
switch (op) {
|
||||
default:
|
||||
throw RException.badRequest("未知操作");
|
||||
}
|
||||
}
|
||||
|
||||
private static enum RequestInfoBatchOp {
|
||||
DELETE,
|
||||
ENABLE_ANONYMOUS,
|
||||
DISABLE_ANONYMOUS
|
||||
DELETE
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,6 +16,13 @@ import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.Accessors;
|
||||
import quant.rich.emoney.validator.ProxySettingValid;
|
||||
|
||||
/**
|
||||
* 平台代理配置。
|
||||
* <p>
|
||||
* SQLite 表 proxy_setting 使用触发器维护默认代理的一致性:新增或更新 is_default = 1
|
||||
* 时会把其他记录置为非默认;不允许把最后一条默认代理改为非默认,也不允许删除默认代理。
|
||||
* 这部分约束不在 Java 代码里重复实现,调整表结构或导入数据时需要同步检查触发器。
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@NoArgsConstructor
|
||||
|
||||
@@ -4,7 +4,6 @@ import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.baomidou.mybatisplus.extension.activerecord.Model;
|
||||
@@ -12,10 +11,8 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Setter;
|
||||
import lombok.experimental.Accessors;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import quant.rich.emoney.entity.config.AndroidSdkLevelConfig;
|
||||
@@ -28,11 +25,21 @@ import quant.rich.emoney.util.TextUtils;
|
||||
import quant.rich.emoney.validator.RequestInfoValid;
|
||||
|
||||
/**
|
||||
* 用于配置请求时的请求行为,一般而言,请求头与安卓系统的信息有关(build.prop)
|
||||
* 虽然部分请求对应服务器可能不进行审核,但合理的请求头能尽可能模仿真机行为,避免风险
|
||||
* <b>请求信息</b>
|
||||
* <p>
|
||||
* 用于配置请求时的参数,包括请求头等。若使用该请求头登录成功,则将会存储鉴权信息。
|
||||
*
|
||||
* 一般而言,请求头与系统的信息有关(build.prop)
|
||||
* 虽然服务器可能不进行请求头审核,但合理的请求头能尽可能模仿真机行为,避免风险。
|
||||
* <p>
|
||||
* SQLite 表 request_info 使用触发器维护默认请求配置的一致性:新增或更新 is_default = 1
|
||||
* 时会把其他记录置为非默认;不允许把最后一条默认请求配置改为非默认,也不允许删除默认请求配置。
|
||||
* 这部分约束不在 Java 代码里重复实现,调整表结构或导入数据时需要同步检查触发器。
|
||||
*
|
||||
* @see DeviceInfoConfig
|
||||
* @see AndroidSdkLevelConfig
|
||||
* @see ChromeVersionsConfig
|
||||
* @See RequestInfoValidator
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper=false)
|
||||
@@ -109,9 +116,10 @@ public class RequestInfo extends Model<RequestInfo> {
|
||||
* @see DeviceInfoConfig
|
||||
* @see DeviceInfo
|
||||
*/
|
||||
@Setter(AccessLevel.PRIVATE)
|
||||
@TableField(exist=false)
|
||||
private String androidVersion;
|
||||
public String getAndroidVersion() {
|
||||
DeviceInfo deviceInfo = getDeviceInfo();
|
||||
return deviceInfo == null ? null : deviceInfo.getVersionRelease();
|
||||
}
|
||||
|
||||
/**
|
||||
* <b>用于:</b><ul>
|
||||
@@ -123,9 +131,13 @@ public class RequestInfo extends Model<RequestInfo> {
|
||||
* @see DeviceInfo
|
||||
* @see AndroidSdkLevelConfig
|
||||
*/
|
||||
@Setter(AccessLevel.PRIVATE)
|
||||
@TableField(exist=false)
|
||||
private String androidSdkLevel;
|
||||
public String getAndroidSdkLevel() {
|
||||
String androidVersion = getAndroidVersion();
|
||||
if (StringUtils.isBlank(androidVersion)) {
|
||||
return null;
|
||||
}
|
||||
return String.valueOf(androidSdkLevelConfig.getSdkLevel(androidVersion));
|
||||
}
|
||||
|
||||
/**
|
||||
* <b>用于:</b><ul>
|
||||
@@ -179,8 +191,10 @@ public class RequestInfo extends Model<RequestInfo> {
|
||||
* @see DeviceInfoConfig
|
||||
*
|
||||
*/
|
||||
@TableField(exist=false)
|
||||
private String buildId;
|
||||
public String getBuildId() {
|
||||
DeviceInfo deviceInfo = getDeviceInfo();
|
||||
return deviceInfo == null ? null : deviceInfo.getBuildId();
|
||||
}
|
||||
|
||||
/**
|
||||
* <b>用于:</b><ul>
|
||||
@@ -214,21 +228,29 @@ public class RequestInfo extends Model<RequestInfo> {
|
||||
/**
|
||||
* 从 deviceInfo 设置相关字段
|
||||
* @param deviceInfo
|
||||
* @return
|
||||
* @return 更新过的 RequestInfo 本身
|
||||
*/
|
||||
public RequestInfo setRelativeFieldsFromDeviceInfo(DeviceInfo deviceInfo) {
|
||||
if (deviceInfo == null) {
|
||||
throw new NullPointerException("deviceInfo is null");
|
||||
}
|
||||
deviceName = deviceInfo.getModel();
|
||||
androidVersion = deviceInfo.getVersionRelease();
|
||||
androidSdkLevel = String.valueOf(androidSdkLevelConfig.getSdkLevel(androidVersion));
|
||||
softwareType = deviceInfo.getDeviceType();
|
||||
fingerprint = deviceInfo.getFingerprint();
|
||||
buildId = deviceInfo.getBuildId();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 获取由相应字段指定的 DeviceInfo
|
||||
*/
|
||||
@JsonIgnore
|
||||
private DeviceInfo getDeviceInfo() {
|
||||
if (ObjectUtils.anyNull(deviceName, fingerprint, softwareType)) {
|
||||
return null;
|
||||
}
|
||||
return DeviceInfo.from(deviceName, fingerprint, softwareType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置密码:<ul>
|
||||
* <li>null or empty,保存空字符串</li>
|
||||
@@ -260,13 +282,7 @@ public class RequestInfo extends Model<RequestInfo> {
|
||||
* @see RequestInfo#setRelativeFieldsFromDeviceInfo
|
||||
*/
|
||||
public RequestInfo setFingerprint(String fingerprint) {
|
||||
if (ObjectUtils.allNotNull(deviceName, softwareType, fingerprint, androidSdkLevelConfig)) {
|
||||
DeviceInfo deviceInfo = DeviceInfo.from(deviceName, fingerprint, softwareType);
|
||||
setRelativeFieldsFromDeviceInfo(deviceInfo);
|
||||
}
|
||||
else {
|
||||
this.fingerprint = fingerprint;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,9 @@ import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* 益盟策略和策略池
|
||||
* <p>
|
||||
* SQLite 表 strategy_and_pool 对 pool_id 设置了 UNIQUE_POOL_ID 唯一约束,并使用
|
||||
* ON CONFLICT REPLACE。重复写入同一 pool_id 时会由 SQLite 替换旧记录。
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain=true)
|
||||
|
||||
@@ -41,4 +41,8 @@ public class ProxySettingService extends SqliteServiceImpl<ProxySettingMapper, P
|
||||
return ipInfo;
|
||||
}
|
||||
|
||||
public void clearIpInfoCache() {
|
||||
ipInfo = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ public class RequestInfoService extends SqliteServiceImpl<RequestInfoMapper, Req
|
||||
);
|
||||
if (requestInfo == null) {
|
||||
requestInfo = new RequestInfo();
|
||||
requestInfo.setIsDefault(true);
|
||||
save(requestInfo);
|
||||
}
|
||||
return requestInfo;
|
||||
|
||||
@@ -18,7 +18,6 @@ import java.net.InetAddress;
|
||||
import java.net.Proxy;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
|
||||
@Slf4j
|
||||
public class GeoIPUtil {
|
||||
@@ -35,7 +34,6 @@ public class GeoIPUtil {
|
||||
}
|
||||
}
|
||||
|
||||
@Async
|
||||
public static IpInfo getIpInfoThroughProxy(ProxySetting proxySetting) {
|
||||
if (proxySetting == null) {
|
||||
throw new RuntimeException("代理为空");
|
||||
@@ -66,7 +64,7 @@ public class GeoIPUtil {
|
||||
String responseBody = response.body().string();
|
||||
log.debug("Response ipv4 from proxy: {}", responseBody.trim());
|
||||
ipInfo.setIp(responseBody);
|
||||
} catch (IOException e) {
|
||||
} catch (Exception e) {
|
||||
log.warn("Proxy ipv4 error: {}", e.getMessage());
|
||||
return IpInfo.EMPTY;
|
||||
}
|
||||
@@ -81,7 +79,7 @@ public class GeoIPUtil {
|
||||
String responseBody = response.body().string();
|
||||
log.debug("Response ipv6 from proxy: {}", responseBody.trim());
|
||||
ipInfo.setIpv6(responseBody);
|
||||
} catch (IOException e) {
|
||||
} catch (Exception e) {
|
||||
log.warn("Proxy ipv6 error {}", e.getMessage());
|
||||
}
|
||||
return queryIpInfoGeoLite(ipInfo);
|
||||
@@ -104,7 +102,7 @@ public class GeoIPUtil {
|
||||
|
||||
if (response.getSubdivisions() != null && !response.getSubdivisions().isEmpty()) {
|
||||
// 省
|
||||
Subdivision subdivision = response.getSubdivisions().get(0);
|
||||
Subdivision subdivision = response.getSubdivisions().getFirst();
|
||||
if (subdivision != null) {
|
||||
String subdivisionStr = subdivision.getNames().get(LOCALE);
|
||||
ipInfo.setSubdivision(subdivisionStr);
|
||||
|
||||
@@ -14,7 +14,7 @@ import jakarta.validation.Payload;
|
||||
@Target({ ElementType.TYPE })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface RequestInfoValid {
|
||||
String message() default "非法的 EmoneyRequestConfig";
|
||||
String message() default "非法的 RequestInfo";
|
||||
Class<?>[] groups() default {};
|
||||
Class<? extends Payload>[] payload() default {};
|
||||
}
|
||||
|
||||
@@ -10,6 +10,9 @@ import quant.rich.emoney.entity.config.DeviceInfoConfig;
|
||||
import quant.rich.emoney.entity.sqlite.RequestInfo;
|
||||
import quant.rich.emoney.interfaces.IValidator;
|
||||
|
||||
/**
|
||||
* RequestInfo 校验器
|
||||
*/
|
||||
public class RequestInfoValidator implements IValidator, ConstraintValidator<RequestInfoValid, RequestInfo> {
|
||||
|
||||
static final Pattern androidIdPattern = Pattern.compile("^[0-9a-f]{16}$");
|
||||
|
||||
@@ -158,7 +158,7 @@
|
||||
}
|
||||
else {
|
||||
$(non).children('label').children('span').remove();
|
||||
$(non).find('lay-verify').removeAttr('lay-verify');
|
||||
$(non).find('[lay-verify]').removeAttr('lay-verify');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -58,9 +58,7 @@
|
||||
Helper.renderDropdown({
|
||||
elem: '.operdown',
|
||||
data: [
|
||||
{title: '删除', op: 'DELETE'},
|
||||
{title: '开启匿名', op: 'ENABLE_ANONYMOUS'},
|
||||
{title: '关闭匿名', op: 'DISABLE_ANONYMOUS'}],
|
||||
{title: '删除', op: 'DELETE'}],
|
||||
tableFilter: 'requestInfos',
|
||||
url: '/admin/v1/manage/requestInfo/batchOp',
|
||||
idName: 'id'
|
||||
|
||||
Reference in New Issue
Block a user