add openapi service

This commit is contained in:
MaxKey
2023-05-23 10:10:40 +08:00
parent 973d715e47
commit 4b1c88c1a2
30 changed files with 1439 additions and 10 deletions

View File

@@ -0,0 +1,2 @@
Manifest-Version: 1.0

View File

@@ -0,0 +1,82 @@
/*
* Copyright [2020] [MaxKey of copyright http://www.maxkey.top]
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.maxkey;
import javax.servlet.ServletException;
import org.joda.time.DateTime;
import org.maxkey.configuration.ApplicationConfig;
import org.maxkey.web.InitializeContext;
import org.mybatis.spring.annotation.MapperScan;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;
@ComponentScan(basePackages = {
"org.maxkey.authn",
"org.maxkey.configuration",
"org.maxkey.entity",
"org.maxkey.entity.apps",
"org.maxkey.entity.userinfo",
"org.maxkey.web.apis.identity.kafka",
"org.maxkey.web.apis.identity.rest",
"org.maxkey.web.apis.identity.scim",
"org.maxkey.persistence",
"org.maxkey.provision",
"org.maxkey.web",
"org.maxkey.web.api.endpoint",
"org.maxkey.web.contorller",
"org.maxkey.web.endpoint",
"org.maxkey.web.interceptor",
})
@MapperScan("org.maxkey.persistence.mapper,")
@SpringBootApplication
@EnableDiscoveryClient
public class MaxKeyOpenApiApplication extends SpringBootServletInitializer {
private static final Logger _logger = LoggerFactory.getLogger(MaxKeyOpenApiApplication.class);
public static void main(String[] args) {
_logger.info("Start MaxKey OpenApi Application ...");
ConfigurableApplicationContext applicationContext =
SpringApplication.run(MaxKeyOpenApiApplication.class, args);
InitializeContext initWebContext = new InitializeContext(applicationContext);
try {
initWebContext.init(null);
} catch (ServletException e) {
_logger.error("Exception ",e);
}
_logger.info("MaxKey OpenApi at {}" , new DateTime());
_logger.info("MaxKey OpenApi Server Port {}"
,applicationContext.getBean(ApplicationConfig.class).getPort());
_logger.info("MaxKey OpenApi started.");
}
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(MaxKeyOpenApiApplication.class);
}
}

View File

@@ -0,0 +1,73 @@
/*
* Copyright [2022] [MaxKey of copyright http://www.maxkey.top]
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.maxkey.autoconfigure;
import org.maxkey.password.onetimepwd.AbstractOtpAuthn;
import org.maxkey.password.onetimepwd.impl.TimeBasedOtpAuthn;
import org.maxkey.persistence.repository.LoginHistoryRepository;
import org.maxkey.persistence.repository.LoginRepository;
import org.maxkey.persistence.repository.PasswordPolicyValidator;
import org.maxkey.persistence.service.UserInfoService;
import org.maxkey.authn.realm.jdbc.JdbcAuthenticationRealm;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.security.crypto.password.PasswordEncoder;
@AutoConfiguration
public class MaxKeyOpenApiConfig implements InitializingBean {
private static final Logger _logger = LoggerFactory.getLogger(MaxKeyOpenApiConfig.class);
//authenticationRealm for MaxKeyMgtApplication
@Bean
public JdbcAuthenticationRealm authenticationRealm(
PasswordEncoder passwordEncoder,
PasswordPolicyValidator passwordPolicyValidator,
LoginRepository loginRepository,
LoginHistoryRepository loginHistoryRepository,
UserInfoService userInfoService,
JdbcTemplate jdbcTemplate) {
JdbcAuthenticationRealm authenticationRealm = new JdbcAuthenticationRealm(
passwordEncoder,
passwordPolicyValidator,
loginRepository,
loginHistoryRepository,
userInfoService,
jdbcTemplate);
_logger.debug("JdbcAuthenticationRealm inited.");
return authenticationRealm;
}
@Bean
public AbstractOtpAuthn timeBasedOtpAuthn() {
AbstractOtpAuthn tfaOtpAuthn = new TimeBasedOtpAuthn();
_logger.debug("TimeBasedOtpAuthn inited.");
return tfaOtpAuthn;
}
@Override
public void afterPropertiesSet() throws Exception {
}
}

View File

@@ -0,0 +1,144 @@
/*
* Copyright [2022] [MaxKey of copyright http://www.maxkey.top]
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.maxkey.autoconfigure;
import java.util.List;
import org.maxkey.authn.provider.AbstractAuthenticationProvider;
import org.maxkey.authn.web.CurrentUserMethodArgumentResolver;
import org.maxkey.authn.web.interceptor.PermissionInterceptor;
import org.maxkey.configuration.ApplicationConfig;
import org.maxkey.web.interceptor.RestApiPermissionAdapter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
@EnableWebMvc
@AutoConfiguration
public class MaxKeyOpenApiMvcConfig implements WebMvcConfigurer {
private static final Logger _logger = LoggerFactory.getLogger(MaxKeyOpenApiMvcConfig.class);
@Autowired
ApplicationConfig applicationConfig;
@Autowired
AbstractAuthenticationProvider authenticationProvider ;
@Autowired
PermissionInterceptor permissionInterceptor;
@Autowired
RestApiPermissionAdapter restApiPermissionAdapter;
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
_logger.debug("add Resource Handlers");
_logger.debug("add statics");
registry.addResourceHandler("/static/**")
.addResourceLocations("classpath:/static/");
_logger.debug("add templates");
registry.addResourceHandler("/templates/**")
.addResourceLocations("classpath:/templates/");
_logger.debug("add swagger");
registry.addResourceHandler("swagger-ui.html")
.addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/");
_logger.debug("add knife4j");
registry.addResourceHandler("doc.html").addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
_logger.debug("add Resource Handler finished .");
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
//addPathPatterns 用于添加拦截规则 先把所有路径都加入拦截, 再一个个排除
//excludePathPatterns 表示改路径不用拦截
_logger.debug("add Interceptors");
permissionInterceptor.setMgmt(true);
registry.addInterceptor(permissionInterceptor)
.addPathPatterns("/dashboard/**")
.addPathPatterns("/orgs/**")
.addPathPatterns("/users/**")
.addPathPatterns("/apps/**")
.addPathPatterns("/session/**")
.addPathPatterns("/accounts/**")
.addPathPatterns("/access/**")
.addPathPatterns("/access/**/**")
.addPathPatterns("/permissions/**")
.addPathPatterns("/permissions/**/**")
.addPathPatterns("/config/**")
.addPathPatterns("/config/**/**")
.addPathPatterns("/historys/**")
.addPathPatterns("/historys/**/**")
.addPathPatterns("/institutions/**")
.addPathPatterns("/localization/**")
.addPathPatterns("/file/upload/")
.addPathPatterns("/logout")
.addPathPatterns("/logout/**")
;
_logger.debug("add Permission Adapter");
/*
* api
* idm
* scim
* */
registry.addInterceptor(restApiPermissionAdapter)
.addPathPatterns("/api/**")
.addPathPatterns("/api/idm/**")
.addPathPatterns("/api/idm/scim/**")
;
_logger.debug("add Rest Api Permission Adapter");
}
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(currentUserMethodArgumentResolver());
}
@Bean
public CurrentUserMethodArgumentResolver currentUserMethodArgumentResolver() {
return new CurrentUserMethodArgumentResolver();
}
}

