diff --git a/blade-core-boot/pom.xml b/blade-core-boot/pom.xml index 7c1ab1e..b05be16 100644 --- a/blade-core-boot/pom.xml +++ b/blade-core-boot/pom.xml @@ -5,7 +5,7 @@ org.springblade blade-tool - 2.1.1 + 2.2.0 4.0.0 diff --git a/blade-core-boot/src/main/resources/bootstrap.yml b/blade-core-boot/src/main/resources/bootstrap.yml index de15ff2..06783df 100644 --- a/blade-core-boot/src/main/resources/bootstrap.yml +++ b/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 diff --git a/blade-core-cloud/pom.xml b/blade-core-cloud/pom.xml index 4bf1274..8628617 100644 --- a/blade-core-cloud/pom.xml +++ b/blade-core-cloud/pom.xml @@ -5,7 +5,7 @@ blade-tool org.springblade - 2.1.1 + 2.2.0 4.0.0 diff --git a/blade-core-launch/pom.xml b/blade-core-launch/pom.xml index 43b1295..a1b4047 100644 --- a/blade-core-launch/pom.xml +++ b/blade-core-launch/pom.xml @@ -5,7 +5,7 @@ blade-tool org.springblade - 2.1.1 + 2.2.0 4.0.0 diff --git a/blade-core-launch/src/main/java/org/springblade/core/launch/BladeApplication.java b/blade-core-launch/src/main/java/org/springblade/core/launch/BladeApplication.java index 8b21bd2..9aa110c 100644 --- a/blade-core-launch/src/main/java/org/springblade/core/launch/BladeApplication.java +++ b/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); diff --git a/blade-core-launch/src/main/java/org/springblade/core/launch/constant/SentinelConstant.java b/blade-core-launch/src/main/java/org/springblade/core/launch/constant/SentinelConstant.java index eaf7ed4..6cb0bc3 100644 --- a/blade-core-launch/src/main/java/org/springblade/core/launch/constant/SentinelConstant.java +++ b/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). + *

+ * 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 + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * 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; /** diff --git a/blade-core-launch/src/main/java/org/springblade/core/launch/constant/TokenConstant.java b/blade-core-launch/src/main/java/org/springblade/core/launch/constant/TokenConstant.java new file mode 100644 index 0000000..3ee07c1 --- /dev/null +++ b/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). + *

+ * 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 + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * 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; + +} diff --git a/blade-core-log/pom.xml b/blade-core-log/pom.xml index 36976e8..903825e 100644 --- a/blade-core-log/pom.xml +++ b/blade-core-log/pom.xml @@ -5,7 +5,7 @@ blade-tool org.springblade - 2.1.1 + 2.2.0 4.0.0 diff --git a/blade-core-mybatis/pom.xml b/blade-core-mybatis/pom.xml index 2aa136f..1a51835 100644 --- a/blade-core-mybatis/pom.xml +++ b/blade-core-mybatis/pom.xml @@ -5,7 +5,7 @@ blade-tool org.springblade - 2.1.1 + 2.2.0 4.0.0 diff --git a/blade-core-secure/pom.xml b/blade-core-secure/pom.xml index d8addd5..a63b07b 100644 --- a/blade-core-secure/pom.xml +++ b/blade-core-secure/pom.xml @@ -5,7 +5,7 @@ blade-tool org.springblade - 2.1.1 + 2.2.0 4.0.0 @@ -28,6 +28,17 @@ blade-core-tool ${blade.tool.version} + + + org.springframework.boot + spring-boot-starter-jdbc + + + tomcat-jdbc + org.apache.tomcat + + + diff --git a/blade-core-secure/src/main/java/org/springblade/core/secure/AuthInfo.java b/blade-core-secure/src/main/java/org/springblade/core/secure/AuthInfo.java index 5eb486f..909d589 100644 --- a/blade-core-secure/src/main/java/org/springblade/core/secure/AuthInfo.java +++ b/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"; } diff --git a/blade-core-secure/src/main/java/org/springblade/core/secure/BladeUser.java b/blade-core-secure/src/main/java/org/springblade/core/secure/BladeUser.java index f10d2a9..2710976 100644 --- a/blade-core-secure/src/main/java/org/springblade/core/secure/BladeUser.java +++ b/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 diff --git a/blade-core-secure/src/main/java/org/springblade/core/secure/annotation/PreAuth.java b/blade-core-secure/src/main/java/org/springblade/core/secure/annotation/PreAuth.java index 6cbd96a..1927cfc 100644 --- a/blade-core-secure/src/main/java/org/springblade/core/secure/annotation/PreAuth.java +++ b/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) diff --git a/blade-core-secure/src/main/java/org/springblade/core/secure/config/RegistryConfiguration.java b/blade-core-secure/src/main/java/org/springblade/core/secure/config/RegistryConfiguration.java index 9fd4d16..5c7fa37 100644 --- a/blade-core-secure/src/main/java/org/springblade/core/secure/config/RegistryConfiguration.java +++ b/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 */ diff --git a/blade-core-secure/src/main/java/org/springblade/core/secure/config/SecureConfiguration.java b/blade-core-secure/src/main/java/org/springblade/core/secure/config/SecureConfiguration.java index 8e6917e..9f6df94 100644 --- a/blade-core-secure/src/main/java/org/springblade/core/secure/config/SecureConfiguration.java +++ b/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); + } + } diff --git a/blade-core-secure/src/main/java/org/springblade/core/secure/constant/SecureConstant.java b/blade-core-secure/src/main/java/org/springblade/core/secure/constant/SecureConstant.java new file mode 100644 index 0000000..f0f7534 --- /dev/null +++ b/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). + *

