Forráskód Böngészése

:tada: 2.2.0.RELEASE

smallchill 6 éve
szülő
commit
70a1574a8a
32 módosított fájl, 678 hozzáadás és 54 törlés
  1. 1 1
      blade-core-boot/pom.xml
  2. 6 0
      blade-core-boot/src/main/resources/bootstrap.yml
  3. 1 1
      blade-core-cloud/pom.xml
  4. 1 1
      blade-core-launch/pom.xml
  5. 1 0
      blade-core-launch/src/main/java/org/springblade/core/launch/BladeApplication.java
  6. 15 0
      blade-core-launch/src/main/java/org/springblade/core/launch/constant/SentinelConstant.java
  7. 45 0
      blade-core-launch/src/main/java/org/springblade/core/launch/constant/TokenConstant.java
  8. 1 1
      blade-core-log/pom.xml
  9. 1 1
      blade-core-mybatis/pom.xml
  10. 12 1
      blade-core-secure/pom.xml
  11. 1 1
      blade-core-secure/src/main/java/org/springblade/core/secure/AuthInfo.java
  12. 5 0
      blade-core-secure/src/main/java/org/springblade/core/secure/BladeUser.java
  13. 3 1
      blade-core-secure/src/main/java/org/springblade/core/secure/annotation/PreAuth.java
  14. 1 1
      blade-core-secure/src/main/java/org/springblade/core/secure/config/RegistryConfiguration.java
  15. 26 2
      blade-core-secure/src/main/java/org/springblade/core/secure/config/SecureConfiguration.java
  16. 55 0
      blade-core-secure/src/main/java/org/springblade/core/secure/constant/SecureConstant.java
  17. 1 1
      blade-core-secure/src/main/java/org/springblade/core/secure/exception/SecureException.java
  18. 67 0
      blade-core-secure/src/main/java/org/springblade/core/secure/interceptor/ClientInterceptor.java
  19. 3 2
      blade-core-secure/src/main/java/org/springblade/core/secure/interceptor/SecureInterceptor.java
  20. 35 0
      blade-core-secure/src/main/java/org/springblade/core/secure/props/BladeClientProperties.java
  21. 35 0
      blade-core-secure/src/main/java/org/springblade/core/secure/props/BladeSecureProperties.java
  22. 35 0
      blade-core-secure/src/main/java/org/springblade/core/secure/props/ClientSecure.java
  23. 51 0
      blade-core-secure/src/main/java/org/springblade/core/secure/provider/ClientDetails.java
  24. 42 0
      blade-core-secure/src/main/java/org/springblade/core/secure/provider/ClientDetailsServiceImpl.java
  25. 55 0
      blade-core-secure/src/main/java/org/springblade/core/secure/provider/IClientDetails.java
  26. 33 0
      blade-core-secure/src/main/java/org/springblade/core/secure/provider/IClientDetailsService.java
  27. 0 6
      blade-core-secure/src/main/java/org/springblade/core/secure/registry/SecureRegistry.java
  28. 133 27
      blade-core-secure/src/main/java/org/springblade/core/secure/utils/SecureUtil.java
  29. 1 1
      blade-core-swagger/pom.xml
  30. 1 1
      blade-core-tool/pom.xml
  31. 9 3
      blade-core-tool/src/main/java/org/springblade/core/tool/utils/Charsets.java
  32. 2 2
      pom.xml

+ 1 - 1
blade-core-boot/pom.xml

@@ -5,7 +5,7 @@
     <parent>
         <groupId>org.springblade</groupId>
         <artifactId>blade-tool</artifactId>
-        <version>2.1.1</version>
+        <version>2.2.0</version>
     </parent>
 
     <modelVersion>4.0.0</modelVersion>

+ 6 - 0
blade-core-boot/src/main/resources/bootstrap.yml

@@ -29,6 +29,12 @@ spring:
     add-mappings: false
   datasource:
     driver-class-name: com.mysql.jdbc.Driver
+    hikari:
+      connection-test-query: SELECT 1 FROM DUAL
+      connection-timeout: 30000
+      maximum-pool-size: 5
+      max-lifetime: 1800000
+      minimum-idle: 1
   devtools:
     restart:
       log-condition-evaluation-delta: false

+ 1 - 1
blade-core-cloud/pom.xml

@@ -5,7 +5,7 @@
     <parent>
         <artifactId>blade-tool</artifactId>
         <groupId>org.springblade</groupId>