View File

@@ -0,0 +1,121 @@
/*
* Copyright [2020] [MaxKey of copyright http://www.maxkey.top]
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.maxkey.autoconfigure;
import javax.sql.DataSource;
import org.maxkey.authz.oauth2.provider.client.ClientDetailsUserDetailsService;
import org.maxkey.authz.oauth2.provider.client.JdbcClientDetailsService;
import org.maxkey.authz.oauth2.provider.token.DefaultTokenServices;
import org.maxkey.authz.oauth2.provider.token.TokenStore;
import org.maxkey.authz.oauth2.provider.token.store.InMemoryTokenStore;
import org.maxkey.authz.oauth2.provider.token.store.RedisTokenStore;
import org.maxkey.persistence.redis.RedisConnectionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.crypto.password.PasswordEncoder;
/**
* like Oauth20AutoConfiguration for mgmt
* @author Crystal.Sea
*
*/
@AutoConfiguration
public class Oauth20ClientAutoConfiguration implements InitializingBean {
private static final Logger _logger = LoggerFactory.getLogger(Oauth20ClientAutoConfiguration.class);
@Bean
public JdbcClientDetailsService oauth20JdbcClientDetailsService(
DataSource dataSource,PasswordEncoder passwordReciprocal) {
JdbcClientDetailsService clientDetailsService = new JdbcClientDetailsService(dataSource);
//clientDetailsService.setPasswordEncoder(passwordReciprocal);
_logger.debug("JdbcClientDetailsService inited.");
return clientDetailsService;
}
/**
* TokenStore.
* @param persistence int
* @return oauth20TokenStore
*/
@Bean
public TokenStore oauth20TokenStore(
@Value("${maxkey.server.persistence}") int persistence,
JdbcTemplate jdbcTemplate,
RedisConnectionFactory jedisConnectionFactory) {
TokenStore tokenStore = null;
if (persistence == 2) {
tokenStore = new RedisTokenStore(jedisConnectionFactory);
_logger.debug("RedisTokenStore");
}else {
tokenStore = new InMemoryTokenStore();
_logger.debug("InMemoryTokenStore");
}
return tokenStore;
}
/**
* clientDetailsUserDetailsService.
* @return oauth20TokenServices
*/
@Bean
public DefaultTokenServices oauth20TokenServices(
JdbcClientDetailsService oauth20JdbcClientDetailsService,
TokenStore oauth20TokenStore) {
DefaultTokenServices tokenServices = new DefaultTokenServices();
tokenServices.setClientDetailsService(oauth20JdbcClientDetailsService);
tokenServices.setTokenStore(oauth20TokenStore);
tokenServices.setSupportRefreshToken(true);
return tokenServices;
}
/**
* ProviderManager.
* @return oauth20ClientAuthenticationManager
*/
@Bean
public ProviderManager oauth20ClientAuthenticationManager(
JdbcClientDetailsService oauth20JdbcClientDetailsService,
PasswordEncoder passwordReciprocal
) {
ClientDetailsUserDetailsService cientDetailsUserDetailsService =
new ClientDetailsUserDetailsService(oauth20JdbcClientDetailsService);
DaoAuthenticationProvider daoAuthenticationProvider= new DaoAuthenticationProvider();
daoAuthenticationProvider.setPasswordEncoder(passwordReciprocal);
daoAuthenticationProvider.setUserDetailsService(cientDetailsUserDetailsService);
ProviderManager authenticationManager = new ProviderManager(daoAuthenticationProvider);
_logger.debug("OAuth 2 Client Authentication Manager init.");
return authenticationManager;
}
@Override
public void afterPropertiesSet() throws Exception {
}
}

