feat: 初始化

This commit is contained in:
2024-04-09 11:34:46 +08:00
commit 39f3acc15f
3209 changed files with 253442 additions and 0 deletions
@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>febs-common</artifactId>
<groupId>com.yida.data</groupId>
<version>2.2-RELEASE</version>
</parent>
<artifactId>febs-common-security-starter</artifactId>
<name>FEBS-Common-Security-Starter</name>
<description>FEBS-Common-Security-Starter安全模块</description>
<dependencies>
<dependency>
<groupId>com.yida.data</groupId>
<artifactId>febs-common-core</artifactId>
<version>${febs-cloud.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-security</artifactId>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,16 @@
package cc.mrbird.febs.common.security.starter.annotation;
import cc.mrbird.febs.common.security.starter.configure.FebsCloudResourceServerConfigure;
import org.springframework.context.annotation.Import;
import java.lang.annotation.*;
/**
* @author MrBird
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(FebsCloudResourceServerConfigure.class)
public @interface EnableFebsCloudResourceServer {
}
@@ -0,0 +1,84 @@
package cc.mrbird.febs.common.security.starter.configure;
import com.yida.data.common.core.entity.constant.EndpointConstant;
import com.yida.data.common.core.entity.constant.StringConstant;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import cc.mrbird.febs.common.security.starter.handler.FebsAccessDeniedHandler;
import cc.mrbird.febs.common.security.starter.handler.FebsAuthExceptionEntryPoint;
import cc.mrbird.febs.common.security.starter.properties.FebsCloudSecurityProperties;
/**
* 资源配置
*/
@EnableResourceServer
@EnableAutoConfiguration(exclude = UserDetailsServiceAutoConfiguration.class)
public class FebsCloudResourceServerConfigure extends ResourceServerConfigurerAdapter {
private FebsCloudSecurityProperties properties;
private FebsAccessDeniedHandler accessDeniedHandler;
private FebsAuthExceptionEntryPoint exceptionEntryPoint;
@Autowired(required = false)
public void setProperties(FebsCloudSecurityProperties properties) {
this.properties = properties;
}
@Autowired(required = false)
public void setAccessDeniedHandler(FebsAccessDeniedHandler accessDeniedHandler) {
this.accessDeniedHandler = accessDeniedHandler;
}
@Autowired(required = false)
public void setExceptionEntryPoint(FebsAuthExceptionEntryPoint exceptionEntryPoint) {
this.exceptionEntryPoint = exceptionEntryPoint;
}
@Override
public void configure(HttpSecurity http) throws Exception {
if (properties == null) {
permitAll(http);
return;
}
String[] anonUrls = StringUtils.splitByWholeSeparatorPreserveAllTokens(properties.getAnonUris(), StringConstant.COMMA);
if (ArrayUtils.isEmpty(anonUrls)) {
anonUrls = new String[]{};
}
if (ArrayUtils.contains(anonUrls, EndpointConstant.ALL)) {
permitAll(http);
return;
}
http.csrf().disable()
.requestMatchers().antMatchers(properties.getAuthUri())
.and()
.authorizeRequests()
.antMatchers(anonUrls).permitAll()
.antMatchers(properties.getAuthUri()).authenticated()
.and()
.httpBasic();
}
@Override
public void configure(ResourceServerSecurityConfigurer resources) {
if (exceptionEntryPoint != null) {
resources.authenticationEntryPoint(exceptionEntryPoint);
}
if (accessDeniedHandler != null) {
resources.accessDeniedHandler(accessDeniedHandler);
}
}
private void permitAll(HttpSecurity http) throws Exception {
http.csrf().disable();
http.authorizeRequests().anyRequest().permitAll();
}
}
@@ -0,0 +1,88 @@
package cc.mrbird.febs.common.security.starter.configure;
import com.yida.data.common.core.entity.constant.FebsConstant;
import com.yida.data.common.core.utils.FebsUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.security.oauth2.resource.ResourceServerProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.http.HttpHeaders;
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.provider.expression.OAuth2MethodSecurityExpressionHandler;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.util.Base64Utils;
import cc.mrbird.febs.common.security.starter.handler.FebsAccessDeniedHandler;
import cc.mrbird.febs.common.security.starter.handler.FebsAuthExceptionEntryPoint;
import cc.mrbird.febs.common.security.starter.properties.FebsCloudSecurityProperties;
import feign.RequestInterceptor;
/**
* @author MrBird
*/
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
@EnableConfigurationProperties(FebsCloudSecurityProperties.class)
@ConditionalOnProperty(value = "febs.cloud.security.enable", havingValue = "true", matchIfMissing = true)
public class FebsCloudSecurityAutoConfigure extends GlobalMethodSecurityConfiguration {
@Bean
@ConditionalOnMissingBean(name = "accessDeniedHandler")
public FebsAccessDeniedHandler accessDeniedHandler() {
return new FebsAccessDeniedHandler();
}
@Bean
@ConditionalOnMissingBean(name = "authenticationEntryPoint")
public FebsAuthExceptionEntryPoint authenticationEntryPoint() {
return new FebsAuthExceptionEntryPoint();
}
@Bean
@ConditionalOnMissingBean(value = PasswordEncoder.class)
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public FebsCloudSecurityInteceptorConfigure febsCloudSecurityInteceptorConfigure() {
return new FebsCloudSecurityInteceptorConfigure();
}
@Bean
@Primary
@ConditionalOnMissingBean(DefaultTokenServices.class)
public FebsUserInfoTokenServices febsUserInfoTokenServices(ResourceServerProperties properties) {
return new FebsUserInfoTokenServices(properties.getUserInfoUri(), properties.getClientId());
}
/**
* feign过滤器
*/
@Bean
public RequestInterceptor oauth2FeignRequestInterceptor() {
return requestTemplate -> {
String gatewayToken = new String(Base64Utils.encode(FebsConstant.GATEWAY_TOKEN_VALUE.getBytes()));
requestTemplate.header(FebsConstant.GATEWAY_TOKEN_HEADER, gatewayToken);
if (FebsUtil.isLogin()) {
String authorizationToken = FebsUtil.getCurrentTokenValue();
if (StringUtils.isNotBlank(authorizationToken)) {
requestTemplate.header(HttpHeaders.AUTHORIZATION, FebsConstant.OAUTH2_TOKEN_TYPE + authorizationToken);
}
}
};
}
@Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
return new OAuth2MethodSecurityExpressionHandler();
}
}
@@ -0,0 +1,35 @@
package cc.mrbird.febs.common.security.starter.configure;
import cc.mrbird.febs.common.security.starter.interceptor.FebsServerProtectInterceptor;
import cc.mrbird.febs.common.security.starter.properties.FebsCloudSecurityProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* @author MrBird
*/
public class FebsCloudSecurityInteceptorConfigure implements WebMvcConfigurer {
private FebsCloudSecurityProperties properties;
@Autowired
public void setProperties(FebsCloudSecurityProperties properties) {
this.properties = properties;
}
@Bean
public HandlerInterceptor febsServerProtectInterceptor() {
FebsServerProtectInterceptor febsServerProtectInterceptor = new FebsServerProtectInterceptor();
febsServerProtectInterceptor.setProperties(properties);
return febsServerProtectInterceptor;
}
@Override
@SuppressWarnings("all")
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(febsServerProtectInterceptor());
}
}
@@ -0,0 +1,150 @@
package cc.mrbird.febs.common.security.starter.configure;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.boot.autoconfigure.security.oauth2.resource.AuthoritiesExtractor;
import org.springframework.boot.autoconfigure.security.oauth2.resource.FixedAuthoritiesExtractor;
import org.springframework.boot.autoconfigure.security.oauth2.resource.FixedPrincipalExtractor;
import org.springframework.boot.autoconfigure.security.oauth2.resource.PrincipalExtractor;
import org.springframework.boot.autoconfigure.security.oauth2.resource.UserInfoTokenServices;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.oauth2.client.OAuth2RestOperations;
import org.springframework.security.oauth2.client.OAuth2RestTemplate;
import org.springframework.security.oauth2.client.resource.BaseOAuth2ProtectedResourceDetails;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.InvalidTokenException;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.OAuth2Request;
import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices;
import org.springframework.util.Assert;
import java.io.Serializable;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* 重写UserInfoTokenServices
* {@link UserInfoTokenServices#loadAuthentication(String)}
*
* @author MrBird
*/
public class FebsUserInfoTokenServices implements ResourceServerTokenServices {
protected final Log logger = LogFactory.getLog(this.getClass());
private final String userInfoEndpointUrl;
private final String clientId;
private OAuth2RestOperations restTemplate;
private String tokenType = "Bearer";
private AuthoritiesExtractor authoritiesExtractor = new FixedAuthoritiesExtractor();
private PrincipalExtractor principalExtractor = new FixedPrincipalExtractor();
public FebsUserInfoTokenServices(String userInfoEndpointUrl, String clientId) {
this.userInfoEndpointUrl = userInfoEndpointUrl;
this.clientId = clientId;
}
public void setTokenType(String tokenType) {
this.tokenType = tokenType;
}
public void setRestTemplate(OAuth2RestOperations restTemplate) {
this.restTemplate = restTemplate;
}
public void setAuthoritiesExtractor(AuthoritiesExtractor authoritiesExtractor) {
Assert.notNull(authoritiesExtractor, "AuthoritiesExtractor must not be null");
this.authoritiesExtractor = authoritiesExtractor;
}
public void setPrincipalExtractor(PrincipalExtractor principalExtractor) {
Assert.notNull(principalExtractor, "PrincipalExtractor must not be null");
this.principalExtractor = principalExtractor;
}
@Override
public OAuth2Authentication loadAuthentication(String accessToken) throws AuthenticationException, InvalidTokenException {
Map<String, Object> map = this.getMap(this.userInfoEndpointUrl, accessToken);
String error = "error";
if (map.containsKey(error)) {
if (this.logger.isDebugEnabled()) {
this.logger.debug("userinfo returned error: " + map.get(error));
}
throw new InvalidTokenException(accessToken);
} else {
return this.extractAuthentication(map);
}
}
private OAuth2Authentication extractAuthentication(Map<String, Object> map) {
Object principal = this.getPrincipal(map);
List<GrantedAuthority> authorities = this.authoritiesExtractor.extractAuthorities(map);
String oauth2RequestString = JSONObject.toJSONString(map.get("oauth2Request"));
JSONObject oauth2Request = JSONObject.parseObject(oauth2RequestString);
TypeReference<Set<String>> setTypeReference = new TypeReference<Set<String>>() {
};
Map<String, String> requestParameters = JSONObject.parseObject(oauth2Request.getString("requestParameters"), new TypeReference<Map<String, String>>() {
});
boolean approved = oauth2Request.getBooleanValue("approved");
Set<String> scope = JSONObject.parseObject(oauth2Request.getString("scope"), setTypeReference);
Set<String> resourceIds = JSONObject.parseObject(oauth2Request.getString("resourceIds"), setTypeReference);
String redirectUri = oauth2Request.getString("redirectUri");
Set<String> responseTypes = JSONObject.parseObject(oauth2Request.getString("responseTypes"), setTypeReference);
Map<String, Serializable> extensions = JSONObject.parseObject(oauth2Request.getString("extensions"), new TypeReference<Map<String, Serializable>>() {
});
OAuth2Request request = new OAuth2Request(requestParameters, this.clientId, authorities, approved, scope, resourceIds, redirectUri, responseTypes, extensions);
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(principal, "N/A", authorities);
token.setDetails(map);
return new OAuth2Authentication(request, token);
}
protected Object getPrincipal(Map<String, Object> map) {
Object principal = this.principalExtractor.extractPrincipal(map);
return principal == null ? "unknown" : principal;
}
@Override
public OAuth2AccessToken readAccessToken(String accessToken) {
throw new UnsupportedOperationException("Not supported: read access token");
}
@SuppressWarnings("all")
private Map<String, Object> getMap(String path, String accessToken) {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Getting user info from: " + path);
}
try {
OAuth2RestOperations restTemplate = this.restTemplate;
if (restTemplate == null) {
BaseOAuth2ProtectedResourceDetails resource = new BaseOAuth2ProtectedResourceDetails();
resource.setClientId(this.clientId);
restTemplate = new OAuth2RestTemplate(resource);
}
OAuth2AccessToken existingToken = restTemplate.getOAuth2ClientContext().getAccessToken();
if (existingToken == null || !accessToken.equals(existingToken.getValue())) {
DefaultOAuth2AccessToken token = new DefaultOAuth2AccessToken(accessToken);
token.setTokenType(this.tokenType);
restTemplate.getOAuth2ClientContext().setAccessToken(token);
}
return (Map) restTemplate.getForEntity(path, Map.class, new Object[0]).getBody();
} catch (Exception e) {
this.logger.warn("Could not fetch user details: " + e.getClass() + ", " + e.getMessage());
return Collections.singletonMap("error", "Could not fetch user details");
}
}
}
@@ -0,0 +1,24 @@
package cc.mrbird.febs.common.security.starter.handler;
import com.yida.data.common.core.entity.FebsResponse;
import com.yida.data.common.core.utils.FebsUtil;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 无权限处理
*/
public class FebsAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException {
FebsResponse febsResponse = new FebsResponse();
FebsUtil.makeJsonResponse(response, HttpServletResponse.SC_FORBIDDEN, febsResponse.message("没有权限访问该资源"));
}
}
@@ -0,0 +1,31 @@
package cc.mrbird.febs.common.security.starter.handler;
import com.yida.data.common.core.entity.FebsResponse;
import com.yida.data.common.core.utils.FebsUtil;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
/**
* 认证失败处理
*/
@Slf4j
public class FebsAuthExceptionEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException {
String requestUri = request.getRequestURI();
int status = HttpServletResponse.SC_UNAUTHORIZED;
String message = "访问令牌不合法";
log.error("客户端访问{}请求失败: {}", requestUri, message, authException);
FebsUtil.makeJsonResponse(response, status, new FebsResponse().message(message));
}
}
@@ -0,0 +1,45 @@
package cc.mrbird.febs.common.security.starter.interceptor;
import com.yida.data.common.core.entity.FebsResponse;
import com.yida.data.common.core.entity.constant.FebsConstant;
import com.yida.data.common.core.utils.FebsUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.lang.NonNull;
import org.springframework.util.Base64Utils;
import org.springframework.web.servlet.HandlerInterceptor;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import cc.mrbird.febs.common.security.starter.properties.FebsCloudSecurityProperties;
/**
* 只能通过网关获取资源
*/
public class FebsServerProtectInterceptor implements HandlerInterceptor {
private FebsCloudSecurityProperties properties;
@Override
public boolean preHandle(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response, @NonNull Object handler) throws IOException {
if (!properties.getOnlyFetchByGateway()) {
return true;
}
String token = request.getHeader(FebsConstant.GATEWAY_TOKEN_HEADER);
String gatewayToken = new String(Base64Utils.encode(FebsConstant.GATEWAY_TOKEN_VALUE.getBytes()));
if (StringUtils.equals(gatewayToken, token)) {
return true;
} else {
FebsResponse febsResponse = new FebsResponse();
FebsUtil.makeJsonResponse(response, HttpServletResponse.SC_FORBIDDEN, febsResponse.message("请通过网关获取资源"));
return false;
}
}
public void setProperties(FebsCloudSecurityProperties properties) {
this.properties = properties;
}
}
@@ -0,0 +1,71 @@
package cc.mrbird.febs.common.security.starter.properties;
import com.yida.data.common.core.entity.constant.EndpointConstant;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* @author MrBird
*/
@ConfigurationProperties(prefix = "febs.cloud.security")
public class FebsCloudSecurityProperties {
/**
* 是否开启安全配置
*/
private Boolean enable;
/**
* 配置需要认证的uri,默认为所有/**
*/
private String authUri = EndpointConstant.ALL;
/**
* 免认证资源路径,支持通配符
* 多个值时使用逗号分隔
*/
private String anonUris;
/**
* 是否只能通过网关获取资源
*/
private Boolean onlyFetchByGateway = Boolean.TRUE;
public Boolean getEnable() {
return enable;
}
public void setEnable(Boolean enable) {
this.enable = enable;
}
public String getAuthUri() {
return authUri;
}
public void setAuthUri(String authUri) {
this.authUri = authUri;
}
public String getAnonUris() {
return anonUris;
}
public void setAnonUris(String anonUris) {
this.anonUris = anonUris;
}
public Boolean getOnlyFetchByGateway() {
return onlyFetchByGateway;
}
public void setOnlyFetchByGateway(Boolean onlyFetchByGateway) {
this.onlyFetchByGateway = onlyFetchByGateway;
}
@Override
public String toString() {
return "FebsCloudSecurityProperties{" +
"enable=" + enable +
", authUri='" + authUri + '\'' +
", anonUris='" + anonUris + '\'' +
", onlyFetchByGateway=" + onlyFetchByGateway +
'}';
}
}
@@ -0,0 +1,93 @@
{
"groups": [
{
"name": "febs.doc",
"type": "cc.mrbird.febs.common.doc.starter.properties.FebsDocProperties",
"sourceType": "cc.mrbird.febs.common.doc.starter.properties.FebsDocProperties"
}
],
"properties": [
{
"name": "febs.doc.base-package",
"type": "java.lang.String",
"description": "接口扫描路径,如Controller路径",
"sourceType": "cc.mrbird.febs.common.doc.starter.properties.FebsDocProperties"
},
{
"name": "febs.doc.description",
"type": "java.lang.String",
"description": "文档描述",
"sourceType": "cc.mrbird.febs.common.doc.starter.properties.FebsDocProperties"
},
{
"name": "febs.doc.description-color",
"type": "java.lang.String",
"description": "文档描述颜色",
"sourceType": "cc.mrbird.febs.common.doc.starter.properties.FebsDocProperties",
"defaultValue": "#42b983"
},
{
"name": "febs.doc.description-font-size",
"type": "java.lang.String",
"description": "文档描述字体大小",
"sourceType": "cc.mrbird.febs.common.doc.starter.properties.FebsDocProperties",
"defaultValue": "14"
},
{
"name": "febs.doc.email",
"type": "java.lang.String",
"description": "联系方式:邮箱",
"sourceType": "cc.mrbird.febs.common.doc.starter.properties.FebsDocProperties"
},
{
"name": "febs.doc.enable",
"type": "java.lang.Boolean",
"description": "是否开启doc功能",
"sourceType": "cc.mrbird.febs.common.doc.starter.properties.FebsDocProperties",
"defaultValue": true
},
{
"name": "febs.doc.license",
"type": "java.lang.String",
"description": "协议",
"sourceType": "cc.mrbird.febs.common.doc.starter.properties.FebsDocProperties"
},
{
"name": "febs.doc.license-url",
"type": "java.lang.String",
"description": "协议地址",
"sourceType": "cc.mrbird.febs.common.doc.starter.properties.FebsDocProperties"
},
{
"name": "febs.doc.name",
"type": "java.lang.String",
"description": "联系方式:姓名",
"sourceType": "cc.mrbird.febs.common.doc.starter.properties.FebsDocProperties"
},
{
"name": "febs.doc.terms-of-service-url",
"type": "java.lang.String",
"description": "服务url",
"sourceType": "cc.mrbird.febs.common.doc.starter.properties.FebsDocProperties"
},
{
"name": "febs.doc.title",
"type": "java.lang.String",
"description": "文档标题",
"sourceType": "cc.mrbird.febs.common.doc.starter.properties.FebsDocProperties"
},
{
"name": "febs.doc.url",
"type": "java.lang.String",
"description": "联系方式:个人网站url",
"sourceType": "cc.mrbird.febs.common.doc.starter.properties.FebsDocProperties"
},
{
"name": "febs.doc.version",
"type": "java.lang.String",
"description": "版本",
"sourceType": "cc.mrbird.febs.common.doc.starter.properties.FebsDocProperties"
}
],
"hints": []
}
@@ -0,0 +1,3 @@
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
cc.mrbird.febs.common.security.starter.configure.FebsCloudSecurityAutoConfigure