requiredType) {
+ assertContextInjected();
+ return applicationContext.getBean(requiredType);
+ }
+
+ /**
+ * 检查ApplicationContext不为空.
+ */
+ private static void assertContextInjected() {
+ if (applicationContext == null) {
+ throw new IllegalStateException("applicaitonContext属性未注入, 请在applicationContext" +
+ ".xml中定义SpringContextHolder或在SpringBoot启动类中注册SpringContextHolder.");
+ }
+ }
+
+ /**
+ * 清除SpringContextHolder中的ApplicationContext为Null.
+ */
+ private static void clearHolder() {
+ log.debug("清除SpringContextHolder中的ApplicationContext:"
+ + applicationContext);
+ applicationContext = null;
+ }
+
+ @Override
+ public void destroy(){
+ SpringContextHolder.clearHolder();
+ }
+
+ @Override
+ public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+ if (SpringContextHolder.applicationContext != null) {
+ log.warn("SpringContextHolder中的ApplicationContext被覆盖, 原有ApplicationContext为:" + SpringContextHolder.applicationContext);
+ }
+ SpringContextHolder.applicationContext = applicationContext;
+ }
+
+ /**
+ * 发布事件
+ *
+ * @param event
+ */
+ public static void publishEvent(ApplicationEvent event) {
+ if (applicationContext == null) {
+ return;
+ }
+ applicationContext.publishEvent(event);
+ }
+}
diff --git a/src/main/java/com/sztzjy/forex/trading_trading/config/exception/UnAuthorizedException.java b/src/main/java/com/sztzjy/forex/trading_trading/config/exception/UnAuthorizedException.java
new file mode 100644
index 0000000..d1479d6
--- /dev/null
+++ b/src/main/java/com/sztzjy/forex/trading_trading/config/exception/UnAuthorizedException.java
@@ -0,0 +1,41 @@
+package com.sztzjy.forex.trading_trading.config.exception;
+
+/**
+ * 当用户访问受保护资源但不提供任何凭据时将抛出此异常
+ *
+ * @author 陈沅
+ * @version 1.0.0
+ */
+public class UnAuthorizedException extends RuntimeException {
+
+ /**
+ * Constructs a new runtime exception with the specified detail message.
+ * The cause is not initialized, and may subsequently be initialized by a
+ * call to {@link #initCause}.
+ *
+ * @param message the detail message. The detail message is saved for
+ * later retrieval by the {@link #getMessage()} method.
+ */
+ public UnAuthorizedException(String message) {
+ super(message);
+ }
+
+ /**
+ * Constructs a new runtime exception with the specified detail message and
+ * cause. Note that the detail message associated with
+ * {@code cause} is not automatically incorporated in
+ * this runtime exception's detail message.
+ *
+ * @param message the detail message (which is saved for later retrieval
+ * by the {@link #getMessage()} method).
+ * @param cause the cause (which is saved for later retrieval by the
+ * {@link #getCause()} method). (A {@code null} value is
+ * permitted, and indicates that the cause is nonexistent or
+ * unknown.)
+ * @since 1.4
+ */
+ public UnAuthorizedException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+}
diff --git a/src/main/java/com/sztzjy/forex/trading_trading/config/exception/handler/CustomAccessDeniedHandler.java b/src/main/java/com/sztzjy/forex/trading_trading/config/exception/handler/CustomAccessDeniedHandler.java
new file mode 100644
index 0000000..6226f21
--- /dev/null
+++ b/src/main/java/com/sztzjy/forex/trading_trading/config/exception/handler/CustomAccessDeniedHandler.java
@@ -0,0 +1,32 @@
+package com.sztzjy.forex.trading_trading.config.exception.handler;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.security.access.AccessDeniedException;
+import org.springframework.security.web.access.AccessDeniedHandler;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+/**
+ * 权限不足异常拦截
+ * 当用户在没有授权的情况下访问受保护的REST资源时,将调用此方法发送403 Forbidden响应
+ *
+ * @author 陈沅
+ */
+@Component
+public class CustomAccessDeniedHandler implements AccessDeniedHandler {
+
+ @Override
+ public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException {
+ response.setCharacterEncoding("UTF-8");
+ response.setStatus(HttpStatus.FORBIDDEN.value());
+ response.addHeader("Access-Control-Allow-Origin", "*");
+ response.addHeader("Content-Type", "application/json");
+ PrintWriter printWriter = response.getWriter();
+ printWriter.write("{\"code\":" + HttpStatus.FORBIDDEN.value() + ", \"msg\": \"权限不足\"}");
+ printWriter.flush();
+ }
+}
diff --git a/src/main/java/com/sztzjy/forex/trading_trading/config/exception/handler/CustomAuthenticationEntryPoint.java b/src/main/java/com/sztzjy/forex/trading_trading/config/exception/handler/CustomAuthenticationEntryPoint.java
new file mode 100644
index 0000000..22c5c9f
--- /dev/null
+++ b/src/main/java/com/sztzjy/forex/trading_trading/config/exception/handler/CustomAuthenticationEntryPoint.java
@@ -0,0 +1,39 @@
+package com.sztzjy.forex.trading_trading.config.exception.handler;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.oauth2.common.exceptions.InvalidTokenException;
+import org.springframework.security.web.AuthenticationEntryPoint;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+/**
+ * token异常拦截
+ * 当用户尝试访问安全的REST资源而不提供任何凭据时,将调用此方法发送401 响应
+ *
+ * @author 陈沅
+ */
+@Component
+public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
+
+ @Override
+ public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException {
+ // 当用户尝试访问安全的REST资源而不提供任何凭据时,将调用此方法发送401 响应
+ response.setCharacterEncoding("UTF-8");
+ response.setStatus(HttpStatus.UNAUTHORIZED.value());
+ response.addHeader("Access-Control-Allow-Origin", "*");
+ response.addHeader("Content-Type", "application/json");
+ Throwable cause = authException.getCause();
+ PrintWriter printWriter = response.getWriter();
+ if (cause instanceof InvalidTokenException) {
+ printWriter.write("{\"code\":" + HttpStatus.UNAUTHORIZED.value() + ",\"msg\":\"身份认证失败,无效或过期token\"}");
+ } else {
+ printWriter.write("{\"code\":" + HttpStatus.UNAUTHORIZED.value() + ",\"msg\":\"身份认证失败\"}");
+ }
+ printWriter.flush();
+ }
+}
diff --git a/src/main/java/com/sztzjy/forex/trading_trading/config/exception/handler/GlobalExceptionHandler.java b/src/main/java/com/sztzjy/forex/trading_trading/config/exception/handler/GlobalExceptionHandler.java
new file mode 100644
index 0000000..e7799bc
--- /dev/null
+++ b/src/main/java/com/sztzjy/forex/trading_trading/config/exception/handler/GlobalExceptionHandler.java
@@ -0,0 +1,79 @@
+package com.sztzjy.forex.trading_trading.config.exception.handler;
+
+import com.sztzjy.forex.trading_trading.config.exception.UnAuthorizedException;
+import com.sztzjy.forex.trading_trading.util.ResultEntity;
+import com.sztzjy.forex.trading_trading.util.ThrowableUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.HttpStatus;
+import org.springframework.security.access.AccessDeniedException;
+import org.springframework.security.authentication.BadCredentialsException;
+import org.springframework.validation.FieldError;
+import org.springframework.validation.ObjectError;
+import org.springframework.web.bind.MethodArgumentNotValidException;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+
+/**
+ * 全局异常处理
+ */
+@Slf4j
+@RestControllerAdvice
+public class GlobalExceptionHandler {
+
+ @ExceptionHandler(IllegalArgumentException.class)
+ public ResultEntity illegalArgumentException(IllegalArgumentException e) {
+ log.error(ThrowableUtil.getStackTrace(e));
+ return new ResultEntity<>(HttpStatus.BAD_REQUEST, e.getMessage());
+ }
+
+ @ExceptionHandler(AccessDeniedException.class)
+ public ResultEntity accessDeniedException(AccessDeniedException e) {
+ log.error(ThrowableUtil.getStackTrace(e));
+ return new ResultEntity<>(HttpStatus.FORBIDDEN, e.getMessage());
+ }
+
+ @ExceptionHandler(UnAuthorizedException.class)
+ public ResultEntity UnAuthorizedException(UnAuthorizedException e) {
+ log.error(ThrowableUtil.getStackTrace(e));
+ return new ResultEntity<>(HttpStatus.UNAUTHORIZED, e.getMessage());
+ }
+
+ /**
+ * 处理所有接口数据验证异常
+ */
+ @ExceptionHandler(MethodArgumentNotValidException.class)
+ public ResultEntity handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
+ log.error(ThrowableUtil.getStackTrace(e));
+ ObjectError objectError = e.getBindingResult().getAllErrors().get(0);
+ String message = objectError.getDefaultMessage();
+ if (objectError instanceof FieldError) {
+ message = ((FieldError) objectError).getField() + ": " + message;
+ }
+ return new ResultEntity<>(HttpStatus.BAD_REQUEST, message);
+ }
+
+ /**
+ * 处理所有不可知的异常
+ *
+ * @param e 未知异常
+ * @return /
+ */
+ @ExceptionHandler(Throwable.class)
+ public ResultEntity nullPointerException(Throwable e) {
+ log.error(ThrowableUtil.getStackTrace(e));
+ return new ResultEntity<>(HttpStatus.BAD_REQUEST, e.getMessage());
+ }
+
+ /**
+ * BadCredentialsException
+ */
+ @ExceptionHandler(BadCredentialsException.class)
+ public ResultEntity badCredentialsException(BadCredentialsException e) {
+ // 打印堆栈信息
+ String message = "坏的凭证".equals(e.getMessage()) ? "用户名或密码不正确" : e.getMessage();
+ log.error(message);
+ return new ResultEntity<>(HttpStatus.BAD_REQUEST, message);
+ }
+
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/sztzjy/forex/trading_trading/config/security/AuthenticationFilter.java b/src/main/java/com/sztzjy/forex/trading_trading/config/security/AuthenticationFilter.java
new file mode 100644
index 0000000..25e3773
--- /dev/null
+++ b/src/main/java/com/sztzjy/forex/trading_trading/config/security/AuthenticationFilter.java
@@ -0,0 +1,91 @@
+package com.sztzjy.forex.trading_trading.config.security;
+
+import cn.hutool.extra.servlet.ServletUtil;
+import com.sztzjy.forex.trading_trading.config.Constant;
+import com.sztzjy.forex.trading_trading.config.exception.UnAuthorizedException;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.util.AntPathMatcher;
+import org.springframework.util.PathMatcher;
+import org.springframework.util.StringUtils;
+import org.springframework.web.filter.OncePerRequestFilter;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.time.LocalDateTime;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * 系统登陆认证拦截
+ *
+ * @author 陈沅
+ */
+public class AuthenticationFilter extends OncePerRequestFilter {
+ private final PathMatcher matcher = new AntPathMatcher();
+ private final TokenProvider tokenProvider;
+ public AuthenticationFilter(TokenProvider tokenProvider) {
+ this.tokenProvider = tokenProvider;
+ }
+
+ @Override
+ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
+ String requestRri = request.getRequestURI();
+ if (requestRri.equals("/")) {
+ response.sendRedirect("/doc.html");
+ return;
+ }
+ if (!isExclude(request)) {
+ System.out.println("--------------------------------->>> " + LocalDateTime.now() + " ---ip:" + ServletUtil.getClientIP(request) + " requestUri:" + requestRri);
+ }
+ String token = request.getHeader(Constant.AUTHORIZATION);
+
+ if (token == null || token.equals("undefined") || token.equals("") || token.equals("null")) {
+ filterChain.doFilter(request, response);
+ return;
+ }
+
+ if (!StringUtils.startsWithIgnoreCase(token, "Bearer ")) {
+ throw new UnAuthorizedException("令牌错误: 缺失Bearer..");
+ }
+ JwtUser currentUser = tokenProvider.getClaims(token);
+ Authentication authentication = new UsernamePasswordAuthenticationToken(currentUser, "****", currentUser.getAuthorities());
+ request.getUserPrincipal();
+ SecurityContextHolder.getContext().setAuthentication(authentication);
+ filterChain.doFilter(request, response);
+ }
+
+
+ /**
+ * 排除的请求路径匹配
+ */
+ private final static List patterns = Arrays.asList(
+ "/swagger-resources/**",
+ "/doc.html",
+ "/webjars/**",
+ "/v2/**",
+ "/favicon.ico",
+ "/login",
+ "/druid/**"
+ );
+
+ /**
+ * 判断请求路径是否是在排除之外
+ *
+ * @param request /
+ * @return true-是排除的
+ */
+ private boolean isExclude(HttpServletRequest request) {
+ String requestURI = request.getRequestURI();
+ for (String pattern : patterns) {
+ if (matcher.match(pattern, requestURI)) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/sztzjy/forex/trading_trading/config/security/JwtUser.java b/src/main/java/com/sztzjy/forex/trading_trading/config/security/JwtUser.java
new file mode 100644
index 0000000..e39b532
--- /dev/null
+++ b/src/main/java/com/sztzjy/forex/trading_trading/config/security/JwtUser.java
@@ -0,0 +1,48 @@
+package com.sztzjy.forex.trading_trading.config.security;
+
+import lombok.Getter;
+import lombok.Setter;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.userdetails.UserDetails;
+
+import java.util.Collection;
+
+/**
+ * jwt用户对象
+ */
+@Getter
+@Setter
+public class JwtUser implements UserDetails {
+ private String username;
+ private String password;
+ private String name;
+ private String userId;
+ private int roleId;
+ private int schoolId;
+ private int classId;
+
+ @Override
+ public Collection extends GrantedAuthority> getAuthorities() {
+ return null;
+ }
+
+ @Override
+ public boolean isAccountNonExpired() {
+ return false;
+ }
+
+ @Override
+ public boolean isAccountNonLocked() {
+ return false;
+ }
+
+ @Override
+ public boolean isCredentialsNonExpired() {
+ return false;
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return false;
+ }
+}
diff --git a/src/main/java/com/sztzjy/forex/trading_trading/config/security/TokenProvider.java b/src/main/java/com/sztzjy/forex/trading_trading/config/security/TokenProvider.java
new file mode 100644
index 0000000..2538577
--- /dev/null
+++ b/src/main/java/com/sztzjy/forex/trading_trading/config/security/TokenProvider.java
@@ -0,0 +1,20 @@
+package com.sztzjy.forex.trading_trading.config.security;
+
+import org.springframework.stereotype.Component;
+
+@Component
+public class TokenProvider {
+
+ /**
+ * 解析jwtToken
+ *
+ * @param jwtToken jwtToken
+ * @return jwt解析对象
+ */
+
+ public JwtUser getClaims(String jwtToken) {
+ //todo 解析用户token
+ return null;
+ }
+
+}
diff --git a/src/main/java/com/sztzjy/forex/trading_trading/config/security/WebConfigurerAdapter.java b/src/main/java/com/sztzjy/forex/trading_trading/config/security/WebConfigurerAdapter.java
new file mode 100644
index 0000000..fa53b0b
--- /dev/null
+++ b/src/main/java/com/sztzjy/forex/trading_trading/config/security/WebConfigurerAdapter.java
@@ -0,0 +1,62 @@
+package com.sztzjy.forex.trading_trading.config.security;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.support.ReloadableResourceBundleMessageSource;
+import org.springframework.http.converter.HttpMessageConverter;
+import org.springframework.http.converter.StringHttpMessageConverter;
+import org.springframework.web.cors.CorsConfiguration;
+import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
+import org.springframework.web.filter.CorsFilter;
+import org.springframework.web.servlet.config.annotation.EnableWebMvc;
+import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+import javax.annotation.Resource;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+
+/**
+ * 系统配置
+ */
+@Configuration
+@EnableWebMvc
+public class WebConfigurerAdapter implements WebMvcConfigurer {
+ @Resource
+ private StringHttpMessageConverter stringHttpMessageConverter;
+
+
+ @Bean
+ public CorsFilter corsFilter() {
+ UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
+ CorsConfiguration config = new CorsConfiguration();
+ config.setAllowCredentials(true);
+ config.addAllowedOriginPattern("*");
+ config.addAllowedMethod("*");
+ config.addAllowedHeader("*");
+ source.registerCorsConfiguration("/**", config);
+ return new CorsFilter(source);
+ }
+
+ @Bean
+ public ReloadableResourceBundleMessageSource messageSource() {
+ ReloadableResourceBundleMessageSource reloadableResourceBundleMessageSource = new ReloadableResourceBundleMessageSource();
+ reloadableResourceBundleMessageSource.setBasename("classpath:messages");
+ return reloadableResourceBundleMessageSource;
+ }
+
+ @Override
+ public void addResourceHandlers(ResourceHandlerRegistry registry) {
+ registry.addResourceHandler("/**").addResourceLocations("classpath:/META-INF/resources/").setCachePeriod(0);
+ }
+
+ @Override
+ public void extendMessageConverters(List> converters) {
+ for (int i = 0; i < converters.size(); i++) {
+ if (converters.get(i) instanceof StringHttpMessageConverter) {
+ stringHttpMessageConverter.setDefaultCharset(StandardCharsets.UTF_8);
+ converters.set(i, stringHttpMessageConverter);
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/sztzjy/forex/trading_trading/config/security/WebSecurityConfig.java b/src/main/java/com/sztzjy/forex/trading_trading/config/security/WebSecurityConfig.java
new file mode 100644
index 0000000..f3c64ca
--- /dev/null
+++ b/src/main/java/com/sztzjy/forex/trading_trading/config/security/WebSecurityConfig.java
@@ -0,0 +1,112 @@
+package com.sztzjy.forex.trading_trading.config.security;
+
+import com.sztzjy.forex.trading_trading.annotation.AnonymousAccess;
+import com.sztzjy.forex.trading_trading.config.SpringContextHolder;
+import com.sztzjy.forex.trading_trading.config.exception.handler.CustomAccessDeniedHandler;
+import com.sztzjy.forex.trading_trading.config.exception.handler.CustomAuthenticationEntryPoint;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
+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.configuration.WebSecurityConfigurerAdapter;
+import org.springframework.security.config.http.SessionCreationPolicy;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
+import org.springframework.web.method.HandlerMethod;
+import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
+import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
+
+import javax.annotation.Resource;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * secret配置
+ */
+@Configuration
+@EnableWebSecurity
+@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
+public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
+
+ @Resource
+ private TokenProvider tokenProvider;
+ @Resource
+ private CustomAccessDeniedHandler customAccessDeniedHandler;
+ @Resource
+ private CustomAuthenticationEntryPoint customAuthenticationEntryPoint;
+
+
+ @Bean
+ public PasswordEncoder passwordEncoder() {
+ return new BCryptPasswordEncoder();
+ }
+
+
+ @Override
+ protected void configure(HttpSecurity http) throws Exception {
+ RequestMappingHandlerMapping handlerMapping = SpringContextHolder.getBean("requestMappingHandlerMapping");
+ Map handlerMethods = handlerMapping.getHandlerMethods();
+ Set anonymousUrls = discoveryAnonymousUrls(handlerMethods);
+ http
+ .addFilterBefore(new AuthenticationFilter(tokenProvider), UsernamePasswordAuthenticationFilter.class)
+ // 禁用 CSRF
+ .csrf().disable()
+ .exceptionHandling()
+ .authenticationEntryPoint(customAuthenticationEntryPoint)
+ .accessDeniedHandler(customAccessDeniedHandler)
+ // 防止iframe 造成跨域
+ .and().headers().frameOptions().disable()
+ // 不创建会话
+ .and()
+ .sessionManagement()
+ .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
+ .and()
+ // 所有请求都需要认证
+ .authorizeRequests()
+ .antMatchers("/swagger-resources/**").permitAll()
+ .antMatchers("/doc.html").permitAll()
+ .antMatchers("/webjars/**").permitAll()
+ .antMatchers("/v2/**").permitAll()
+ .antMatchers("/test/**").permitAll()
+ .antMatchers(anonymousUrls.toArray(new String[]{})).permitAll()
+ .anyRequest().authenticated();
+ }
+
+ /**
+ * 认证相关的核心接口
+ *
+ * @return {@link AuthenticationManager}
+ * @throws Exception /
+ */
+ @Bean
+ public AuthenticationManager authenticationManager() throws Exception {
+ return super.authenticationManager();
+ }
+
+
+
+ /**
+ * 根据注解发现需要匿名访问的请求路径
+ *
+ * @param handlerMethodMap ?
+ * @return 匿名Mapping patterns
+ */
+ private Set discoveryAnonymousUrls(Map handlerMethodMap) {
+ Set patterns = new HashSet<>();
+ for (Map.Entry infoEntry : handlerMethodMap.entrySet()) {
+ HandlerMethod handlerMethod = infoEntry.getValue();
+ AnonymousAccess anonymousAccess = handlerMethod.getMethodAnnotation(AnonymousAccess.class);
+ if (Objects.nonNull(anonymousAccess)) {
+ assert infoEntry.getKey().getPatternsCondition() != null;
+ patterns.addAll(infoEntry.getKey().getPatternsCondition().getPatterns());
+ }
+ }
+ return patterns;
+ }
+
+}
diff --git a/src/main/java/com/sztzjy/forex/trading_trading/config/swagger/Swagger2Config.java b/src/main/java/com/sztzjy/forex/trading_trading/config/swagger/Swagger2Config.java
new file mode 100644
index 0000000..b79d7e5
--- /dev/null
+++ b/src/main/java/com/sztzjy/forex/trading_trading/config/swagger/Swagger2Config.java
@@ -0,0 +1,104 @@
+package com.sztzjy.forex.trading_trading.config.swagger;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.models.auth.In;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.boot.web.context.WebServerInitializedEvent;
+import org.springframework.context.ApplicationListener;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Profile;
+import springfox.documentation.builders.ApiInfoBuilder;
+import springfox.documentation.builders.PathSelectors;
+import springfox.documentation.builders.RequestHandlerSelectors;
+import springfox.documentation.service.*;
+import springfox.documentation.spi.DocumentationType;
+import springfox.documentation.spi.service.contexts.SecurityContext;
+import springfox.documentation.spring.web.plugins.Docket;
+import springfox.documentation.swagger2.annotations.EnableSwagger2;
+
+import javax.annotation.Resource;
+import java.net.Inet4Address;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * swagger 3.0接口文档配置类
+ *
+ * @author 陈沅
+ */
+@EnableSwagger2
+@Configuration
+@Profile({"dev", "pro", "test"})
+public class Swagger2Config implements ApplicationListener {
+ @Resource
+ private SwaggerProperties swaggerProperties;
+
+ @Bean
+ public Docket docket() {
+ return new Docket(DocumentationType.SWAGGER_2)
+ .enable(swaggerProperties.getEnable())
+ .apiInfo(apiInfo())
+ .select()
+ .apis(RequestHandlerSelectors.withClassAnnotation(Api.class))
+ .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
+ .paths(PathSelectors.any())
+// .globalRequestParameters(requestParameters())
+ .build()
+ .securitySchemes(securitySchemes())
+ .securityContexts(securityContexts());
+ }
+
+ private ApiInfo apiInfo() {
+ return new ApiInfoBuilder().title(swaggerProperties.getTitle())
+ .description(swaggerProperties.getDescription())
+ .contact(new Contact(swaggerProperties.getContactName(), swaggerProperties.getContactAddress(), swaggerProperties.getContactEmail()))
+ .version(swaggerProperties.getVersion())
+ .build();
+ }
+
+ /**
+ * 设置全局安全策略
+ *
+ * @return /
+ */
+ private List securitySchemes() {
+ List schemes = new ArrayList<>();
+ SecurityScheme scheme = new ApiKey(swaggerProperties.getTokenHeader(), swaggerProperties.getTokenHeader(), In.HEADER.toValue());
+ schemes.add(scheme);
+ return schemes;
+ }
+
+ /**
+ * 授权信息全局应用
+ */
+ private List securityContexts() {
+ SecurityContext securityContext = SecurityContext.builder()
+ .securityReferences(Collections.singletonList(new SecurityReference(swaggerProperties.getTokenHeader(), new AuthorizationScope[]{new AuthorizationScope("global", "")})))
+ .build();
+ return Collections.singletonList(securityContext);
+
+ }
+
+ @Override
+ public void onApplicationEvent(WebServerInitializedEvent webServerInitializedEvent) {
+ try {
+ Logger logger = LoggerFactory.getLogger(Swagger2Config.class);
+
+ //获取IP
+ String hostAddress = Inet4Address.getLocalHost().getHostAddress();
+ //获取端口号
+ int port = webServerInitializedEvent.getWebServer().getPort();
+ //获取应用名
+ String applicationName = webServerInitializedEvent.getApplicationContext().getApplicationName();
+ logger.info("项目启动成功!接口文档地址: http://" + hostAddress + ":" + webServerInitializedEvent.getWebServer().getPort() + applicationName + "/doc.html#");
+ } catch (UnknownHostException e) {
+ e.printStackTrace();
+ }
+ }
+
+}
diff --git a/src/main/java/com/sztzjy/forex/trading_trading/config/swagger/SwaggerProperties.java b/src/main/java/com/sztzjy/forex/trading_trading/config/swagger/SwaggerProperties.java
new file mode 100644
index 0000000..27d3bcd
--- /dev/null
+++ b/src/main/java/com/sztzjy/forex/trading_trading/config/swagger/SwaggerProperties.java
@@ -0,0 +1,115 @@
+package com.sztzjy.forex.trading_trading.config.swagger;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+/**
+ * @author 陈沅
+ */
+@Component
+@ConfigurationProperties(prefix = "swagger")
+public class SwaggerProperties {
+ /**
+ * 是否开启swagger,生产环境一般关闭
+ */
+ private Boolean enable;
+ /**
+ * 授权KEY
+ */
+ private String tokenHeader;
+
+ /**
+ * 标题
+ */
+ private String title;
+
+ /**
+ * 描述
+ */
+ private String description;
+
+ /**
+ * 联系人姓名
+ */
+ private String contactName;
+
+ /**
+ * 联系人地址
+ */
+ private String contactAddress;
+
+ /**
+ * 联系人邮箱
+ */
+ private String contactEmail;
+
+ /**
+ * 版本
+ */
+ private String version;
+
+ public Boolean getEnable() {
+ return enable;
+ }
+
+ public void setEnable(Boolean enable) {
+ this.enable = enable;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public String getContactName() {
+ return contactName;
+ }
+
+ public void setContactName(String contactName) {
+ this.contactName = contactName;
+ }
+
+ public String getContactAddress() {
+ return contactAddress;
+ }
+
+ public void setContactAddress(String contactAddress) {
+ this.contactAddress = contactAddress;
+ }
+
+ public String getContactEmail() {
+ return contactEmail;
+ }
+
+ public void setContactEmail(String contactEmail) {
+ this.contactEmail = contactEmail;
+ }
+
+ public String getVersion() {
+ return version;
+ }
+
+ public void setVersion(String version) {
+ this.version = version;
+ }
+
+ public String getTokenHeader() {
+ return tokenHeader;
+ }
+
+ public void setTokenHeader(String tokenHeader) {
+ this.tokenHeader = tokenHeader;
+ }
+}
+
diff --git a/src/main/java/com/sztzjy/forex/trading_trading/util/ResultDataEntity.java b/src/main/java/com/sztzjy/forex/trading_trading/util/ResultDataEntity.java
new file mode 100644
index 0000000..fc5c05b
--- /dev/null
+++ b/src/main/java/com/sztzjy/forex/trading_trading/util/ResultDataEntity.java
@@ -0,0 +1,55 @@
+package com.sztzjy.forex.trading_trading.util;
+
+import lombok.Getter;
+import org.springframework.http.HttpStatus;
+
+/**
+ * 出参对象统一封装
+ */
+@Getter
+public class ResultDataEntity {
+ /**
+ * 状态码
+ */
+ private final Integer code;
+ /**
+ * 异常提示
+ */
+// @JsonInclude(JsonInclude.Include.NON_NULL)
+ private String msg = null;
+ /**
+ * 返回体对象
+ */
+// @JsonInclude(JsonInclude.Include.NON_NULL)
+ private T data = null;
+
+ public ResultDataEntity(T data) {
+ this.code = HttpStatus.OK.value();
+ this.data = data;
+ }
+
+ public ResultDataEntity(HttpStatus status, T data) {
+ this.code = status.value();
+ this.data = data;
+ }
+
+ public ResultDataEntity(HttpStatus status, String msg, T data) {
+ this.code = status.value();
+ this.msg = msg;
+ this.data = data;
+ }
+
+ public ResultDataEntity(String msg) {
+ this.code = HttpStatus.OK.value();
+ this.msg = msg;
+ }
+
+ public ResultDataEntity(HttpStatus status, String msg) {
+ this.code = status.value();
+ this.msg = msg;
+ }
+
+ public ResultDataEntity(HttpStatus status) {
+ this.code = status.value();
+ }
+}
diff --git a/src/main/java/com/sztzjy/forex/trading_trading/util/ResultEntity.java b/src/main/java/com/sztzjy/forex/trading_trading/util/ResultEntity.java
new file mode 100644
index 0000000..f89acba
--- /dev/null
+++ b/src/main/java/com/sztzjy/forex/trading_trading/util/ResultEntity.java
@@ -0,0 +1,38 @@
+package com.sztzjy.forex.trading_trading.util;
+
+import io.swagger.annotations.ApiModel;
+import lombok.Getter;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+
+/**
+ * 对ResponseEntity做了简单包装
+ *
+ */
+@ApiModel("API返回对象")
+@Getter
+public class ResultEntity extends ResponseEntity> {
+ public ResultEntity(T data) {
+ super(new ResultDataEntity<>(HttpStatus.OK, data), HttpStatus.OK);
+ }
+
+ public ResultEntity(HttpStatus status, T data) {
+ super(new ResultDataEntity<>(status, data), status);
+ }
+
+ public ResultEntity(HttpStatus status, String msg, T data) {
+ super(new ResultDataEntity<>(status, msg, data), status);
+ }
+
+ public ResultEntity(String msg) {
+ super(new ResultDataEntity<>(HttpStatus.OK, msg), HttpStatus.OK);
+ }
+
+ public ResultEntity(HttpStatus status, String msg) {
+ super(new ResultDataEntity<>(status, msg), status);
+ }
+
+ public ResultEntity(HttpStatus status) {
+ super(new ResultDataEntity<>(status), status);
+ }
+}
diff --git a/src/main/java/com/sztzjy/forex/trading_trading/util/ThrowableUtil.java b/src/main/java/com/sztzjy/forex/trading_trading/util/ThrowableUtil.java
new file mode 100644
index 0000000..0b1e831
--- /dev/null
+++ b/src/main/java/com/sztzjy/forex/trading_trading/util/ThrowableUtil.java
@@ -0,0 +1,23 @@
+package com.sztzjy.forex.trading_trading.util;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+/**
+ * 异常工具
+ *
+ * 陈沅
+ */
+public class ThrowableUtil {
+
+ /**
+ * 获取堆栈信息
+ */
+ public static String getStackTrace(Throwable throwable) {
+ StringWriter sw = new StringWriter();
+ try (PrintWriter pw = new PrintWriter(sw)) {
+ throwable.printStackTrace(pw);
+ return sw.toString();
+ }
+ }
+}
diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml
new file mode 100644
index 0000000..3e38a98
--- /dev/null
+++ b/src/main/resources/application-dev.yml
@@ -0,0 +1,8 @@
+spring:
+ datasource:
+ druid:
+ db-type: com.alibaba.druid.pool.DruidDataSource
+ driverClassName: com.mysql.cj.jdbc.Driver
+ url: jdbc:mysql://${DB_HOST:118.31.7.2}:${DB_PORT:3306}}/${DB_NAME:foreign_trading_system}?useSSL=false&serverTimezone=UTC
+ username: ${DB_USER:root}
+ password: ${DB_PWD:sztzjy2017}
\ No newline at end of file
diff --git a/src/main/resources/application-pro.yml b/src/main/resources/application-pro.yml
new file mode 100644
index 0000000..e69de29
diff --git a/src/main/resources/application-test.yml b/src/main/resources/application-test.yml
new file mode 100644
index 0000000..e69de29
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
new file mode 100644
index 0000000..6a4c421
--- /dev/null
+++ b/src/main/resources/application.yml
@@ -0,0 +1,82 @@
+server:
+ port: 8800
+ servlet:
+ context-path: /
+
+spring:
+ application:
+ name: trading_system
+ profiles:
+ active: dev
+ mvc:
+ pathmatch:
+ matching-strategy: ant_path_matcher
+ jackson:
+ time-zone: GMT+8
+ datasource:
+ druid:
+ # 初始连接数
+ initial-size: 5
+ # 最小连接数
+ min-idle: 15
+ # 最大连接数
+ max-active: 500
+ # 获取连接超时时间
+ max-wait: 5000
+ # 连接有效性检测时间
+ time-between-eviction-runs-millis: 60000
+ # 连接在池中最小生存的时间
+ min-evictable-idle-time-millis: 300000
+ # 连接在池中最大生存的时间
+ max-evictable-idle-time-millis: 900000
+ # 指明连接是否被空闲连接回收器(如果有)进行检验.如果检测失败,则连接将被从池中去除
+ test-while-idle: true
+ # 指明是否在从池中取出连接前进行检验,如果检验失败, 则从池中去除连接并尝试取出另一个
+ test-on-borrow: false
+ # 是否在归还到池中前进行检验
+ test-on-return: false
+ # 检测连接是否有效
+ validation-query: select 'X'
+ # 配置监控统计
+ webStatFilter:
+ enabled: true
+ stat-view-servlet:
+ allow:
+ enabled: true
+ # 控制台管理用户名和密码
+ url-pattern: /druid/*
+ reset-enable: false
+ login-username: admin
+ login-password: 2023inspect
+ filter:
+ stat:
+ enabled: true
+ # 记录慢SQL
+ log-slow-sql: false
+ slow-sql-millis: 1000
+ merge-sql: true
+ wall:
+ config:
+ multi-statement-allow: true
+ #配置 Jpa
+ jpa:
+ hibernate:
+ ddl-auto: update
+ open-in-view: true
+ show-sql: true
+ properties:
+ hibernate:
+ format_sql: true
+ dialect: org.hibernate.dialect.MySQL8Dialect
+ temp:
+ use_jdbc_metadata_defaults: false
+ database: mysql
+
+swagger:
+ enable: true
+ tokenHeader: Authorization
+ title: 天择外汇模拟交易 • 接口文档
+ description: 天择外汇模拟交易WebAPI接口文档
+ contactName: 深圳天择教育科技有限公司
+ contactAddress: www.sztzjy.com
+ version: @project.version@
diff --git a/src/test/java/com/sztzjy/foreign_exchange_trading/ForeignExchangeTradingApplicationTests.java b/src/test/java/com/sztzjy/foreign_exchange_trading/ForeignExchangeTradingApplicationTests.java
new file mode 100644
index 0000000..b44314f
--- /dev/null
+++ b/src/test/java/com/sztzjy/foreign_exchange_trading/ForeignExchangeTradingApplicationTests.java
@@ -0,0 +1,13 @@
+package com.sztzjy.foreign_exchange_trading;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+
+@SpringBootTest
+class ForeignExchangeTradingApplicationTests {
+
+ @Test
+ void contextLoads() {
+ }
+
+}