View File

@@ -0,0 +1,60 @@
/*
* Copyright [2020] [MaxKey of copyright http://www.maxkey.top]
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.maxkey.web.api.endpoint;
import org.maxkey.entity.UserInfo;
import org.maxkey.password.onetimepwd.AbstractOtpAuthn;
import org.maxkey.persistence.service.UserInfoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
@Tag(name = "基于时间令牌验证 API文档模块")
@Controller
@RequestMapping(value={"/api/otp"})
public class RestTimeBasedOtpController {
@Autowired
protected AbstractOtpAuthn timeBasedOtpAuthn;
@Autowired
private UserInfoService userInfoService;
@Operation(summary = "基于时间令牌验证 API文档模块", description = "传递参数username和token",method="GET")
@ResponseBody
@RequestMapping(value = "/timebased/validate", method = RequestMethod.GET)
public boolean getUser(@RequestParam String username,
@RequestParam String token) {
UserInfo validUserInfo = userInfoService.findByUsername(username);
if(validUserInfo != null) {
if(timeBasedOtpAuthn.validate(validUserInfo, token)) {
return true;
}
}
return false;
}
}

View File

@@ -0,0 +1,80 @@
/*
* Copyright [2020] [MaxKey of copyright http://www.maxkey.top]
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.maxkey.web.interceptor;
import java.util.concurrent.ConcurrentHashMap;
import javax.servlet.RequestDispatcher;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.maxkey.authz.oauth2.provider.OAuth2Authentication;
import org.maxkey.authz.oauth2.provider.token.DefaultTokenServices;
import org.maxkey.crypto.password.PasswordReciprocal;
import org.maxkey.util.RequestTokenUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.AsyncHandlerInterceptor;
/**
* OAuth v2.0 accessToken认证Interceptor处理.
* @author Crystal.Sea
*
*/
@Component
public class Oauth20ApiPermissionAdapter implements AsyncHandlerInterceptor {
private static final Logger _logger = LoggerFactory.getLogger(Oauth20ApiPermissionAdapter.class);
@Autowired
protected PasswordReciprocal passwordReciprocal;
@Autowired
private DefaultTokenServices oauth20TokenServices;
static ConcurrentHashMap<String ,String >navigationsMap=null;
/*
* 请求前处理
* (non-Javadoc)
* @see org.springframework.web.servlet.handler.HandlerInterceptorAdapter#preHandle(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, java.lang.Object)
*/
@Override
public boolean preHandle(HttpServletRequest request,HttpServletResponse response, Object handler) throws Exception {
_logger.trace("OAuth20 API Permission Adapter pre handle");
String accessToken = RequestTokenUtils.resolveAccessToken(request);
_logger.trace("access_token {} " , accessToken);
try {
OAuth2Authentication authentication = oauth20TokenServices.loadAuthentication(accessToken);
//判断应用的accessToken信息
if(authentication != null ){
_logger.trace("authentication "+ authentication);
return true;
}
}catch(Exception e) {
_logger.error("load Authentication Exception ! ",e);
}
_logger.trace("No Authentication ... forward to /login");
RequestDispatcher dispatcher = request.getRequestDispatcher("/login");
dispatcher.forward(request, response);
return false;
}
}