-        <version>2.1.1</version>
+        <version>2.2.0</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
 

+ 1 - 1
blade-core-launch/pom.xml

@@ -5,7 +5,7 @@
     <parent>
         <artifactId>blade-tool</artifactId>
         <groupId>org.springblade</groupId>
-        <version>2.1.1</version>
+        <version>2.2.0</version>
     </parent>
 
     <modelVersion>4.0.0</modelVersion>

+ 1 - 0
blade-core-launch/src/main/java/org/springblade/core/launch/BladeApplication.java

@@ -93,6 +93,7 @@ public class BladeApplication {
 		props.setProperty("blade.is-local", String.valueOf(isLocalDev()));
 		props.setProperty("blade.dev-mode", profile.equals(AppConstant.PROD_CODE) ? "false" : "true");
 		props.setProperty("blade.service.version", AppConstant.APPLICATION_VERSION);
+		props.setProperty("spring.main.allow-bean-definition-overriding", "true");
 		props.setProperty("spring.cloud.nacos.discovery.server-addr", NacosConstant.NACOS_ADDR);
 		props.setProperty("spring.cloud.nacos.config.server-addr", NacosConstant.NACOS_ADDR);
 		props.setProperty("spring.cloud.nacos.config.prefix", NacosConstant.NACOS_CONFIG_PREFIX);

+ 15 - 0
blade-core-launch/src/main/java/org/springblade/core/launch/constant/SentinelConstant.java

@@ -1,3 +1,18 @@
+/**
+ * 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.launch.constant;
 
 /**

+ 45 - 0
blade-core-launch/src/main/java/org/springblade/core/launch/constant/TokenConstant.java

@@ -0,0 +1,45 @@
+/**
+ * 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.launch.constant;
+
+/**
+ * Token配置常量.
+ *
+ * @author Chill
+ */
+public interface TokenConstant {
+
+	String SIGN_KEY = "BladeX";
+	String AVATAR = "avatar";
+	String HEADER = "blade-auth";
+	String BEARER = "bearer";
+	String ACCESS_TOKEN = "access_token";
+	String REFRESH_TOKEN = "refresh_token";
+	String TOKEN_TYPE = "token_type";
+	String EXPIRES_IN = "expires_in";
+	String ACCOUNT = "account";
+	String USER_ID = "user_id";
+	String ROLE_ID = "role_id";
+	String USER_NAME = "user_name";
+	String ROLE_NAME = "role_name";
+	String TENANT_CODE = "tenant_code";
+	String CLIENT_ID = "client_id";
+	String LICENSE = "license";
+	String LICENSE_NAME = "powered by bladex";
+	String DEFAULT_AVATAR = "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png";
+	Integer AUTH_LENGTH = 7;
+
+}

+ 1 - 1
blade-core-log/pom.xml

@@ -5,7 +5,7 @@
     <parent>
         <artifactId>blade-tool</artifactId>
         <groupId>org.springblade</groupId>
-        <version>2.1.1</version>
+        <version>2.2.0</version>
     </parent>
 
     <modelVersion>4.0.0</modelVersion>

+ 1 - 1
blade-core-mybatis/pom.xml

@@ -5,7 +5,7 @@
     <parent>
         <artifactId>blade-tool</artifactId>
         <groupId>org.springblade</groupId>
-        <version>2.1.1</version>
+        <version>2.2.0</version>
     </parent>
 
     <modelVersion>4.0.0</modelVersion>

+ 12 - 1
blade-core-secure/pom.xml

@@ -5,7 +5,7 @@
     <parent>
         <artifactId>blade-tool</artifactId>
         <groupId>org.springblade</groupId>
-        <version>2.1.1</version>
+        <version>2.2.0</version>
     </parent>
 
     <modelVersion>4.0.0</modelVersion>
@@ -28,6 +28,17 @@
             <artifactId>blade-core-tool</artifactId>
             <version>${blade.tool.version}</version>
         </dependency>
+        <!--Jdbc-->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-jdbc</artifactId>
+            <exclusions>
+                <exclusion>
+                    <artifactId>tomcat-jdbc</artifactId>
+                    <groupId>org.apache.tomcat</groupId>
+                </exclusion>
+            </exclusions>
+        </dependency>
     </dependencies>
 
 </project>

+ 1 - 1
blade-core-secure/src/main/java/org/springblade/core/secure/AuthInfo.java

@@ -42,5 +42,5 @@ public class AuthInfo {
 	@ApiModelProperty(value = "过期时间")
 	private long expiresIn;
 	@ApiModelProperty(value = "许可证")
-	private String license = "made by blade";
+	private String license = "powered by blade";
 }

+ 5 - 0
blade-core-secure/src/main/java/org/springblade/core/secure/BladeUser.java

@@ -29,6 +29,11 @@ import java.io.Serializable;
 public class BladeUser implements Serializable {
 
 	private static final long serialVersionUID = 1L;
+	/**
+	 * 客户端id
+	 */
+	@ApiModelProperty(hidden = true)
+	private String clientId;
 
 	/**
 	 * 用户id

+ 3 - 1
blade-core-secure/src/main/java/org/springblade/core/secure/annotation/PreAuth.java

@@ -20,7 +20,9 @@ import java.lang.annotation.*;
 /**
  * 权限注解 用于检查权限 规定访问权限
  *
- * @author Chill
+ * @example @PreAuth("#userVO.id<10")
+ * @example @PreAuth("hasRole(#test, #test1)")
+ * @example @PreAuth("hasPermission(#test) and @PreAuth.hasPermission(#test)")
  */
 @Target({ElementType.METHOD, ElementType.TYPE})
 @Retention(RetentionPolicy.RUNTIME)

+ 1 - 1
blade-core-secure/src/main/java/org/springblade/core/secure/config/RegistryConfiguration.java

@@ -24,7 +24,7 @@ import org.springframework.context.annotation.Configuration;
 import org.springframework.core.annotation.Order;
 
 /**
- * secure模块api放行默认配置
+ * secure注册默认配置
  *
  * @author Chill
  */

+ 26 - 2
blade-core-secure/src/main/java/org/springblade/core/secure/config/SecureConfiguration.java

@@ -18,32 +18,50 @@ package org.springblade.core.secure.config;
 
 import lombok.AllArgsConstructor;
 import org.springblade.core.secure.aspect.AuthAspect;
+import org.springblade.core.secure.interceptor.ClientInterceptor;
 import org.springblade.core.secure.interceptor.SecureInterceptor;
+import org.springblade.core.secure.props.BladeClientProperties;
+import org.springblade.core.secure.props.BladeSecureProperties;
+import org.springblade.core.secure.provider.ClientDetailsServiceImpl;
+import org.springblade.core.secure.provider.IClientDetailsService;
 import org.springblade.core.secure.registry.SecureRegistry;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.core.annotation.Order;
+import org.springframework.jdbc.core.JdbcTemplate;
 import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
 import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
 
 /**
- * 配置类
+ * 安全配置类
  *
  * @author Chill
  */
 @Order
 @Configuration
 @AllArgsConstructor
+@EnableConfigurationProperties({BladeSecureProperties.class, BladeClientProperties.class})
 public class SecureConfiguration implements WebMvcConfigurer {
 
 	private final SecureRegistry secureRegistry;
 
+	private final BladeSecureProperties secureProperties;
+
+	private final BladeClientProperties clientProperties;
+
+	private final JdbcTemplate jdbcTemplate;
+
 	@Override
 	public void addInterceptors(InterceptorRegistry registry) {
+		clientProperties.getClient().forEach(cs -> registry.addInterceptor(new ClientInterceptor(cs.getClientId())).addPathPatterns(cs.getPathPatterns()));
+
 		if (secureRegistry.isEnable()) {
 			registry.addInterceptor(new SecureInterceptor())
 				.excludePathPatterns(secureRegistry.getExcludePatterns())
-				.excludePathPatterns(secureRegistry.getDefaultExcludePatterns());
+				.excludePathPatterns(secureRegistry.getDefaultExcludePatterns())
+				.excludePathPatterns(secureProperties.getExcludePatterns());
 		}
 	}
 
@@ -52,4 +70,10 @@ public class SecureConfiguration implements WebMvcConfigurer {
 		return new AuthAspect();
 	}
 
+	@Bean
+	@ConditionalOnMissingBean(IClientDetailsService.class)
+	public IClientDetailsService clientDetailsService() {
+		return new ClientDetailsServiceImpl(jdbcTemplate);
+	}
+
 }

+ 55 - 0
blade-core-secure/src/main/java/org/springblade/core/secure/constant/SecureConstant.java

@@ -0,0 +1,55 @@
+/**
+ * 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.constant;
+
+/**
+ * 授权校验常量
+ *
+ * @author Chill
+ */
+public interface SecureConstant {
+
+	/**
+	 * 认证请求头
+	 */
+	String BASIC_HEADER_KEY = "Authorization";
+
+	/**
+	 * 认证请求头前缀
+	 */
+	String BASIC_HEADER_PREFIX = "Basic ";
+
+	/**
+	 * blade_client表字段
+	 */
+	String CLIENT_FIELDS = "client_id, client_secret, access_token_validity, refresh_token_validity";
+
+	/**
+	 * blade_client查询语句
+	 */
+	String BASE_STATEMENT = "select " + CLIENT_FIELDS + " from blade_client";
+
+	/**
+	 * blade_client查询排序
+	 */
+	String DEFAULT_FIND_STATEMENT = BASE_STATEMENT + " order by client_id";
+
+	/**
+	 * 查询client_id
+	 */
+	String DEFAULT_SELECT_STATEMENT = BASE_STATEMENT + " where client_id = ?";
+
+}

+ 1 - 1
blade-core-secure/src/main/java/org/springblade/core/secure/exception/SecureException.java

@@ -32,7 +32,7 @@ public class SecureException extends RuntimeException {
 
 	public SecureException(String message) {
 		super(message);
-		this.resultCode = ResultCode.INTERNAL_SERVER_ERROR;
+		this.resultCode = ResultCode.UN_AUTHORIZED;
 	}
 
 	public SecureException(IResultCode resultCode) {

+ 67 - 0
blade-core-secure/src/main/java/org/springblade/core/secure/interceptor/ClientInterceptor.java

@@ -0,0 +1,67 @@
+/**
+ * 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.interceptor;
+
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springblade.core.secure.BladeUser;
+import org.springblade.core.secure.utils.SecureUtil;
+import org.springblade.core.tool.api.R;
+import org.springblade.core.tool.api.ResultCode;
+import org.springblade.core.tool.constant.BladeConstant;
+import org.springblade.core.tool.jackson.JsonUtil;
+import org.springblade.core.tool.utils.StringUtil;
+import org.springblade.core.tool.utils.WebUtil;
+import org.springframework.http.MediaType;
+import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.Objects;
+
+/**
+ * 客户端校验
+ *
+ * @author Chill
+ */
+@Slf4j
+@AllArgsConstructor
+public class ClientInterceptor extends HandlerInterceptorAdapter {
+
+	private final String clientId;
+
+	@Override
+	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
+		BladeUser user = SecureUtil.getUser();
+		if (user != null && StringUtil.equals(clientId, SecureUtil.getClientIdFromHeader()) && StringUtil.equals(clientId, user.getClientId())) {
+			return true;
+		} else {
+			log.warn("客户端认证失败,请求接口:{},请求IP:{},请求参数:{}", request.getRequestURI(), WebUtil.getIP(request), JsonUtil.toJson(request.getParameterMap()));
+			R result = R.fail(ResultCode.UN_AUTHORIZED);
+			response.setHeader(BladeConstant.CONTENT_TYPE_NAME, MediaType.APPLICATION_JSON_UTF8_VALUE);
+			response.setCharacterEncoding(BladeConstant.UTF_8);
+			response.setStatus(HttpServletResponse.SC_OK);
+			try {
+				response.getWriter().write(Objects.requireNonNull(JsonUtil.toJson(result)));
+			} catch (IOException ex) {
+				log.error(ex.getMessage());
+			}
+			return false;
+		}
+	}
+
+}

+ 3 - 2
blade-core-secure/src/main/java/org/springblade/core/secure/interceptor/SecureInterceptor.java

@@ -15,13 +15,13 @@
  */
 package org.springblade.core.secure.interceptor;
 
+import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springblade.core.secure.utils.SecureUtil;
 import org.springblade.core.tool.api.R;
 import org.springblade.core.tool.api.ResultCode;
 import org.springblade.core.tool.constant.BladeConstant;
 import org.springblade.core.tool.jackson.JsonUtil;
-import org.springblade.core.tool.utils.StringPool;
 import org.springblade.core.tool.utils.WebUtil;
 import org.springframework.http.MediaType;
 import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
@@ -37,6 +37,7 @@ import java.util.Objects;
  * @author Chill
  */
 @Slf4j
+@AllArgsConstructor
 public class SecureInterceptor extends HandlerInterceptorAdapter {
 
 	@Override
@@ -46,7 +47,7 @@ public class SecureInterceptor extends HandlerInterceptorAdapter {
 		} else {
 			log.warn("签名认证失败,请求接口:{},请求IP:{},请求参数:{}", request.getRequestURI(), WebUtil.getIP(request), JsonUtil.toJson(request.getParameterMap()));
 			R result = R.fail(ResultCode.UN_AUTHORIZED);
-			response.setCharacterEncoding(StringPool.UTF_8);
+			response.setCharacterEncoding(BladeConstant.UTF_8);
 			response.setHeader(BladeConstant.CONTENT_TYPE_NAME, MediaType.APPLICATION_JSON_UTF8_VALUE);
 			response.setStatus(HttpServletResponse.SC_OK);
 			try {

+ 35 - 0
blade-core-secure/src/main/java/org/springblade/core/secure/props/BladeClientProperties.java

@@ -0,0 +1,35 @@
+/**
+ * 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.props;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 客户端校验配置
+ *
+ * @author Chill
+ */
+@Data
+@ConfigurationProperties("blade.secure")
+public class BladeClientProperties {
+
+	private final List<ClientSecure> client = new ArrayList<>();
+
+}

+ 35 - 0
blade-core-secure/src/main/java/org/springblade/core/secure/props/BladeSecureProperties.java

@@ -0,0 +1,35 @@
+/**
+ * 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.props;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * secure放行额外配置
+ *
+ * @author Chill
+ */
+@Data
+@ConfigurationProperties("blade.secure.url")
+public class BladeSecureProperties {
+
+	private final List<String> excludePatterns = new ArrayList<>();
+
+}

+ 35 - 0
blade-core-secure/src/main/java/org/springblade/core/secure/props/ClientSecure.java

@@ -0,0 +1,35 @@
+/**
+ * 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.props;
+
+import lombok.Data;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 客户端令牌认证信息
+ *
+ * @author Chill
+ */
+@Data
+public class ClientSecure {
+
+	private String clientId;
+
+	private final List<String> pathPatterns = new ArrayList<>();
+
+}

+ 51 - 0
blade-core-secure/src/main/java/org/springblade/core/secure/provider/ClientDetails.java

@@ -0,0 +1,51 @@
+/**
+ * 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.provider;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ * 客户端详情
+ *
+ * @author Chill
+ */
+@Data
+public class ClientDetails implements IClientDetails {
+
+	/**
+	 * 客户端id
+	 */
+	@ApiModelProperty(value = "客户端id")
+	private String clientId;
+	/**
+	 * 客户端密钥
+	 */
+	@ApiModelProperty(value = "客户端密钥")
+	private String clientSecret;
+
+	/**
+	 * 令牌过期秒数
+	 */
+	@ApiModelProperty(value = "令牌过期秒数")
+	private Integer accessTokenValidity;
+	/**
+	 * 刷新令牌过期秒数
+	 */
+	@ApiModelProperty(value = "刷新令牌过期秒数")
+	private Integer refreshTokenValidity;
+
+}

+ 42 - 0
blade-core-secure/src/main/java/org/springblade/core/secure/provider/ClientDetailsServiceImpl.java

@@ -0,0 +1,42 @@
+/**
+ * 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.provider;
+
+import lombok.AllArgsConstructor;
+import org.springblade.core.secure.constant.SecureConstant;
+import org.springframework.jdbc.core.BeanPropertyRowMapper;
+import org.springframework.jdbc.core.JdbcTemplate;
+
+/**
+ * 获取客户端详情
+ *
+ * @author Chill
+ */
+@AllArgsConstructor
+public class ClientDetailsServiceImpl implements IClientDetailsService {
+
+	private final JdbcTemplate jdbcTemplate;
+
+	@Override
+	public IClientDetails loadClientByClientId(String clientId) {
+		try {
+			return jdbcTemplate.queryForObject(SecureConstant.DEFAULT_SELECT_STATEMENT, new String[]{clientId}, new BeanPropertyRowMapper<>(ClientDetails.class));
+		} catch (Exception ex) {
+			return null;
+		}
+	}
+
+}

+ 55 - 0
blade-core-secure/src/main/java/org/springblade/core/secure/provider/IClientDetails.java

@@ -0,0 +1,55 @@
+/**
+ * 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.provider;
+
+import java.io.Serializable;
+
+/**
+ * 多终端详情接口
+ *
+ * @author Chill
+ */
+public interface IClientDetails extends Serializable {
+
+	/**
+	 * 客户端id.
+	 *
+	 * @return String.
+	 */
+	String getClientId();
+
+	/**
+	 * 客户端密钥.
+	 *
+	 * @return String.
+	 */
+	String getClientSecret();
+
+	/**
+	 * 客户端token过期时间
+	 *
+	 * @return Integer
+	 */
+	Integer getAccessTokenValidity();
+
+	/**
+	 * 客户端刷新token过期时间
+	 *
+	 * @return Integer
+	 */
+	Integer getRefreshTokenValidity();
+
+}

+ 33 - 0
blade-core-secure/src/main/java/org/springblade/core/secure/provider/IClientDetailsService.java

@@ -0,0 +1,33 @@
+/**
+ * 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.provider;
+
+/**
+ * 多终端注册接口
+ *
+ * @author Chill
+ */
+public interface IClientDetailsService {
+
+	/**
+	 * 根据clientId获取Client详情
+	 *
+	 * @param clientId 客户端id
+	 * @return
+	 */
+	IClientDetails loadClientByClientId(String clientId);
+
+}

+ 0 - 6
blade-core-secure/src/main/java/org/springblade/core/secure/registry/SecureRegistry.java

@@ -50,9 +50,6 @@ public class SecureRegistry {
 
 	/**
 	 * 设置放行api
-	 *
-	 * @param patterns api配置
-	 * @return SecureRegistry
 	 */
 	public SecureRegistry excludePathPatterns(String... patterns) {
 		return excludePathPatterns(Arrays.asList(patterns));
@@ -60,9 +57,6 @@ public class SecureRegistry {
 
 	/**
 	 * 设置放行api
-	 *
-	 * @param patterns api配置
-	 * @return SecureRegistry
 	 */
 	public SecureRegistry excludePathPatterns(List<String> patterns) {
 		this.excludePatterns.addAll(patterns);

+ 133 - 27
blade-core-secure/src/main/java/org/springblade/core/secure/utils/SecureUtil.java

@@ -19,19 +19,19 @@ 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.tool.utils.Charsets;
-import org.springblade.core.tool.utils.Func;
-import org.springblade.core.tool.utils.StringPool;
-import org.springblade.core.tool.utils.WebUtil;
+import org.springblade.core.secure.constant.SecureConstant;
+import org.springblade.core.secure.exception.SecureException;
+import org.springblade.core.secure.provider.IClientDetails;
+import org.springblade.core.secure.provider.IClientDetailsService;
+import org.springblade.core.tool.utils.*;
 
 import javax.crypto.spec.SecretKeySpec;
 import javax.servlet.http.HttpServletRequest;
 import java.security.Key;
-import java.util.Base64;
-import java.util.Calendar;
-import java.util.Date;
-import java.util.Map;
+import java.util.*;
 
 /**
  * Secure工具类
@@ -39,18 +39,25 @@ import java.util.Map;
  * @author Chill
  */
 public class SecureUtil {
-	public static final String BLADE_USER_REQUEST_ATTR = "_BLADE_USER_REQUEST_ATTR_";
-
-	public final static String HEADER = "blade-auth";
-	public final static String BEARER = "bearer";
-	public final static String ACCOUNT = "account";
-	public final static String USER_ID = "userId";
-	public final static String ROLE_ID = "roleId";
-	public final static String USER_NAME = "userName";
-	public final static String ROLE_NAME = "roleName";
-	public final static String TENANT_CODE = "tenantCode";
-	public final static Integer AUTH_LENGTH = 7;
-	public static String BASE64_SECURITY = Base64.getEncoder().encodeToString("BladeX".getBytes(Charsets.UTF_8));
+	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 USER_NAME = TokenConstant.USER_NAME;
+	private final static String ROLE_NAME = TokenConstant.ROLE_NAME;
+	private final static String TENANT_CODE = TokenConstant.TENANT_CODE;
+	private final static String CLIENT_ID = TokenConstant.CLIENT_ID;
+	private final static Integer AUTH_LENGTH = TokenConstant.AUTH_LENGTH;
+	private static String BASE64_SECURITY = Base64.getEncoder().encodeToString(TokenConstant.SIGN_KEY.getBytes(Charsets.UTF_8));
+
+	private static IClientDetailsService clientDetailsService;
+
+	static {
+		clientDetailsService = SpringUtil.getBean(IClientDetailsService.class);
+	}
 
 	/**
 	 * 获取用户信息
@@ -59,8 +66,11 @@ public class SecureUtil {
 	 */
 	public static BladeUser getUser() {
 		HttpServletRequest request = WebUtil.getRequest();
+		if (request == null) {
+			return null;
+		}
 		// 优先从 request 中获取
-		BladeUser bladeUser = (BladeUser) request.getAttribute(BLADE_USER_REQUEST_ATTR);
+		Object bladeUser = request.getAttribute(BLADE_USER_REQUEST_ATTR);
 		if (bladeUser == null) {
 			bladeUser = getUser(request);
 			if (bladeUser != null) {
@@ -68,7 +78,7 @@ public class SecureUtil {
 				request.setAttribute(BLADE_USER_REQUEST_ATTR, bladeUser);
 			}
 		}
-		return bladeUser;
+		return (BladeUser) bladeUser;
 	}
 
 	/**
@@ -82,6 +92,7 @@ public class SecureUtil {
 		if (claims == null) {
 			return null;
 		}
+		String clientId = Func.toStr(claims.get(SecureUtil.CLIENT_ID));
 		Integer userId = Func.toInt(claims.get(SecureUtil.USER_ID));
 		String tenantCode = Func.toStr(claims.get(SecureUtil.TENANT_CODE));
 		String roleId = Func.toStr(claims.get(SecureUtil.ROLE_ID));
@@ -89,6 +100,7 @@ public class SecureUtil {
 		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.setTenantCode(tenantCode);
 		bladeUser.setAccount(account);
@@ -183,7 +195,6 @@ public class SecureUtil {
 		return (null == user) ? StringPool.EMPTY : user.getRoleName();
 	}
 
-
 	/**
 	 * 获取租户编号
 	 *
@@ -205,6 +216,27 @@ public class SecureUtil {
 		return (null == user) ? StringPool.EMPTY : user.getTenantCode();
 	}
 
+	/**
+	 * 获取客户端id
+	 *
+	 * @return tenantCode
+	 */
+	public static String getClientId() {
+		BladeUser user = getUser();
+		return (null == user) ? StringPool.EMPTY : user.getClientId();
+	}
+
+	/**
+	 * 获取客户端id
+	 *
+	 * @param request request
+	 * @return tenantCode
+	 */
+	public static String getClientId(HttpServletRequest request) {
+		BladeUser user = getUser(request);
+		return (null == user) ? StringPool.EMPTY : user.getClientId();
+	}
+
 	/**
 	 * 获取Claims
 	 *
@@ -229,7 +261,7 @@ public class SecureUtil {
 	 * @return header
 	 */
 	public static String getHeader() {
-		return getHeader(WebUtil.getRequest());
+		return getHeader(Objects.requireNonNull(WebUtil.getRequest()));
 	}
 
 	/**
@@ -250,17 +282,16 @@ public class SecureUtil {
 	 */
 	public static Claims parseJWT(String jsonWebToken) {
 		try {
-			Claims claims = Jwts.parser()
+			return Jwts.parser()
 				.setSigningKey(Base64.getDecoder().decode(BASE64_SECURITY))
 				.parseClaimsJws(jsonWebToken).getBody();
-			return claims;
 		} catch (Exception ex) {
 			return null;
 		}
 	}
 
 	/**
-	 * 创建jwt
+	 * 创建令牌
 	 *
 	 * @param user     user
 	 * @param audience audience
@@ -269,6 +300,17 @@ public class SecureUtil {
 	 * @return jwt
 	 */
 	public static String createJWT(Map<String, String> user, String audience, String issuer, boolean isExpire) {
+
+		String[] tokens = extractAndDecodeHeader();
+		assert tokens.length == 2;
+		String clientId = tokens[0];
+		String clientSecret = tokens[1];
+
+		// 校验客户端信息
+		if (!validateClient(clientId, clientSecret)) {
+			throw new SecureException("客户端认证失败!");
+		}
+
 		SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
 
 		long nowMillis = System.currentTimeMillis();
@@ -287,6 +329,9 @@ public class SecureUtil {
 		//设置JWT参数
 		user.forEach(builder::claim);
 
+		//设置应用id
+		builder.claim(CLIENT_ID, clientId);
+
 		//添加Token过期时间
 		if (isExpire) {
 			long expMillis = nowMillis + getExpire();
@@ -313,4 +358,65 @@ public class SecureUtil {
 		return cal.getTimeInMillis() - System.currentTimeMillis();
 	}
 
+	/**
+	 * 获取过期时间的秒数(次日凌晨3点)
+	 *
+	 * @return expire
+	 */
+	public static int getExpireSeconds() {
+		return (int) (getExpire() / 1000);
+	}
+
+	/**
+	 * 客户端信息解码
+	 */
+	@SneakyThrows
+	public static String[] extractAndDecodeHeader() {
+		// 获取请求头客户端信息
+		String header = Objects.requireNonNull(WebUtil.getRequest()).getHeader(SecureConstant.BASIC_HEADER_KEY);
+		if (header == null || !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];
+	}
+
+	/**
+	 * 校验Client
+	 *
+	 * @param clientId     客户端id
+	 * @param clientSecret 客户端密钥
+	 * @return boolean
+	 */
+	private static boolean validateClient(String clientId, String clientSecret) {
+		IClientDetails clientDetails = clientDetailsService.loadClientByClientId(clientId);
+		if (clientDetails != null) {
+			return StringUtil.equals(clientId, clientDetails.getClientId()) && StringUtil.equals(clientSecret, clientDetails.getClientSecret());
+		}
+		return false;
+	}
+
 }

+ 1 - 1
blade-core-swagger/pom.xml

@@ -5,7 +5,7 @@
     <parent>
         <artifactId>blade-tool</artifactId>
         <groupId>org.springblade</groupId>
-        <version>2.1.1</version>
+        <version>2.2.0</version>
     </parent>
 
     <modelVersion>4.0.0</modelVersion>

+ 1 - 1
blade-core-tool/pom.xml

@@ -6,7 +6,7 @@
     <parent>
         <groupId>org.springblade</groupId>
         <artifactId>blade-tool</artifactId>
-        <version>2.1.1</version>
+        <version>2.2.0</version>
     </parent>
 
     <modelVersion>4.0.0</modelVersion>

+ 9 - 3
blade-core-tool/src/main/java/org/springblade/core/tool/utils/Charsets.java

@@ -16,6 +16,7 @@
 package org.springblade.core.tool.utils;
 
 
+import java.nio.charset.Charset;
 import java.nio.charset.StandardCharsets;
 import java.nio.charset.UnsupportedCharsetException;
 
@@ -29,15 +30,20 @@ public class Charsets {
 	/**
 	 * 字符集ISO-8859-1
 	 */
-	public static final java.nio.charset.Charset ISO_8859_1 = StandardCharsets.ISO_8859_1;
+	public static final Charset ISO_8859_1 = StandardCharsets.ISO_8859_1;
+	public static final String ISO_8859_1_NAME = ISO_8859_1.name();
+
 	/**
 	 * 字符集GBK
 	 */
-	public static final java.nio.charset.Charset GBK = java.nio.charset.Charset.forName(StringPool.GBK);
+	public static final Charset GBK = Charset.forName(StringPool.GBK);
+	public static final String GBK_NAME = GBK.name();
+
 	/**
 	 * 字符集utf-8
 	 */
-	public static final java.nio.charset.Charset UTF_8 = StandardCharsets.UTF_8;
+	public static final Charset UTF_8 = StandardCharsets.UTF_8;
+	public static final String UTF_8_NAME = UTF_8.name();
 
 	/**
 	 * 转换为Charset对象

+ 2 - 2
pom.xml

@@ -5,7 +5,7 @@
 
     <groupId>org.springblade</groupId>
     <artifactId>blade-tool</artifactId>
-    <version>2.1.1</version>
+    <version>2.2.0</version>
     <packaging>pom</packaging>
     <name>blade-tool</name>
     <description>
@@ -36,7 +36,7 @@
     </scm>
 
     <properties>
-        <blade.tool.version>2.1.1</blade.tool.version>
+        <blade.tool.version>2.2.0</blade.tool.version>
 
         <java.version>1.8</java.version>
         <maven.plugin.version>3.8.0</maven.plugin.version>