From e33c6dfd0bf7550bdecd1244ba6aa7f952fc428e Mon Sep 17 00:00:00 2001 From: shimingxy Date: Sat, 16 May 2020 11:54:58 +0800 Subject: [PATCH] v 1.5.0 RC2 v 1.5.0 RC2 --- build.gradle | 1 + gradle.properties | 6 +- .../rememberme/RedisRemeberMeService.java | 5 + .../org/maxkey/config/ApplicationConfig.java | 23 +- .../config/CharacterEncodingConfig.java | 8 +- .../org/maxkey/config/DataSoruceConfig.java | 177 ------------ .../java/org/maxkey/config/EmailConfig.java | 38 +-- .../config/KaptchaAutoConfiguration.java | 36 +++ .../maxkey/config/MvcAutoConfiguration.java | 187 +++++++++++++ .../password/opt/impl/MailOtpAuthn.java | 2 +- .../redis/RedisConnectionFactory.java | 254 +++++++++--------- .../java/org/maxkey/web/ImageEndpoint.java | 174 ------------ .../web/image/AbstractImageEndpoint.java | 103 +++++++ .../web/image/ImageCaptchaEndpoint.java | 76 ++++++ .../org/maxkey/web/image/ImageEndpoint.java | 40 +++ ...atis-jpa-extra-spring-boot-starter-2.0.jar | Bin 0 -> 20855 bytes .../java/org/maxkey/MaxKeyMgtApplication.java | 69 ----- .../main/java/org/maxkey/MaxKeyMgtConfig.java | 80 ++++++ .../main/resources/META-INF/spring.factories | 5 + .../src/main/resources/application.properties | 37 ++- .../config/applicationConfig.properties | 66 +---- .../resources/{config => }/kaptcha.properties | 0 .../spring/maxkey-mgt-persistence.xml | 90 ------- .../main/resources/spring/maxkey-mgt-web.xml | 131 --------- .../src/main/resources/spring/maxkey-mgt.xml | 23 +- .../main/java/org/maxkey/MaxKeyConfig.java | 94 +++++-- .../org/maxkey/RedisAutoConfiguration.java | 50 ++++ .../contorller/OneTimePasswordController.java | 2 +- .../contorller/RegistrationController.java | 2 +- .../main/resources/META-INF/spring.factories | 5 + .../src/main/resources/application.properties | 48 +++- .../config/applicationConfig.properties | 73 +---- .../resources/{config => }/kaptcha.properties | 0 .../resources/spring/maxkey-persistence.xml | 103 ------- .../main/resources/spring/maxkey-security.xml | 20 +- .../src/main/resources/spring/maxkey-web.xml | 108 -------- .../src/main/resources/spring/maxkey.xml | 4 - 37 files changed, 931 insertions(+), 1209 deletions(-) delete mode 100644 maxkey-core/src/main/java/org/maxkey/config/DataSoruceConfig.java create mode 100644 maxkey-core/src/main/java/org/maxkey/config/KaptchaAutoConfiguration.java create mode 100644 maxkey-core/src/main/java/org/maxkey/config/MvcAutoConfiguration.java delete mode 100644 maxkey-core/src/main/java/org/maxkey/web/ImageEndpoint.java create mode 100644 maxkey-core/src/main/java/org/maxkey/web/image/AbstractImageEndpoint.java create mode 100644 maxkey-core/src/main/java/org/maxkey/web/image/ImageCaptchaEndpoint.java create mode 100644 maxkey-core/src/main/java/org/maxkey/web/image/ImageEndpoint.java create mode 100644 maxkey-lib/mybatis-jpa-extra-spring-boot-starter-2.0.jar create mode 100644 maxkey-web-manage/src/main/resources/META-INF/spring.factories rename maxkey-web-manage/src/main/resources/{config => }/kaptcha.properties (100%) delete mode 100644 maxkey-web-manage/src/main/resources/spring/maxkey-mgt-persistence.xml delete mode 100644 maxkey-web-manage/src/main/resources/spring/maxkey-mgt-web.xml create mode 100644 maxkey-web-maxkey/src/main/java/org/maxkey/RedisAutoConfiguration.java create mode 100644 maxkey-web-maxkey/src/main/resources/META-INF/spring.factories rename maxkey-web-maxkey/src/main/resources/{config => }/kaptcha.properties (100%) delete mode 100644 maxkey-web-maxkey/src/main/resources/spring/maxkey-persistence.xml delete mode 100644 maxkey-web-maxkey/src/main/resources/spring/maxkey-web.xml diff --git a/build.gradle b/build.gradle index 4a8cc6ca..e3e0eef8 100644 --- a/build.gradle +++ b/build.gradle @@ -248,6 +248,7 @@ subprojects { //database compile group: 'mysql', name: 'mysql-connector-java', version: '5.1.48' compile group: 'com.alibaba', name: 'druid', version: '1.1.21' + compile group: 'com.alibaba', name: 'druid-spring-boot-starter', version: '1.1.21' compile group: 'redis.clients', name: 'jedis', version: '3.2.0' compile group: 'org.ehcache', name: 'ehcache', version: '3.8.1' //mybatis diff --git a/gradle.properties b/gradle.properties index 0d7631fe..615601e9 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,9 +5,9 @@ author =shimingxy #Version For use jar jackson2Version =2.9.8 log4jVersion =2.13.2 -springVersion =5.2.5.RELEASE -springBootVersion =2.2.6.RELEASE -springSecurityVersion =5.3.0.RELEASE +springVersion =5.2.6.RELEASE +springBootVersion =2.3.0.RELEASE +springSecurityVersion =5.3.2.RELEASE hibernateVersion =6.1.2.Final slf4jVersion =1.7.30 jacksonVersion =2.10.3 diff --git a/maxkey-core/src/main/java/org/maxkey/authn/support/rememberme/RedisRemeberMeService.java b/maxkey-core/src/main/java/org/maxkey/authn/support/rememberme/RedisRemeberMeService.java index 9ac8ca90..b907a214 100644 --- a/maxkey-core/src/main/java/org/maxkey/authn/support/rememberme/RedisRemeberMeService.java +++ b/maxkey-core/src/main/java/org/maxkey/authn/support/rememberme/RedisRemeberMeService.java @@ -41,4 +41,9 @@ public class RedisRemeberMeService extends AbstractRemeberMeService { conn.close(); } + public void setConnectionFactory(RedisConnectionFactory connectionFactory) { + this.connectionFactory = connectionFactory; + } + + } diff --git a/maxkey-core/src/main/java/org/maxkey/config/ApplicationConfig.java b/maxkey-core/src/main/java/org/maxkey/config/ApplicationConfig.java index 932d2a68..f675f410 100644 --- a/maxkey-core/src/main/java/org/maxkey/config/ApplicationConfig.java +++ b/maxkey-core/src/main/java/org/maxkey/config/ApplicationConfig.java @@ -21,8 +21,7 @@ import org.springframework.stereotype.Component; @PropertySource("classpath:/config/applicationConfig.properties") public class ApplicationConfig { private static final Logger _logger = LoggerFactory.getLogger(ApplicationConfig.class); - @Autowired - DataSoruceConfig dataSoruceConfig; + @Autowired EmailConfig emailConfig; @Autowired @@ -45,8 +44,8 @@ public class ApplicationConfig { @Value("${config.server.default.uri}") String defaultUri; - @Value("${config.server.manage.uri}") - String manageUri; + @Value("${config.server.management.uri}") + String managementUri; /* * //is enable whiteList for ipAddress filter boolean whiteList; @@ -80,14 +79,6 @@ public class ApplicationConfig { } - public DataSoruceConfig getDataSoruceConfig() { - return dataSoruceConfig; - } - - public void setDataSoruceConfig(DataSoruceConfig dataSoruceConfig) { - this.dataSoruceConfig = dataSoruceConfig; - } - /** * @return the characterEncodingConfig */ @@ -179,12 +170,12 @@ public class ApplicationConfig { this.emailConfig = emailConfig; } - public String getManageUri() { - return manageUri; + public String getManagementUri() { + return managementUri; } - public void setManageUri(String manageUri) { - this.manageUri = manageUri; + public void setManagementUri(String managementUri) { + this.managementUri = managementUri; } public String getDefaultUri() { diff --git a/maxkey-core/src/main/java/org/maxkey/config/CharacterEncodingConfig.java b/maxkey-core/src/main/java/org/maxkey/config/CharacterEncodingConfig.java index 6bfaf4e7..5e5fbd73 100644 --- a/maxkey-core/src/main/java/org/maxkey/config/CharacterEncodingConfig.java +++ b/maxkey-core/src/main/java/org/maxkey/config/CharacterEncodingConfig.java @@ -12,25 +12,25 @@ import org.springframework.context.annotation.PropertySource; * */ @Configuration -@PropertySource("classpath:/config/applicationConfig.properties") +@PropertySource("classpath:/application.properties") public class CharacterEncodingConfig { /** * 源字符集. */ - @Value("${config.characterencoding.charset.from}") + @Value("${server.servlet.encoding.charset.from:UTF-8}") String fromCharSet; /** * 目标字符集. */ - @Value("${config.characterencoding.charset.to}") + @Value("${server.servlet.encoding.charset:UTF-8}") String toCharSet; /** * 转换标志. */ - @Value("${config.characterencoding.encoding}") + @Value("${server.servlet.encoding.enabled:false}") boolean encoding = false; public CharacterEncodingConfig() { diff --git a/maxkey-core/src/main/java/org/maxkey/config/DataSoruceConfig.java b/maxkey-core/src/main/java/org/maxkey/config/DataSoruceConfig.java deleted file mode 100644 index bbc0419e..00000000 --- a/maxkey-core/src/main/java/org/maxkey/config/DataSoruceConfig.java +++ /dev/null @@ -1,177 +0,0 @@ -package org.maxkey.config; - -import org.apache.commons.logging.LogFactory; -import org.apache.mybatis.jpa.dialect.Dialect; -import org.maxkey.crypto.password.PasswordReciprocal; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.PropertySource; - -/* - * 数据源配置. - * - * @author Crystal.Sea - * dataSource.driverClassName=com.mysql.jdbc.Driver - * dataSource.url=jdbc:mysql://192.168.1.49/parasecdb?autoReconnect=true&characterEncoding=UTF-8 - * dataSource.username=root - * dataSource.password=connsec - * dataSource.type=mysql - * - */ -@Configuration -@PropertySource("classpath:/config/applicationConfig.properties") -public class DataSoruceConfig { - - /* - * 数据库类型 - */ - @Value("${config.datasource.database:mysql}") - String database; - /* - * jdbc驱动类 - */ - @Value("${config.datasource.driverclass:com.mysql.jdbc.Driver}") - String driverClass; - /* - * jdbc连接地址 - */ - @Value("${config.datasource.url:" - + "jdbc:mysql://localhost/maxkey?autoReconnect=true&characterEncoding=UTF-8}") - String url; - /* - * 数据库用户名 - */ - @Value("${config.datasource.username:root}") - String username; - /* - * 数据库密码 - */ - @Value("${config.datasource.password:maxkey}") - String password; - - /* - * 数据库密码是否加密 - */ - @Value("${config.datasource.password.encrypt}") - boolean encrypt = false; - - /* - * 数据库dialect for mybatis - */ - String dialect; - - public DataSoruceConfig() { - super(); - } - - public String getUrl() { - return url; - } - - public void setUrl(String url) { - this.url = url; - } - - public String getUsername() { - return username; - } - - public void setUsername(String username) { - this.username = username; - } - - /** - * 取得数据库密码 如果是加密密码(encrypt==true),则进行解密. - * - * @return decodePassword - */ - public String getPassword() { - String decodePassword = ""; - LogFactory.getLog(DataSoruceConfig.class).debug("password is " + password); - if (encrypt) { - decodePassword = PasswordReciprocal.getInstance().decoder(password); - } else { - decodePassword = password; - } - LogFactory.getLog(DataSoruceConfig.class) - .debug("password is " + password + " , decodePassword is " + decodePassword); - return decodePassword; - } - - public void setPassword(String password) { - this.password = password; - } - - /* - * - * @return the database - */ - public String getDatabase() { - return database; - } - - /* - * @param database the database to set - */ - public void setDatabase(String database) { - this.database = database; - - } - - /* - * @return the driverClass - */ - public String getDriverClass() { - return driverClass; - } - - /* - * @param driverClass the driverClass to set - */ - public void setDriverClass(String driverClass) { - this.driverClass = driverClass; - } - - public boolean isEncrypt() { - return encrypt; - } - - public void setEncrypt(boolean encrypt) { - this.encrypt = encrypt; - } - - /** - * getDialect. - * @return the dialect - */ - public String getDialect() { - if (this.dialect == null) { - this.dialect = Dialect.getDialectMap().get(database); - } - return dialect; - } - - /* - * @param dialect the dialect to set - */ - public void setDialect(String dialect) { - this.dialect = dialect; - } - - /* - * (non-Javadoc) - * - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - return "DataSoruceConfig [database=" + database - + ", driverClass=" + driverClass - + ", url=" + url - + ", username=" + username - + ", password=" + password - + ", encrypt=" + encrypt - + "]"; - } - -} diff --git a/maxkey-core/src/main/java/org/maxkey/config/EmailConfig.java b/maxkey-core/src/main/java/org/maxkey/config/EmailConfig.java index d078398f..1b50a1d0 100644 --- a/maxkey-core/src/main/java/org/maxkey/config/EmailConfig.java +++ b/maxkey-core/src/main/java/org/maxkey/config/EmailConfig.java @@ -5,24 +5,28 @@ import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; @Configuration -@PropertySource("classpath:/config/applicationConfig.properties") +@PropertySource("classpath:/application.properties") public class EmailConfig { - @Value("${config.email.username}") + @Value("${spring.mail.username}") private String username; - @Value("${config.email.password}") + + @Value("${spring.mail.password}") private String password; - @Value("${config.email.smtpHost}") + + @Value("${spring.mail.host}") private String smtpHost; - @Value("${config.email.senderMail}") - private String senderMail; - @Value("${config.email.port}") + + @Value("${spring.mail.port}") private Integer port; - @Value("${config.email.ssl}") + + @Value("${spring.mail.properties.ssl}") private boolean ssl; + + @Value("${spring.mail.properties.sender}") + private String sender; public EmailConfig() { - // TODO Auto-generated constructor stub } /* @@ -67,18 +71,14 @@ public class EmailConfig { this.smtpHost = smtpHost; } - /* - * @return the senderMail - */ - public String getSenderMail() { - return senderMail; + + + public String getSender() { + return sender; } - /* - * @param senderMail the senderMail to set - */ - public void setSenderMail(String senderMail) { - this.senderMail = senderMail; + public void setSender(String sender) { + this.sender = sender; } /* diff --git a/maxkey-core/src/main/java/org/maxkey/config/KaptchaAutoConfiguration.java b/maxkey-core/src/main/java/org/maxkey/config/KaptchaAutoConfiguration.java new file mode 100644 index 00000000..993a2224 --- /dev/null +++ b/maxkey-core/src/main/java/org/maxkey/config/KaptchaAutoConfiguration.java @@ -0,0 +1,36 @@ +package org.maxkey.config; + +import com.google.code.kaptcha.Producer; +import com.google.code.kaptcha.impl.DefaultKaptcha; +import com.google.code.kaptcha.util.Config; +import java.io.IOException; +import java.util.Properties; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.ClassPathResource; +import org.springframework.core.io.Resource; + + +@Configuration +public class KaptchaAutoConfiguration { + private static final Logger _logger = LoggerFactory.getLogger(KaptchaAutoConfiguration.class); + + /** + * Captcha Producer Config . + * @return Producer + * @throws IOException kaptcha.properties is null + */ + @Bean (name = "captchaProducer") + public Producer captchaProducer() throws IOException { + Resource resource = new ClassPathResource("/kaptcha.properties"); + _logger.debug("Kaptcha config file " + resource.getURL()); + DefaultKaptcha kaptcha = new DefaultKaptcha(); + Properties properties = new Properties(); + properties.load(resource.getInputStream()); + Config config = new Config(properties); + kaptcha.setConfig(config); + return kaptcha; + } +} diff --git a/maxkey-core/src/main/java/org/maxkey/config/MvcAutoConfiguration.java b/maxkey-core/src/main/java/org/maxkey/config/MvcAutoConfiguration.java new file mode 100644 index 00000000..ddaf1b68 --- /dev/null +++ b/maxkey-core/src/main/java/org/maxkey/config/MvcAutoConfiguration.java @@ -0,0 +1,187 @@ +package org.maxkey.config; + +import java.util.ArrayList; +import java.util.List; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.PropertySource; +import org.springframework.context.support.ReloadableResourceBundleMessageSource; +import org.springframework.http.MediaType; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.http.converter.xml.MarshallingHttpMessageConverter; +import org.springframework.oxm.jaxb.Jaxb2Marshaller; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.multipart.commons.CommonsMultipartResolver; +import org.springframework.web.servlet.i18n.CookieLocaleResolver; +import org.springframework.web.servlet.i18n.LocaleChangeInterceptor; +import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter; +import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; + + +@Configuration +@PropertySource("classpath:/application.properties") +@PropertySource("classpath:/config/applicationConfig.properties") +public class MvcAutoConfiguration { + private static final Logger _logger = LoggerFactory.getLogger(MvcAutoConfiguration.class); + + @Value("${config.server.domain.sub}") + String subDomainName; + @Value("${spring.servlet.multipart.max-file-size:4194304}") + int maxUploadSize; + @Value("${spring.messages.basename:classpath:messages/message}") + String messagesBasename; + + /** + * cookieLocaleResolver . + * @return cookieLocaleResolver + */ + @Bean (name = "localeResolver") + public CookieLocaleResolver cookieLocaleResolver() { + _logger.debug("subDomainName " + subDomainName); + CookieLocaleResolver cookieLocaleResolver = new CookieLocaleResolver(); + cookieLocaleResolver.setCookieName("maxkey_lang"); + cookieLocaleResolver.setCookieDomain(subDomainName); + cookieLocaleResolver.setCookieMaxAge(604800); + return cookieLocaleResolver; + } + + /** + * 消息处理,可以直接使用properties的key值,返回的是对应的value值 + * messageSource . + * @return messageSource + */ + @Bean (name = "messageSource") + public ReloadableResourceBundleMessageSource reloadableResourceBundleMessageSource() { + _logger.debug("Basename " + messagesBasename); + ReloadableResourceBundleMessageSource messageSource = + new ReloadableResourceBundleMessageSource(); + messageSource.setBasename(messagesBasename); + messageSource.setUseCodeAsDefaultMessage(false); + return messageSource; + } + + /** + * Locale Change Interceptor and Resolver definition . + * @return localeChangeInterceptor + */ + //@Primary + @Bean (name = "localeChangeInterceptor") + public LocaleChangeInterceptor localeChangeInterceptor() { + LocaleChangeInterceptor localeChangeInterceptor = + new LocaleChangeInterceptor(); + localeChangeInterceptor.setParamName("language"); + return localeChangeInterceptor; + } + + /** + * upload file support . + * @return multipartResolver + */ + @Bean (name = "multipartResolver") + public CommonsMultipartResolver commonsMultipartResolver() { + _logger.debug("maxUploadSize " + maxUploadSize); + CommonsMultipartResolver multipartResolver = + new CommonsMultipartResolver(); + multipartResolver.setMaxUploadSize(maxUploadSize); + return multipartResolver; + } + + /** + * handlerMapping . + * @return handlerMapping + */ + @Bean (name = "handlerMapping") + public RequestMappingHandlerMapping requestMappingHandlerMapping( + LocaleChangeInterceptor localeChangeInterceptor) { + RequestMappingHandlerMapping requestMappingHandlerMapping = + new RequestMappingHandlerMapping(); + requestMappingHandlerMapping.setInterceptors(localeChangeInterceptor); + return requestMappingHandlerMapping; + } + + /** + * jaxb2Marshaller . + * @return jaxb2Marshaller + */ + @Bean (name = "jaxb2Marshaller") + public Jaxb2Marshaller jaxb2Marshaller() { + Jaxb2Marshaller jaxb2Marshaller = new Jaxb2Marshaller(); + jaxb2Marshaller.setClassesToBeBound(org.maxkey.domain.xml.UserInfoXML.class);; + return jaxb2Marshaller; + } + + /** + * marshallingHttpMessageConverter . + * @return marshallingHttpMessageConverter + */ + @Bean (name = "marshallingHttpMessageConverter") + public MarshallingHttpMessageConverter marshallingHttpMessageConverter( + Jaxb2Marshaller jaxb2Marshaller) { + MarshallingHttpMessageConverter marshallingHttpMessageConverter = + new MarshallingHttpMessageConverter(); + marshallingHttpMessageConverter.setMarshaller(jaxb2Marshaller); + marshallingHttpMessageConverter.setUnmarshaller(jaxb2Marshaller); + ArrayList mediaTypesList = new ArrayList(); + mediaTypesList.add(MediaType.APPLICATION_XML); + marshallingHttpMessageConverter.setSupportedMediaTypes(mediaTypesList); + return marshallingHttpMessageConverter; + } + + /** + * mappingJacksonHttpMessageConverter . + * @return mappingJacksonHttpMessageConverter + */ + @Bean (name = "mappingJacksonHttpMessageConverter") + public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() { + MappingJackson2HttpMessageConverter mappingJacksonHttpMessageConverter = + new MappingJackson2HttpMessageConverter(); + ArrayList mediaTypesList = new ArrayList(); + mediaTypesList.add(MediaType.APPLICATION_JSON); + mappingJacksonHttpMessageConverter.setSupportedMediaTypes(mediaTypesList); + return mappingJacksonHttpMessageConverter; + } + + /** + * AnnotationMethodHandlerAdapter + * requestMappingHandlerAdapter . + * @return requestMappingHandlerAdapter + */ + @Bean (name = "requestMappingHandlerAdapter") + public RequestMappingHandlerAdapter requestMappingHandlerAdapter( + MappingJackson2HttpMessageConverter mappingJacksonHttpMessageConverter, + MarshallingHttpMessageConverter marshallingHttpMessageConverter) { + RequestMappingHandlerAdapter requestMappingHandlerAdapter = + new RequestMappingHandlerAdapter(); + List> httpMessageConverterList = + new ArrayList>(); + httpMessageConverterList.add(mappingJacksonHttpMessageConverter); + httpMessageConverterList.add(marshallingHttpMessageConverter); + requestMappingHandlerAdapter.setMessageConverters(httpMessageConverterList); + return requestMappingHandlerAdapter; + } + + /** + * restTemplate . + * @return restTemplate + */ + @Bean (name = "restTemplate") + public RestTemplate restTemplate( + MappingJackson2HttpMessageConverter mappingJacksonHttpMessageConverter, + MarshallingHttpMessageConverter marshallingHttpMessageConverter) { + RestTemplate restTemplate = new RestTemplate(); + List> httpMessageConverterList = + new ArrayList>(); + httpMessageConverterList.add(mappingJacksonHttpMessageConverter); + httpMessageConverterList.add(marshallingHttpMessageConverter); + restTemplate.setMessageConverters(httpMessageConverterList); + return restTemplate; + } + + + + +} diff --git a/maxkey-core/src/main/java/org/maxkey/crypto/password/opt/impl/MailOtpAuthn.java b/maxkey-core/src/main/java/org/maxkey/crypto/password/opt/impl/MailOtpAuthn.java index 22486b98..a3414f7e 100644 --- a/maxkey-core/src/main/java/org/maxkey/crypto/password/opt/impl/MailOtpAuthn.java +++ b/maxkey-core/src/main/java/org/maxkey/crypto/password/opt/impl/MailOtpAuthn.java @@ -35,7 +35,7 @@ public class MailOtpAuthn extends AbstractOptAuthn { email.setAuthenticator( new DefaultAuthenticator(emailConfig.getUsername(), emailConfig.getPassword())); - email.setFrom(emailConfig.getSenderMail()); + email.setFrom(emailConfig.getSender()); email.setSubject(subject); email.setMsg( MessageFormat.format( diff --git a/maxkey-core/src/main/java/org/maxkey/persistence/redis/RedisConnectionFactory.java b/maxkey-core/src/main/java/org/maxkey/persistence/redis/RedisConnectionFactory.java index 326bacfa..1d660277 100644 --- a/maxkey-core/src/main/java/org/maxkey/persistence/redis/RedisConnectionFactory.java +++ b/maxkey-core/src/main/java/org/maxkey/persistence/redis/RedisConnectionFactory.java @@ -6,134 +6,140 @@ import redis.clients.jedis.JedisPoolConfig; public class RedisConnectionFactory { - public static class DEFAULT_CONFIG{ - /** - * Redis默认服务器IP - */ - public static String DEFAULT_ADDRESS = "127.0.0.1"; - /** - * Redis默认端口号 - */ - public static int DEFAULT_PORT = 6379; - /** - * 访问密码 - */ - public static String DEFAULT_AUTH = "admin"; - /** - * 可用连接实例的最大数目,默认值为8;
- *如果赋值为-1,则表示不限制;如果pool已经分配了maxActive个jedis实例,则此时pool的状态为exhausted(耗尽)。 - **/ - public static int DEFAULT_MAX_ACTIVE = 5000; - - /** - * 控制一个pool最多有多少个状态为idle(空闲的)的jedis实例,默认值也是8。 - */ - public static int DEFAULT_MAX_IDLE = 5000; - - /** - * 等待可用连接的最大时间,单位毫秒,默认值为-1,表示永不超时。如果超过等待时间,则直接抛出JedisConnectionException; - */ - public static int DEFAULT_MAX_WAIT_MILLIS = 10000; - - public static int DEFAULT_TIMEOUT = 10000; - - /** - * 在borrow一个jedis实例时,是否提前进行validate操作;如果为true,则得到的jedis实例均是可用的; - */ - public static boolean DEFAULT_TEST_ON_BORROW = true; - /** - * 默认过期时间 - */ - public static int DEFAULT_LIFETIME = 600; - } + public static class DEFAULT_CONFIG { + /** + * Redis默认服务器IP + */ + public static String DEFAULT_ADDRESS = "127.0.0.1"; + /** + * Redis默认端口号 + */ + public static int DEFAULT_PORT = 6379; + /** + * 访问密码 + */ + public static String DEFAULT_AUTH = "admin"; + /** + * 可用连接实例的最大数目,默认值为8;
+ * 如果赋值为-1,则表示不限制;如果pool已经分配了maxActive个jedis实例,则此时pool的状态为exhausted(耗尽)。 + **/ + public static int DEFAULT_MAX_ACTIVE = 5000; - JedisPoolConfig poolConfig; - - private JedisPool jedisPool = null; - - private String hostname; + /** + * 控制一个pool最多有多少个状态为idle(空闲的)的jedis实例,默认值也是8。 + */ + public static int DEFAULT_MAX_IDLE = 5000; + + /** + * 等待可用连接的最大时间,单位毫秒,默认值为-1,表示永不超时。如果超过等待时间,则直接抛出JedisConnectionException; + */ + public static int DEFAULT_MAX_WAIT_MILLIS = 10000; + + public static int DEFAULT_TIMEOUT = 10000; + + /** + * 在borrow一个jedis实例时,是否提前进行validate操作;如果为true,则得到的jedis实例均是可用的; + */ + public static boolean DEFAULT_TEST_ON_BORROW = true; + /** + * 默认过期时间 + */ + public static int DEFAULT_LIFETIME = 600; + } + + JedisPoolConfig poolConfig; + + private JedisPool jedisPool = null; + + private String hostName; private int port; private String password; private int timeOut; + + public RedisConnectionFactory() { + + } + + public void initConnectionFactory() { + if (jedisPool == null) { + try { + if (this.hostName == null || hostName.equals("")) { + hostName = DEFAULT_CONFIG.DEFAULT_ADDRESS; + } + if (port == 0) { + port = DEFAULT_CONFIG.DEFAULT_PORT; + } + if (timeOut == 0) { + timeOut = DEFAULT_CONFIG.DEFAULT_TIMEOUT; + } + + if (this.password == null || this.password.equals("") || this.password.equalsIgnoreCase("password")) { + this.password = null; + } + jedisPool = new JedisPool(poolConfig, hostName, port, timeOut, password); + + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + public synchronized RedisConnection getConnection() { + initConnectionFactory(); + RedisConnection redisConnection = new RedisConnection(this); + return redisConnection; + } + + public Jedis open() { + return jedisPool.getResource(); + } + + public void close(Jedis conn) { + // jedisPool.returnResource(conn); + conn.close(); + } + + + public String getHostName() { + return hostName; + } + + public void setHostName(String hostName) { + this.hostName = hostName; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public int getTimeOut() { + return timeOut; + } + + public void setTimeOut(int timeOut) { + this.timeOut = timeOut; + } + + public void setPoolConfig(JedisPoolConfig poolConfig) { + this.poolConfig = poolConfig; + } + + public JedisPoolConfig getPoolConfig() { + return poolConfig; + } + - public RedisConnectionFactory() { - - } - public void initConnectionFactory() { - if(jedisPool==null){ - try { - if(this.hostname==null||hostname.equals("")){ - hostname= DEFAULT_CONFIG.DEFAULT_ADDRESS; - } - if(port==0){ - port= DEFAULT_CONFIG.DEFAULT_PORT; - } - if(timeOut==0){ - timeOut=DEFAULT_CONFIG.DEFAULT_TIMEOUT; - } - - if(this.password==null||this.password.equals("")||this.password.equalsIgnoreCase("password")){ - this.password=null; - } - jedisPool = new JedisPool(poolConfig, hostname, port, timeOut, password); - - } catch (Exception e) { - e.printStackTrace(); - } - } - } - - public synchronized RedisConnection getConnection(){ - initConnectionFactory(); - RedisConnection redisConnection=new RedisConnection(this); - return redisConnection; - } - - public Jedis open(){ - return jedisPool.getResource(); - } - - public void close(Jedis conn){ - //jedisPool.returnResource(conn); - conn.close(); - } - - public String getHostname() { - return hostname; - } - - public void setHostname(String hostname) { - this.hostname = hostname; - } - - public int getPort() { - return port; - } - - public void setPort(int port) { - this.port = port; - } - - public String getPassword() { - return password; - } - - public void setPassword(String password) { - this.password = password; - } - - public int getTimeOut() { - return timeOut; - } - - public void setTimeOut(int timeOut) { - this.timeOut = timeOut; - } - - public void setPoolConfig(JedisPoolConfig poolConfig) { - this.poolConfig = poolConfig; - } - - } diff --git a/maxkey-core/src/main/java/org/maxkey/web/ImageEndpoint.java b/maxkey-core/src/main/java/org/maxkey/web/ImageEndpoint.java deleted file mode 100644 index d71090e7..00000000 --- a/maxkey-core/src/main/java/org/maxkey/web/ImageEndpoint.java +++ /dev/null @@ -1,174 +0,0 @@ -package org.maxkey.web; - -import com.google.code.kaptcha.Producer; -import java.awt.image.BufferedImage; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import javax.imageio.ImageIO; -import javax.servlet.ServletOutputStream; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import org.maxkey.config.ApplicationConfig; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; - - -/** - * ImageEndpoint Producer Image and captcha. - * @author Crystal.Sea - * - */ -@Controller -public class ImageEndpoint { - private static final Logger _logger = LoggerFactory.getLogger(ImageEndpoint.class); - - @Autowired - private Producer captchaProducer; - - @Autowired - @Qualifier("applicationConfig") - ApplicationConfig applicationConfig; - - /** - * captcha image Producer. - * - * @param request HttpServletRequest - * @param response HttpServletResponse - */ - @RequestMapping(value = "/captcha") - public void captchaHandleRequest(HttpServletRequest request, HttpServletResponse response) { - try { - - String kaptchaText = captchaProducer.createText(); - if (applicationConfig.getLoginConfig().getCaptchaType() - .equalsIgnoreCase("Arithmetic")) { - Integer intParamA = Integer.valueOf(kaptchaText.substring(0, 1)); - Integer intParamB = Integer.valueOf(kaptchaText.substring(1, 2)); - Integer calculateValue = 0; - if ((intParamA > intParamB) && ((intParamA + intParamB) % 5 > 3)) { - calculateValue = intParamA - intParamB; - kaptchaText = intParamA + "-" + intParamB + "=?"; - } else { - calculateValue = intParamA + intParamB; - kaptchaText = intParamA + "+" + intParamB + "=?"; - } - _logger.trace("Sesssion id " + request.getSession().getId() - + " , Arithmetic calculate Value is " + calculateValue); - request.getSession().setAttribute( - WebConstants.KAPTCHA_SESSION_KEY, calculateValue + ""); - } else { - // store the text in the session - request.getSession().setAttribute(WebConstants.KAPTCHA_SESSION_KEY, kaptchaText); - } - _logger.trace("Sesssion id " + request.getSession().getId() - + " , Captcha Text is " + kaptchaText); - - // create the image with the text - BufferedImage bufferedImage = captchaProducer.createImage(kaptchaText); - producerImage(request,response,bufferedImage); - } catch (Exception e) { - _logger.error("captcha Producer Error " + e.getMessage()); - } - } - - /** - * Session Image Producer. - * - * @param request HttpServletRequest - * @param response HttpServletResponse - */ - - @RequestMapping("/image/{id}") - public void imageHandleRequest(HttpServletRequest request, HttpServletResponse response, - @PathVariable("id") String id) { - try { - // get session image bytes - byte[] image = (byte[]) request.getSession().getAttribute(id); - producerImage(request,response,byte2BufferedImage(image)); - } catch (Exception e) { - _logger.error("captcha Producer Error " + e.getMessage()); - } - } - - /** - * producerImage. - * @param request HttpServletRequest - * @param response HttpServletResponse - * @param bufferedImage BufferedImage - * @throws IOException error - */ - public static void producerImage(HttpServletRequest request, - HttpServletResponse response, - BufferedImage bufferedImage) throws IOException { - // Set to expire far in the past. - response.setDateHeader("Expires", 0); - // Set standard HTTP/1.1 no-cache headers. - response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate"); - // Set IE extended HTTP/1.1 no-cache headers (use addHeader). - response.addHeader("Cache-Control", "post-check=0, pre-check=0"); - // Set standard HTTP/1.0 no-cache header. - response.setHeader("Pragma", "no-cache"); - // return a jpeg/gif - response.setContentType("image/gif"); - - // create the image - if (bufferedImage != null) { - ServletOutputStream out = response.getOutputStream(); - // write the data out - ImageIO.write(bufferedImage, "gif", out); - try { - out.flush(); - } finally { - out.close(); - } - } - } - - /** - * byte2BufferedImage. - * @param imageByte bytes - * @return - */ - public static BufferedImage byte2BufferedImage(byte[] imageByte) { - try { - InputStream in = new ByteArrayInputStream(imageByte); - BufferedImage bufferedImage = ImageIO.read(in); - return bufferedImage; - } catch (IOException e) { - e.printStackTrace(); - } - return null; - } - - /** - * bufferedImage2Byte. - * @param bufferedImage BufferedImage - * @return - */ - public static byte[] bufferedImage2Byte(BufferedImage bufferedImage) { - try { - ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); - ImageIO.write(bufferedImage, "gif", byteArrayOutputStream); - return byteArrayOutputStream.toByteArray(); - } catch (IOException e) { - e.printStackTrace(); - } - return null; - } - - public void setCaptchaProducer(Producer captchaProducer) { - this.captchaProducer = captchaProducer; - } - - public void setApplicationConfig(ApplicationConfig applicationConfig) { - this.applicationConfig = applicationConfig; - } - -} diff --git a/maxkey-core/src/main/java/org/maxkey/web/image/AbstractImageEndpoint.java b/maxkey-core/src/main/java/org/maxkey/web/image/AbstractImageEndpoint.java new file mode 100644 index 00000000..de6c01ed --- /dev/null +++ b/maxkey-core/src/main/java/org/maxkey/web/image/AbstractImageEndpoint.java @@ -0,0 +1,103 @@ +package org.maxkey.web.image; + +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import javax.imageio.ImageIO; +import javax.servlet.ServletOutputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.maxkey.config.ApplicationConfig; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; + + +/** + * AbstractImageEndpoint Producer Image . + * @author Crystal.Sea + * + */ + +public class AbstractImageEndpoint { + private static final Logger _logger = LoggerFactory.getLogger(AbstractImageEndpoint.class); + + @Autowired + @Qualifier("applicationConfig") + ApplicationConfig applicationConfig; + + /** + * producerImage. + * @param request HttpServletRequest + * @param response HttpServletResponse + * @param bufferedImage BufferedImage + * @throws IOException error + */ + public static void producerImage(HttpServletRequest request, + HttpServletResponse response, + BufferedImage bufferedImage) throws IOException { + // Set to expire far in the past. + response.setDateHeader("Expires", 0); + // Set standard HTTP/1.1 no-cache headers. + response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate"); + // Set IE extended HTTP/1.1 no-cache headers (use addHeader). + response.addHeader("Cache-Control", "post-check=0, pre-check=0"); + // Set standard HTTP/1.0 no-cache header. + response.setHeader("Pragma", "no-cache"); + // return a jpeg/gif + response.setContentType("image/gif"); + _logger.trace("create the image"); + // create the image + if (bufferedImage != null) { + ServletOutputStream out = response.getOutputStream(); + // write the data out + ImageIO.write(bufferedImage, "gif", out); + try { + out.flush(); + } finally { + out.close(); + } + } + } + + /** + * byte2BufferedImage. + * @param imageByte bytes + * @return + */ + public static BufferedImage byte2BufferedImage(byte[] imageByte) { + try { + InputStream in = new ByteArrayInputStream(imageByte); + BufferedImage bufferedImage = ImageIO.read(in); + return bufferedImage; + } catch (IOException e) { + e.printStackTrace(); + } + return null; + } + + /** + * bufferedImage2Byte. + * @param bufferedImage BufferedImage + * @return + */ + public static byte[] bufferedImage2Byte(BufferedImage bufferedImage) { + try { + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + ImageIO.write(bufferedImage, "gif", byteArrayOutputStream); + return byteArrayOutputStream.toByteArray(); + } catch (IOException e) { + e.printStackTrace(); + } + return null; + } + + + public void setApplicationConfig(ApplicationConfig applicationConfig) { + this.applicationConfig = applicationConfig; + } + +} diff --git a/maxkey-core/src/main/java/org/maxkey/web/image/ImageCaptchaEndpoint.java b/maxkey-core/src/main/java/org/maxkey/web/image/ImageCaptchaEndpoint.java new file mode 100644 index 00000000..cb3c8c29 --- /dev/null +++ b/maxkey-core/src/main/java/org/maxkey/web/image/ImageCaptchaEndpoint.java @@ -0,0 +1,76 @@ +package org.maxkey.web.image; + +import com.google.code.kaptcha.Producer; +import java.awt.image.BufferedImage; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.maxkey.web.WebConstants; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; + + +/** + * ImageCaptchaEndpoint Producer captcha. + * @author Crystal.Sea + * + */ +@Controller +public class ImageCaptchaEndpoint extends AbstractImageEndpoint { + private static final Logger _logger = LoggerFactory.getLogger(ImageCaptchaEndpoint.class); + + @Autowired + private Producer captchaProducer; + + /** + * captcha image Producer. + * + * @param request HttpServletRequest + * @param response HttpServletResponse + */ + @RequestMapping(value = "/captcha") + public void captchaHandleRequest(HttpServletRequest request, HttpServletResponse response) { + try { + + String kaptchaText = captchaProducer.createText(); + if (applicationConfig.getLoginConfig().getCaptchaType() + .equalsIgnoreCase("Arithmetic")) { + Integer intParamA = Integer.valueOf(kaptchaText.substring(0, 1)); + Integer intParamB = Integer.valueOf(kaptchaText.substring(1, 2)); + Integer calculateValue = 0; + if ((intParamA > intParamB) && ((intParamA + intParamB) % 5 > 3)) { + calculateValue = intParamA - intParamB; + kaptchaText = intParamA + "-" + intParamB + "=?"; + } else { + calculateValue = intParamA + intParamB; + kaptchaText = intParamA + "+" + intParamB + "=?"; + } + _logger.trace("Sesssion id " + request.getSession().getId() + + " , Arithmetic calculate Value is " + calculateValue); + request.getSession().setAttribute( + WebConstants.KAPTCHA_SESSION_KEY, calculateValue + ""); + } else { + // store the text in the session + request.getSession().setAttribute(WebConstants.KAPTCHA_SESSION_KEY, kaptchaText); + } + _logger.trace("Sesssion id " + request.getSession().getId() + + " , Captcha Text is " + kaptchaText); + + // create the image with the text + BufferedImage bufferedImage = captchaProducer.createImage(kaptchaText); + producerImage(request,response,bufferedImage); + } catch (Exception e) { + _logger.error("captcha Producer Error " + e.getMessage()); + } + } + + + + public void setCaptchaProducer(Producer captchaProducer) { + this.captchaProducer = captchaProducer; + } + + +} diff --git a/maxkey-core/src/main/java/org/maxkey/web/image/ImageEndpoint.java b/maxkey-core/src/main/java/org/maxkey/web/image/ImageEndpoint.java new file mode 100644 index 00000000..c499f614 --- /dev/null +++ b/maxkey-core/src/main/java/org/maxkey/web/image/ImageEndpoint.java @@ -0,0 +1,40 @@ +package org.maxkey.web.image; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; + + +/** + * ImageEndpoint Producer Image and captcha. + * @author Crystal.Sea + * + */ +@Controller +public class ImageEndpoint extends AbstractImageEndpoint { + private static final Logger _logger = LoggerFactory.getLogger(ImageEndpoint.class); + + /** + * Session Image Producer. + * + * @param request HttpServletRequest + * @param response HttpServletResponse + */ + + @RequestMapping("/image/{id}") + public void imageHandleRequest(HttpServletRequest request, HttpServletResponse response, + @PathVariable("id") String id) { + try { + // get session image bytes + byte[] image = (byte[]) request.getSession().getAttribute(id); + producerImage(request,response,byte2BufferedImage(image)); + } catch (Exception e) { + _logger.error("captcha Producer Error " + e.getMessage()); + } + } + +} diff --git a/maxkey-lib/mybatis-jpa-extra-spring-boot-starter-2.0.jar b/maxkey-lib/mybatis-jpa-extra-spring-boot-starter-2.0.jar new file mode 100644 index 0000000000000000000000000000000000000000..6064fe0a1490a6b36a28f906adfe5309f2258c84 GIT binary patch literal 20855 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 literal 0 HcmV?d00001 diff --git a/maxkey-web-manage/src/main/java/org/maxkey/MaxKeyMgtApplication.java b/maxkey-web-manage/src/main/java/org/maxkey/MaxKeyMgtApplication.java index eae2a17a..f1cf63d6 100644 --- a/maxkey-web-manage/src/main/java/org/maxkey/MaxKeyMgtApplication.java +++ b/maxkey-web-manage/src/main/java/org/maxkey/MaxKeyMgtApplication.java @@ -1,34 +1,17 @@ package org.maxkey; -import java.io.IOException; import java.util.Date; -import java.util.Properties; - import javax.servlet.ServletException; - -import org.maxkey.authn.SavedRequestAwareAuthenticationSuccessHandler; -import org.maxkey.crypto.password.PasswordReciprocal; import org.maxkey.web.InitializeContext; 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.server.ConfigurableWebServerFactory; -import org.springframework.boot.web.server.ErrorPage; -import org.springframework.boot.web.server.WebServerFactoryCustomizer; import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.ImportResource; -import org.springframework.core.io.ClassPathResource; -import org.springframework.core.io.Resource; -import org.springframework.http.HttpStatus; - -import com.google.code.kaptcha.Producer; -import com.google.code.kaptcha.impl.DefaultKaptcha; -import com.google.code.kaptcha.util.Config; @SpringBootApplication @ImportResource(locations={"classpath:spring/maxkey-mgt.xml"}) @@ -63,57 +46,5 @@ public class MaxKeyMgtApplication extends SpringBootServletInitializer { return application.sources(MaxKeyMgtApplication.class); } - - @Bean - MaxKeyMgtConfig MaxKeyMgtConfig() { - return new MaxKeyMgtConfig(); - } - - /** - * 配置默认错误页面(仅用于内嵌tomcat启动时) - * 使用这种方式,在打包为war后不起作用 - * - * @return - */ - @Bean - public WebServerFactoryCustomizer webServerFactoryCustomizer() { - return new WebServerFactoryCustomizer() { - @Override - public void customize(ConfigurableWebServerFactory factory) { - ErrorPage errorPage400 = new ErrorPage(HttpStatus.BAD_REQUEST,"/exception/error/400"); - ErrorPage errorPage404 = new ErrorPage(HttpStatus.NOT_FOUND,"/exception/error/404"); - ErrorPage errorPage500 = new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR,"/exception/error/500"); - factory.addErrorPages(errorPage400, errorPage404,errorPage500); - - } - }; - } - - @Bean(name = "passwordReciprocal") - public PasswordReciprocal passwordReciprocal() { - return new PasswordReciprocal(); - } - - @Bean(name = "savedRequestSuccessHandler") - public SavedRequestAwareAuthenticationSuccessHandler SavedRequestAwareAuthenticationSuccessHandler() { - return new SavedRequestAwareAuthenticationSuccessHandler(); - } - - /** - * Captcha Producer Config . - * @return Producer - * @throws IOException - */ - @Bean(name = "captchaProducer") - public Producer captchaProducer() throws IOException{ - Resource resource = new ClassPathResource("config/kaptcha.properties"); - _logger.debug("Kaptcha config file " + resource.getURL()); - DefaultKaptcha kaptcha=new DefaultKaptcha(); - Properties properties = new Properties(); - properties.load(resource.getInputStream()); - Config config = new Config(properties); - kaptcha.setConfig(config); - return kaptcha; - } } diff --git a/maxkey-web-manage/src/main/java/org/maxkey/MaxKeyMgtConfig.java b/maxkey-web-manage/src/main/java/org/maxkey/MaxKeyMgtConfig.java index 285a14b3..974b7d39 100644 --- a/maxkey-web-manage/src/main/java/org/maxkey/MaxKeyMgtConfig.java +++ b/maxkey-web-manage/src/main/java/org/maxkey/MaxKeyMgtConfig.java @@ -1,15 +1,42 @@ package org.maxkey; +import javax.sql.DataSource; +import org.apache.ibatis.session.SqlSessionFactory; +import org.maxkey.authn.SavedRequestAwareAuthenticationSuccessHandler; +import org.maxkey.crypto.password.PasswordReciprocal; +import org.mybatis.spring.annotation.MapperScan; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.web.server.ConfigurableWebServerFactory; +import org.springframework.boot.web.server.ErrorPage; +import org.springframework.boot.web.server.WebServerFactoryCustomizer; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; import org.springframework.context.annotation.PropertySource; +import org.springframework.http.HttpStatus; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.datasource.DataSourceTransactionManager; +import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder; @Configuration @PropertySource("classpath:/application.properties") +@MapperScan("org.maxkey.dao.persistence,") public class MaxKeyMgtConfig { private static final Logger _logger = LoggerFactory.getLogger(MaxKeyMgtConfig.class); + + @Autowired + @Qualifier("dataSource") + DataSource dataSource; + + @Autowired + @Qualifier("sqlSessionFactory") + SqlSessionFactory sqlSessionFactory; + @Value("${server.port:8080}") private int port; @@ -21,5 +48,58 @@ public class MaxKeyMgtConfig { this.port = port; } + @Bean + @Primary + @ConfigurationProperties("spring.datasource") + public DataSource dataSource() { + return DruidDataSourceBuilder.create().build(); + } + + @Bean(name = "passwordReciprocal") + public PasswordReciprocal passwordReciprocal() { + return new PasswordReciprocal(); + } + + @Bean(name = "savedRequestSuccessHandler") + public SavedRequestAwareAuthenticationSuccessHandler SavedRequestAwareAuthenticationSuccessHandler() { + return new SavedRequestAwareAuthenticationSuccessHandler(); + } + + + @Bean(name = "jdbcTemplate") + public JdbcTemplate jdbcTemplate() { + return new JdbcTemplate(dataSource); + } + /* + @Bean(name = "sqlSession") + public SqlSessionTemplate sqlSession() throws Exception { + return new SqlSessionTemplate(sqlSessionFactory); + }*/ + + @Bean(name = "transactionManager") + DataSourceTransactionManager transactionManager() { + return new DataSourceTransactionManager(dataSource); + } + + /** + * 配置默认错误页面(仅用于内嵌tomcat启动时) + * 使用这种方式,在打包为war后不起作用 + * + * @return + */ + @Bean + public WebServerFactoryCustomizer webServerFactoryCustomizer() { + return new WebServerFactoryCustomizer() { + @Override + public void customize(ConfigurableWebServerFactory factory) { + _logger.debug("WebServerFactoryCustomizer ... "); + ErrorPage errorPage400 = new ErrorPage(HttpStatus.BAD_REQUEST,"/exception/error/400"); + ErrorPage errorPage404 = new ErrorPage(HttpStatus.NOT_FOUND,"/exception/error/404"); + ErrorPage errorPage500 = new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR,"/exception/error/500"); + factory.addErrorPages(errorPage400, errorPage404,errorPage500); + + } + }; + } } diff --git a/maxkey-web-manage/src/main/resources/META-INF/spring.factories b/maxkey-web-manage/src/main/resources/META-INF/spring.factories new file mode 100644 index 00000000..eba608e3 --- /dev/null +++ b/maxkey-web-manage/src/main/resources/META-INF/spring.factories @@ -0,0 +1,5 @@ +# Auto Configure +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ +org.maxkey.MaxKeyMgtConfig,\ +org.maxkey.config.KaptchaAutoConfiguration,\ +org.maxkey.config.MvcAutoConfiguration diff --git a/maxkey-web-manage/src/main/resources/application.properties b/maxkey-web-manage/src/main/resources/application.properties index 8ecd59e1..f722138b 100644 --- a/maxkey-web-manage/src/main/resources/application.properties +++ b/maxkey-web-manage/src/main/resources/application.properties @@ -1,14 +1,39 @@ -#server config #spring.profiles.active=dev +#application +application.title=MaxKey +application.name=MaxKey-Mgt +application.formatted-version=v1.5.0 GA + +#server config #server port server.port=9521 #web app context path server.servlet.context-path=/maxkey-mgt - -application.name=MaxKey-Mgt -application.formatted-version=v1.5.0 GA - -#for freemarker +spring.servlet.multipart.enabled=true +spring.servlet.multipart.max-file-size=4194304 +#server.servlet.encoding.charset.from= +#server.servlet.encoding.charset= +#server.servlet.encoding.enabled= +#server.servlet.encoding.force= +#datasource +spring.datasource.username=root +spring.datasource.password=maxkey +spring.datasource.url=jdbc:mysql://localhost/maxkey?autoReconnect=true&characterEncoding=UTF-8 +spring.datasource.driver-class-name=com.mysql.jdbc.Driver +spring.datasource.type=com.alibaba.druid.pool.DruidDataSource +#mybatis +mybatis.type-aliases-package=org.maxkey.domain,org.maxkey.domain.apps, +mybatis.mapper-locations=classpath*:/org/maxkey/dao/persistence/xml/mysql/*.xml +#mail +spring.mail.default-encoding=utf-8 +spring.mail.host=smtp.163.com +spring.mail.port=465 +spring.mail.username=maxkey@163.com +spring.mail.password=password +spring.mail.protocol=smtp +spring.mail.properties.ssl=true +spring.mail.properties.sender=maxkey@163.com +#freemarker spring.freemarker.template-loader-path=classpath:/templates/views spring.freemarker.cache=false spring.freemarker.charset=UTF-8 diff --git a/maxkey-web-manage/src/main/resources/config/applicationConfig.properties b/maxkey-web-manage/src/main/resources/config/applicationConfig.properties index fa2fcec8..8a13ccc1 100644 --- a/maxkey-web-manage/src/main/resources/config/applicationConfig.properties +++ b/maxkey-web-manage/src/main/resources/config/applicationConfig.properties @@ -10,75 +10,21 @@ config.server.prefix.uri=${config.server.name}:9521/maxkey-mgt config.server.default.uri=${config.server.prefix.uri}/main config.maxkey.uri=${config.server.name}/maxkey ############################################################################ -# Application Configuration -############################################################################ -# DataBase configuration -# JDBC Driver -# for MySql com.mysql.jdbc.Driver -# for oracle oracle.jdbc.driver.OracleDriver -# for DB2 com.ibm.db2.jdbc.app.DB2Driver -# for SqlServer com.microsoft.jdbc.sqlserver.SQLServerDriver -# for SyBase com.sybase.jdbc.SybDriver -# for PostgreSQL org.postgresql.Driver -# for Derby org.apache.derby.jdbc.ClientDriver -config.datasource.driverclass=com.mysql.jdbc.Driver -# JDBC URL -# you need database hostname,port,databasename -# for MySql jdbc:mysql://hostname:port/secdb -# for oracle jdbc:oracle:thin:@hostname:port:secdb -# for DB2 jdbc:db2://hostname:port/secdb -# for SqlServer jdbc:microsoft:sqlserver://hostname:port;DatabaseName=secdb -# for SyBase jdbc:sybase:Tds:hostname:port/secdb -# for Derby jdbc:derby://localhost:1527/secdb -# -config.datasource.url=jdbc:mysql://localhost/maxkey?autoReconnect=true&characterEncoding=UTF-8 -config.datasource.username=root -#root/maxkey -config.datasource.password=maxkey -#db2,derby,mysql,oracle,postgresql,sqlserver at Dialect -config.datasource.database=mysql -config.datasource.password.encrypt=false -# End DataBase configuration -############################################################################ -# CharacterEncoding -#CharacterEncoding true/false -config.characterencoding.encoding=true -config.characterencoding.fromcharset=iso8859-1 -config.characterencoding.tocharset=UTF-8 -# End CharacterEncoding -############################################################################ - -############################################################################ -# Login -config.login.captcha=false -#text or arithmetic -config.login.captcha.type=text -config.login.socialAuth=true -config.login.msad.kerberos=false -# End Login -############################################################################ -# EMAIL configuration - -config.email.username=test@maxkey.org -config.email.password=3&8Ujbnm5hkjhFD -config.email.smtpHost=smtp.exmail.qq.com -config.email.port=25 -config.email.senderMail=test@maxkey.org -config.email.ssl=false -############################################################################ # Login configuration #enable captcha config.login.captcha=true +#text or arithmetic +config.login.captcha.type=text #enable two factor,use one time password -config.login.onetimepwd=true +config.login.onetimepwd=false #enable social sign on -config.login.socialsignon=true +config.login.socialsignon=false #Enable kerberos/SPNEGO -config.login.kerberos=true +config.login.kerberos=false #wsFederation config.login.wsfederation=false #remeberme -config.login.remeberme=true +config.login.remeberme=false #validity config.login.remeberme.validity= #default.uri diff --git a/maxkey-web-manage/src/main/resources/config/kaptcha.properties b/maxkey-web-manage/src/main/resources/kaptcha.properties similarity index 100% rename from maxkey-web-manage/src/main/resources/config/kaptcha.properties rename to maxkey-web-manage/src/main/resources/kaptcha.properties diff --git a/maxkey-web-manage/src/main/resources/spring/maxkey-mgt-persistence.xml b/maxkey-web-manage/src/main/resources/spring/maxkey-mgt-persistence.xml deleted file mode 100644 index e271d120..00000000 --- a/maxkey-web-manage/src/main/resources/spring/maxkey-mgt-persistence.xml +++ /dev/null @@ -1,90 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/maxkey-web-manage/src/main/resources/spring/maxkey-mgt-web.xml b/maxkey-web-manage/src/main/resources/spring/maxkey-mgt-web.xml deleted file mode 100644 index 482a6970..00000000 --- a/maxkey-web-manage/src/main/resources/spring/maxkey-mgt-web.xml +++ /dev/null @@ -1,131 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - classpath:messages/message - - - - - - - - - - - - - - - - org.maxkey.domain.xml.UserInfoXML - - - - - - - - - - - application/xml;charset=UTF-8 - - - - - - - - - application/json;charset=UTF-8 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/maxkey-web-manage/src/main/resources/spring/maxkey-mgt.xml b/maxkey-web-manage/src/main/resources/spring/maxkey-mgt.xml index 24030c1c..db3d124a 100644 --- a/maxkey-web-manage/src/main/resources/spring/maxkey-mgt.xml +++ b/maxkey-web-manage/src/main/resources/spring/maxkey-mgt.xml @@ -34,6 +34,25 @@ + + + + + + + + + + + + + + + + + + @@ -61,10 +80,6 @@ - - - - diff --git a/maxkey-web-maxkey/src/main/java/org/maxkey/MaxKeyConfig.java b/maxkey-web-maxkey/src/main/java/org/maxkey/MaxKeyConfig.java index 2d1605b2..eff36cdb 100644 --- a/maxkey-web-maxkey/src/main/java/org/maxkey/MaxKeyConfig.java +++ b/maxkey-web-maxkey/src/main/java/org/maxkey/MaxKeyConfig.java @@ -1,18 +1,23 @@ package org.maxkey; -import java.io.IOException; -import java.util.Properties; - +import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder; +import javax.sql.DataSource; import org.apache.catalina.Context; import org.apache.catalina.connector.Connector; +import org.apache.ibatis.session.SqlSessionFactory; import org.apache.tomcat.util.descriptor.web.SecurityCollection; import org.apache.tomcat.util.descriptor.web.SecurityConstraint; import org.maxkey.authz.oauth2.provider.endpoint.TokenEndpointAuthenticationFilter; +import org.maxkey.authn.RealmAuthenticationProvider; import org.maxkey.authn.SavedRequestAwareAuthenticationSuccessHandler; import org.maxkey.crypto.password.PasswordReciprocal; +import org.maxkey.crypto.password.opt.algorithm.KeyUriFormat; +import org.mybatis.spring.SqlSessionTemplate; +import org.mybatis.spring.annotation.MapperScan; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; import org.springframework.boot.web.server.ConfigurableWebServerFactory; import org.springframework.boot.web.server.ErrorPage; @@ -21,20 +26,20 @@ import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.ImportResource; +import org.springframework.context.annotation.Primary; import org.springframework.context.annotation.PropertySource; -import org.springframework.core.io.ClassPathResource; -import org.springframework.core.io.Resource; import org.springframework.http.HttpStatus; - -import com.google.code.kaptcha.Producer; -import com.google.code.kaptcha.impl.DefaultKaptcha; -import com.google.code.kaptcha.util.Config; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.datasource.DataSourceTransactionManager; @Configuration @ImportResource(locations = { "classpath:spring/maxkey.xml" }) @PropertySource("classpath:/application.properties") +@PropertySource("classpath:/config/applicationConfig.properties") +@MapperScan("org.maxkey.dao.persistence,") public class MaxKeyConfig { private static final Logger _logger = LoggerFactory.getLogger(MaxKeyConfig.class); + @Value("${server.port:8080}") private int port; @@ -42,10 +47,13 @@ public class MaxKeyConfig { return port; } - public void setPort(int port) { - this.port = port; + @Bean + @Primary + @ConfigurationProperties("spring.datasource") + public DataSource dataSource() { + return DruidDataSourceBuilder.create().build(); } - + @Bean public FilterRegistrationBean TokenEndpointAuthenticationFilter() { FilterRegistrationBean registration = new FilterRegistrationBean(); @@ -66,6 +74,7 @@ public class MaxKeyConfig { return new WebServerFactoryCustomizer() { @Override public void customize(ConfigurableWebServerFactory factory) { + _logger.debug("WebServerFactoryCustomizer ... "); ErrorPage errorPage400 = new ErrorPage(HttpStatus.BAD_REQUEST, "/exception/error/400"); ErrorPage errorPage404 = new ErrorPage(HttpStatus.NOT_FOUND, "/exception/error/404"); ErrorPage errorPage500 = new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/exception/error/500"); @@ -111,21 +120,52 @@ public class MaxKeyConfig { return new SavedRequestAwareAuthenticationSuccessHandler(); } - /** - * Captcha Producer Config . - * @return Producer - * @throws IOException - */ - @Bean(name = "captchaProducer") - public Producer captchaProducer() throws IOException{ - Resource resource = new ClassPathResource("config/kaptcha.properties"); - _logger.debug("Kaptcha config file " + resource.getURL()); - DefaultKaptcha kaptcha=new DefaultKaptcha(); - Properties properties = new Properties(); - properties.load(resource.getInputStream()); - Config config = new Config(properties); - kaptcha.setConfig(config); - return kaptcha; + + @Value("${config.otp.keyuri.format.type:totp}") + String keyuriFormatType; + + @Value("${config.otp.keyuri.format.domain:MaxKey.top}") + String keyuriFormatDomain; + + @Value("${config.otp.keyuri.format.issuer:MaxKey}") + String keyuriFormatIssuer; + + @Value("${config.otp.keyuri.format.digits:6}") + int keyuriFormatDigits; + + @Value("${config.otp.keyuri.format.period:30}") + int keyuriFormatPeriod; + + @Bean(name = "keyUriFormat") + public KeyUriFormat keyUriFormat() { + KeyUriFormat keyUriFormat=new KeyUriFormat(); + keyUriFormat.setType(keyuriFormatType); + keyUriFormat.setDomain(keyuriFormatDomain); + keyUriFormat.setIssuer(keyuriFormatIssuer); + keyUriFormat.setDigits(keyuriFormatDigits); + keyUriFormat.setPeriod(keyuriFormatPeriod); + _logger.debug("KeyUri Format " + keyUriFormat); + return keyUriFormat; } + @Bean(name = "authenticationProvider") + public RealmAuthenticationProvider authenticationProvider() { + return new RealmAuthenticationProvider(); + } + + @Bean(name = "jdbcTemplate") + public JdbcTemplate jdbcTemplate(DataSource dataSource) { + return new JdbcTemplate(dataSource); + } + + @Bean(name = "sqlSession") + public SqlSessionTemplate sqlSession(SqlSessionFactory sqlSessionFactory) throws Exception { + return new SqlSessionTemplate(sqlSessionFactory); + } + + @Bean(name = "transactionManager") + DataSourceTransactionManager transactionManager(DataSource dataSource) { + return new DataSourceTransactionManager(dataSource); + } + } diff --git a/maxkey-web-maxkey/src/main/java/org/maxkey/RedisAutoConfiguration.java b/maxkey-web-maxkey/src/main/java/org/maxkey/RedisAutoConfiguration.java new file mode 100644 index 00000000..6e256d6f --- /dev/null +++ b/maxkey-web-maxkey/src/main/java/org/maxkey/RedisAutoConfiguration.java @@ -0,0 +1,50 @@ +package org.maxkey; + +import org.maxkey.persistence.redis.RedisConnectionFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.PropertySource; + +import redis.clients.jedis.JedisPoolConfig; + +@Configuration +@PropertySource("classpath:/application.properties") +public class RedisAutoConfiguration { + + @Value("${spring.redis.host}") + private String host; + @Value("${spring.redis.port}") + private int port; + @Value("${spring.redis.timeout}") + private int timeout; + @Value("${spring.redis.password}") + private String password; + @Value("${spring.redis.lettuce.pool.max-active}") + private int maxActive; + @Value("${spring.redis.jedis.pool.max-wait}") + private int maxWait; + @Value("${spring.redis.jedis.pool.max-idle}") + private int maxIdle; + @Value("${spring.redis.lettuce.pool.min-idle}") + private int minIdle; + + @Bean + public RedisConnectionFactory redisConnectionFactory() { + RedisConnectionFactory factory = new RedisConnectionFactory(); + factory.setHostName(host); + factory.setPort(port); + factory.setTimeOut(timeout); + factory.setPassword(password); + + JedisPoolConfig poolConfig = new JedisPoolConfig(); + poolConfig.setMaxIdle(maxIdle); + poolConfig.setMinIdle(minIdle); + poolConfig.setMaxTotal(maxActive); + poolConfig.setMaxWaitMillis(maxWait); + + factory.setPoolConfig(poolConfig); + + return factory; + } +} diff --git a/maxkey-web-maxkey/src/main/java/org/maxkey/web/contorller/OneTimePasswordController.java b/maxkey-web-maxkey/src/main/java/org/maxkey/web/contorller/OneTimePasswordController.java index 625355ea..2292f1ae 100644 --- a/maxkey-web-maxkey/src/main/java/org/maxkey/web/contorller/OneTimePasswordController.java +++ b/maxkey-web-maxkey/src/main/java/org/maxkey/web/contorller/OneTimePasswordController.java @@ -10,8 +10,8 @@ import org.maxkey.crypto.password.opt.algorithm.OtpSecret; import org.maxkey.dao.service.UserInfoService; import org.maxkey.domain.UserInfo; import org.maxkey.util.RQCodeUtils; -import org.maxkey.web.ImageEndpoint; import org.maxkey.web.WebContext; +import org.maxkey.web.image.ImageEndpoint; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; diff --git a/maxkey-web-maxkey/src/main/java/org/maxkey/web/contorller/RegistrationController.java b/maxkey-web-maxkey/src/main/java/org/maxkey/web/contorller/RegistrationController.java index 9c0ad343..224d9479 100644 --- a/maxkey-web-maxkey/src/main/java/org/maxkey/web/contorller/RegistrationController.java +++ b/maxkey-web-maxkey/src/main/java/org/maxkey/web/contorller/RegistrationController.java @@ -80,7 +80,7 @@ public class RegistrationController { email.setAuthenticator(new DefaultAuthenticator(applicationConfig.getEmailConfig().getUsername(), applicationConfig.getEmailConfig().getPassword())); email.addTo(registration.getWorkEmail(), registration.getLastName()+registration.getFirstName()); - email.setFrom(applicationConfig.getEmailConfig().getSenderMail(), "ConnSec"); + email.setFrom(applicationConfig.getEmailConfig().getSender(), "ConnSec"); email.setSubject("ConnSec Cloud Identity & Access Registration activate Email ."); String activateUrl=WebContext.getHttpContextPath()+"/registration/forward/activate/"+registration.getId(); diff --git a/maxkey-web-maxkey/src/main/resources/META-INF/spring.factories b/maxkey-web-maxkey/src/main/resources/META-INF/spring.factories new file mode 100644 index 00000000..d2b16c9d --- /dev/null +++ b/maxkey-web-maxkey/src/main/resources/META-INF/spring.factories @@ -0,0 +1,5 @@ +# Auto Configure +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ +org.maxkey.RedisAutoConfiguration,\ +org.maxkey.config.KaptchaAutoConfiguration,\ +org.maxkey.config.MvcAutoConfiguration diff --git a/maxkey-web-maxkey/src/main/resources/application.properties b/maxkey-web-maxkey/src/main/resources/application.properties index 752b6a58..13b4a592 100644 --- a/maxkey-web-maxkey/src/main/resources/application.properties +++ b/maxkey-web-maxkey/src/main/resources/application.properties @@ -1,3 +1,8 @@ +#spring.profiles.active=dev +#application +application.title=MaxKey +application.name=MaxKey +application.formatted-version=v1.5.0 GA #server config #spring.profiles.active=dev #server port @@ -9,13 +14,41 @@ server.ssl.key-alias=maxkey server.ssl.enabled=true server.ssl.key-store-password=maxkey server.ssl.key-store-type=JKS - #web app context path server.servlet.context-path=/maxkey - -application.name=MaxKey -application.formatted-version=v1.5.0 GA - +spring.servlet.multipart.enabled=true +spring.servlet.multipart.max-file-size=4194304 +#encoding +#server.servlet.encoding.charset=UTF-8 +#server.servlet.encoding.enabled=true +#server.servlet.encoding.force=true +#datasource +spring.datasource.username=root +spring.datasource.password=maxkey +spring.datasource.url=jdbc:mysql://localhost/maxkey?autoReconnect=true&characterEncoding=UTF-8 +spring.datasource.driver-class-name=com.mysql.jdbc.Driver +spring.datasource.type=com.alibaba.druid.pool.DruidDataSource +#mybatis +mybatis.type-aliases-package=org.maxkey.domain,org.maxkey.domain.apps, +mybatis.mapper-locations=classpath*:/org/maxkey/dao/persistence/xml/mysql/*.xml +#redis +spring.redis.host=127.0.0.1 +spring.redis.port=6379 +spring.redis.password=password +spring.redis.timeout=10000 +spring.redis.jedis.pool.max-wait=1000 +spring.redis.jedis.pool.max-idle=200 +spring.redis.lettuce.pool.max-active=-1 +spring.redis.lettuce.pool.min-idle=0 +#mail +spring.mail.default-encoding=utf-8 +spring.mail.host=smtp.163.com +spring.mail.port=465 +spring.mail.username=maxkey@163.com +spring.mail.password=password +spring.mail.protocol=smtp +spring.mail.properties.ssl=true +spring.mail.properties.sender=maxkey@163.com #for freemarker spring.freemarker.template-loader-path=classpath:/templates/views spring.freemarker.cache=false @@ -26,8 +59,9 @@ spring.freemarker.expose-request-attributes=false spring.freemarker.expose-session-attributes=false spring.freemarker.request-context-attribute=request spring.freemarker.suffix=.ftl - #static resources spring.mvc.static-path-pattern=/static/** - +spring.messages.basename=classpath:messages/message +spring.messages.encoding=UTF-8 +#main spring.main.allow-bean-definition-overriding=true diff --git a/maxkey-web-maxkey/src/main/resources/config/applicationConfig.properties b/maxkey-web-maxkey/src/main/resources/config/applicationConfig.properties index 1551d0c0..518835fa 100644 --- a/maxkey-web-maxkey/src/main/resources/config/applicationConfig.properties +++ b/maxkey-web-maxkey/src/main/resources/config/applicationConfig.properties @@ -8,73 +8,9 @@ config.server.name=http://${config.server.domain.sub} config.server.prefix.uri=${config.server.name}/maxkey #default.uri config.server.default.uri=${config.server.prefix.uri}/maxkey/appList -config.server.manage.uri=${config.server.name}:9521/maxkey-mgt/login - -############################################################################ -# DataBase configuration -############################################################################ -#db2,derby,mysql,oracle,postgresql,sqlserver at com.connsec.db.mybatis.dialect.Dialect -config.datasource.database=mysql -# JDBC Driver -# for MySql com.mysql.jdbc.Driver -# for oracle oracle.jdbc.driver.OracleDriver -# for DB2 com.ibm.db2.jdbc.app.DB2Driver -# for SqlServer com.microsoft.jdbc.sqlserver.SQLServerDriver -# for SyBase com.sybase.jdbc.SybDriver -# for PostgreSQL org.postgresql.Driver -# for Derby org.apache.derby.jdbc.ClientDriver -config.datasource.driverclass=com.mysql.jdbc.Driver -# JDBC URL -# you need database hostname,port,databasename -# for MySql jdbc:mysql://hostname:port/secdb -# for oracle jdbc:oracle:thin:@hostname:port:secdb -# for DB2 jdbc:db2://hostname:port/secdb -# for SqlServer jdbc:microsoft:sqlserver://hostname:port;DatabaseName=secdb -# for SyBase jdbc:sybase:Tds:hostname:port/secdb -# for Derby jdbc:derby://localhost:1527/secdb -# -config.datasource.url=jdbc:mysql://localhost/maxkey?autoReconnect=true&characterEncoding=UTF-8 -config.datasource.username=root -config.datasource.password=maxkey -config.datasource.password.encrypt=false -############################################################################ -# EMAIL configuration -############################################################################ -config.email.username=maxkey@163.com -config.email.password=password -config.email.smtpHost=smtp.163.com -config.email.port=465 -config.email.senderMail=maxkey@163.com -config.email.ssl=true - -############################################################################ -# CharacterEncoding configuration -############################################################################ -# CharacterEncoding true/false - -config.characterencoding.encoding=true -config.characterencoding.charset.from=iso8859-1 -config.characterencoding.charset.to=UTF-8 - +config.server.management.uri=${config.server.name}:9521/maxkey-mgt/login config.app.issuer=CN=ConSec,CN=COM,CN=SH -############################################################################ -#IP -config.redis.hostname=127.0.0.1 -#port -config.redis.port=6379 -#password -config.redis.password=password -# -config.redis.timeout=10000 -# -config.redis.pool.maxtotal=1000 -# -config.redis.pool.maxidle=200 -# -config.redis.pool.maxwaitmillis=1000 -# -config.redis.pool.testonborrow=true ############################################################################ # Login configuration #enable captcha @@ -98,6 +34,13 @@ config.login.remeberme.validity= config.login.default.uri=appList config.ipaddress.whitelist=false + +config.otp.keyuri.format.type=totp +config.otp.keyuri.format.digits=6 +config.otp.keyuri.format.issuer=MaxKey +config.otp.keyuri.format.domain=${config.server.domain} +config.otp.keyuri.format.period=30 + ############################################################################ # Kerberos Login configuration ############################################################################ diff --git a/maxkey-web-maxkey/src/main/resources/config/kaptcha.properties b/maxkey-web-maxkey/src/main/resources/kaptcha.properties similarity index 100% rename from maxkey-web-maxkey/src/main/resources/config/kaptcha.properties rename to maxkey-web-maxkey/src/main/resources/kaptcha.properties diff --git a/maxkey-web-maxkey/src/main/resources/spring/maxkey-persistence.xml b/maxkey-web-maxkey/src/main/resources/spring/maxkey-persistence.xml deleted file mode 100644 index b17e1305..00000000 --- a/maxkey-web-maxkey/src/main/resources/spring/maxkey-persistence.xml +++ /dev/null @@ -1,103 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/maxkey-web-maxkey/src/main/resources/spring/maxkey-security.xml b/maxkey-web-maxkey/src/main/resources/spring/maxkey-security.xml index 6ca8f200..5ec5316d 100644 --- a/maxkey-web-maxkey/src/main/resources/spring/maxkey-security.xml +++ b/maxkey-web-maxkey/src/main/resources/spring/maxkey-security.xml @@ -75,15 +75,11 @@ - - - - - - - - - + @@ -146,11 +141,6 @@ - - - - - diff --git a/maxkey-web-maxkey/src/main/resources/spring/maxkey-web.xml b/maxkey-web-maxkey/src/main/resources/spring/maxkey-web.xml deleted file mode 100644 index 59626347..00000000 --- a/maxkey-web-maxkey/src/main/resources/spring/maxkey-web.xml +++ /dev/null @@ -1,108 +0,0 @@ - - - - - - - - - - - - - - - - - classpath:messages/message - - - - - - - - - - - - - - - - org.maxkey.domain.xml.UserInfoXML - - - - - - - - - - - application/xml;charset=UTF-8 - - - - - - - - - application/json;charset=UTF-8 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/maxkey-web-maxkey/src/main/resources/spring/maxkey.xml b/maxkey-web-maxkey/src/main/resources/spring/maxkey.xml index b515e373..fd224c6a 100644 --- a/maxkey-web-maxkey/src/main/resources/spring/maxkey.xml +++ b/maxkey-web-maxkey/src/main/resources/spring/maxkey.xml @@ -43,8 +43,6 @@ - - @@ -53,7 +51,5 @@ - - \ No newline at end of file