+ * 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 + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * 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 = ?"; + +} diff --git a/blade-core-secure/src/main/java/org/springblade/core/secure/exception/SecureException.java b/blade-core-secure/src/main/java/org/springblade/core/secure/exception/SecureException.java index 18c689b..ddeecfd 100644 --- a/blade-core-secure/src/main/java/org/springblade/core/secure/exception/SecureException.java +++ b/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) { diff --git a/blade-core-secure/src/main/java/org/springblade/core/secure/interceptor/ClientInterceptor.java b/blade-core-secure/src/main/java/org/springblade/core/secure/interceptor/ClientInterceptor.java new file mode 100644 index 0000000..a39c925 --- /dev/null +++ b/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). + *

+ * 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 + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * 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; + } + } + +} diff --git a/blade-core-secure/src/main/java/org/springblade/core/secure/interceptor/SecureInterceptor.java b/blade-core-secure/src/main/java/org/springblade/core/secure/interceptor/SecureInterceptor.java index 0c792b2..37f0d9c 100644 --- a/blade-core-secure/src/main/java/org/springblade/core/secure/interceptor/SecureInterceptor.java +++ b/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 { diff --git a/blade-core-secure/src/main/java/org/springblade/core/secure/props/BladeClientProperties.java b/blade-core-secure/src/main/java/org/springblade/core/secure/props/BladeClientProperties.java new file mode 100644 index 0000000..668f92a --- /dev/null +++ b/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). + *

+ * 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 + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * 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 client = new ArrayList<>(); + +} diff --git a/blade-core-secure/src/main/java/org/springblade/core/secure/props/BladeSecureProperties.java b/blade-core-secure/src/main/java/org/springblade/core/secure/props/BladeSecureProperties.java new file mode 100644 index 0000000..7ee3766 --- /dev/null +++ b/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). + *

+ * 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 + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * 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 excludePatterns = new ArrayList<>(); + +} diff --git a/blade-core-secure/src/main/java/org/springblade/core/secure/props/ClientSecure.java b/blade-core-secure/src/main/java/org/springblade/core/secure/props/ClientSecure.java new file mode 100644 index 0000000..e22481a --- /dev/null +++ b/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). + *

+ * 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 + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * 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 pathPatterns = new ArrayList<>(); + +} diff --git a/blade-core-secure/src/main/java/org/springblade/core/secure/provider/ClientDetails.java b/blade-core-secure/src/main/java/org/springblade/core/secure/provider/ClientDetails.java new file mode 100644 index 0000000..6df96b1 --- /dev/null +++ b/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). + *

+ * 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 + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * 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; + +} diff --git a/blade-core-secure/src/main/java/org/springblade/core/secure/provider/ClientDetailsServiceImpl.java b/blade-core-secure/src/main/java/org/springblade/core/secure/provider/ClientDetailsServiceImpl.java new file mode 100644 index 0000000..66be625 --- /dev/null +++ b/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). + *

+ * 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 + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * 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; + } + } + +} diff --git a/blade-core-secure/src/main/java/org/springblade/core/secure/provider/IClientDetails.java b/blade-core-secure/src/main/java/org/springblade/core/secure/provider/IClientDetails.java new file mode 100644 index 0000000..eb0b191 --- /dev/null +++ b/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). + *

+ * 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 + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * 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(); + +} diff --git a/blade-core-secure/src/main/java/org/springblade/core/secure/provider/IClientDetailsService.java b/blade-core-secure/src/main/java/org/springblade/core/secure/provider/IClientDetailsService.java new file mode 100644 index 0000000..52d3803 --- /dev/null +++ b/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). + *

+ * 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 + *

+ * http://www.gnu.org/licenses/lgpl.html + *

+ * 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); + +} diff --git a/blade-core-secure/src/main/java/org/springblade/core/secure/registry/SecureRegistry.java b/blade-core-secure/src/main/java/org/springblade/core/secure/registry/SecureRegistry.java index cdfe955..0f4544b 100644 --- a/blade-core-secure/src/main/java/org/springblade/core/secure/registry/SecureRegistry.java +++ b/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 patterns) { this.excludePatterns.addAll(patterns); diff --git a/blade-core-secure/src/main/java/org/springblade/core/secure/utils/SecureUtil.java b/blade-core-secure/src/main/java/org/springblade/core/secure/utils/SecureUtil.java index 9d00da6..b353b6b 100644 --- a/blade-core-secure/src/main/java/org/springblade/core/secure/utils/SecureUtil.java +++ b/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_"; + private 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 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 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; + } + } diff --git a/blade-core-swagger/pom.xml b/blade-core-swagger/pom.xml index 9290121..aa30748 100644 --- a/blade-core-swagger/pom.xml +++ b/blade-core-swagger/pom.xml @@ -5,7 +5,7 @@ blade-tool org.springblade - 2.1.1 + 2.2.0 4.0.0 diff --git a/blade-core-tool/pom.xml b/blade-core-tool/pom.xml index 7a48dd1..a15876d 100644 --- a/blade-core-tool/pom.xml +++ b/blade-core-tool/pom.xml @@ -6,7 +6,7 @@ org.springblade blade-tool - 2.1.1 + 2.2.0 4.0.0 diff --git a/blade-core-tool/src/main/java/org/springblade/core/tool/utils/Charsets.java b/blade-core-tool/src/main/java/org/springblade/core/tool/utils/Charsets.java index 5b14e45..f9ef657 100644 --- a/blade-core-tool/src/main/java/org/springblade/core/tool/utils/Charsets.java +++ b/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对象 diff --git a/pom.xml b/pom.xml index 4e98e1a..b127552 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springblade blade-tool - 2.1.1 + 2.2.0 pom blade-tool @@ -36,7 +36,7 @@ - 2.1.1 + 2.2.0 1.8 3.8.0