View File

@@ -0,0 +1,112 @@
/*
* Copyright [2020] [MaxKey of copyright http://www.maxkey.top]
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.maxkey.web.interceptor;
import java.util.concurrent.ConcurrentHashMap;
import javax.servlet.RequestDispatcher;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.maxkey.authn.web.AuthorizationUtils;
import org.maxkey.authz.oauth2.provider.OAuth2Authentication;
import org.maxkey.authz.oauth2.provider.token.DefaultTokenServices;
import org.maxkey.util.AuthorizationHeader;
import org.maxkey.util.AuthorizationHeaderUtils;
import org.maxkey.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.userdetails.User;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.AsyncHandlerInterceptor;
/**
* basic认证Interceptor处理.
* @author Crystal.Sea
*
*/
@Component
public class RestApiPermissionAdapter implements AsyncHandlerInterceptor {
private static final Logger _logger = LoggerFactory.getLogger(RestApiPermissionAdapter.class);
@Autowired
DefaultTokenServices oauth20TokenServices;
@Autowired
ProviderManager oauth20ClientAuthenticationManager;
static ConcurrentHashMap<String ,String >navigationsMap=null;
/*
* 请求前处理
* (non-Javadoc)
* @see org.springframework.web.servlet.handler.HandlerInterceptorAdapter#preHandle(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, java.lang.Object)
*/
@Override
public boolean preHandle(HttpServletRequest request,HttpServletResponse response, Object handler) throws Exception {
_logger.trace("Rest API Permission Adapter pre handle");
AuthorizationHeader headerCredential = AuthorizationHeaderUtils.resolve(request);
//判断应用的AppId和Secret
if(headerCredential != null){
UsernamePasswordAuthenticationToken authenticationToken = null;
if(headerCredential.isBasic()) {
if(StringUtils.isNotBlank(headerCredential.getUsername())&&
StringUtils.isNotBlank(headerCredential.getCredential())
) {
UsernamePasswordAuthenticationToken authRequest =
new UsernamePasswordAuthenticationToken(
headerCredential.getUsername(),
headerCredential.getCredential());
authenticationToken= (UsernamePasswordAuthenticationToken)oauth20ClientAuthenticationManager.authenticate(authRequest);
}
}else {
_logger.trace("Authentication bearer {}" , headerCredential.getCredential());
OAuth2Authentication oauth2Authentication =
oauth20TokenServices.loadAuthentication(headerCredential.getCredential());
if(oauth2Authentication != null) {
_logger.trace("Authentication token {}" , oauth2Authentication.getPrincipal().toString());
authenticationToken= new UsernamePasswordAuthenticationToken(
new User(
oauth2Authentication.getPrincipal().toString(),
"CLIENT_SECRET",
oauth2Authentication.getAuthorities()),
"PASSWORD",
oauth2Authentication.getAuthorities()
);
}else {
_logger.trace("Authentication token is null ");
}
}
if(authenticationToken !=null && authenticationToken.isAuthenticated()) {
AuthorizationUtils.setAuthentication(authenticationToken);
return true;
}
}
_logger.trace("No Authentication ... forward to /login");
RequestDispatcher dispatcher = request.getRequestDispatcher("/login");
dispatcher.forward(request, response);
return false;
}
}