diff --git a/build.gradle b/build.gradle index c101714..e4bcc09 100644 --- a/build.gradle +++ b/build.gradle @@ -17,7 +17,7 @@ buildscript { subprojects { group 'com.synebula' - version '1.4.0' + version '1.5.0' buildscript { repositories { diff --git a/src/gaea.app/build.gradle b/src/gaea.app/build.gradle index 6e3cf39..eec4bb5 100644 --- a/src/gaea.app/build.gradle +++ b/src/gaea.app/build.gradle @@ -17,5 +17,6 @@ dependencies { api group: 'org.apache.poi', name: 'poi-ooxml', version: '5.0.0' api group: 'com.google.code.gson', name: 'gson', version: '2.8.6' api group: 'com.auth0', name: 'java-jwt', version: '3.14.0' + api group: 'com.google.guava', name: 'guava', version: '31.1-jre' } diff --git a/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/Application.kt b/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/Application.kt index 7e0a5c7..6ce4a47 100644 --- a/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/Application.kt +++ b/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/Application.kt @@ -2,12 +2,12 @@ package com.synebula.gaea.app import com.synebula.gaea.app.cmd.ICommandApp import com.synebula.gaea.app.query.IQueryApp -import com.synebula.gaea.data.serialization.json.IJsonSerializer +import com.synebula.gaea.data.message.HttpMessageFactory import com.synebula.gaea.domain.service.ICommand import com.synebula.gaea.domain.service.IService import com.synebula.gaea.log.ILogger import com.synebula.gaea.query.IQuery -import javax.annotation.Resource +import org.springframework.beans.factory.annotation.Autowired /** * 联合服务,同时实现了ICommandApp和IQueryApp接口 @@ -24,6 +24,6 @@ open class Application( override var logger: ILogger, ) : ICommandApp, IQueryApp { - @Resource - override var jsonSerializer: IJsonSerializer? = null + @Autowired + override lateinit var httpMessageFactory: HttpMessageFactory } \ No newline at end of file diff --git a/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/IApplication.kt b/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/IApplication.kt index e6429c9..06323a7 100644 --- a/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/IApplication.kt +++ b/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/IApplication.kt @@ -2,6 +2,7 @@ package com.synebula.gaea.app import com.google.gson.Gson import com.synebula.gaea.data.message.HttpMessage +import com.synebula.gaea.data.message.HttpMessageFactory import com.synebula.gaea.data.message.Status import com.synebula.gaea.log.ILogger import org.springframework.security.core.context.SecurityContextHolder @@ -18,12 +19,17 @@ interface IApplication { */ var logger: ILogger + /** + * 日志详细的构造器 + */ + var httpMessageFactory: HttpMessageFactory + /** * 安全执行 */ fun safeExecute(error: String, process: ((msg: HttpMessage) -> Unit)): HttpMessage { - val msg = HttpMessage() + val msg = this.httpMessageFactory.create() try { process(msg) logger.debug(this, "$name business execute success") @@ -39,7 +45,7 @@ interface IApplication { * 可抛出自定义异常信息的安全controller实现了异常捕获和消息组成。 */ fun throwExecute(error: String, process: ((msg: HttpMessage) -> Unit)): HttpMessage { - val msg = HttpMessage() + val msg = this.httpMessageFactory.create() try { process(msg) logger.debug(this, "$name business execute success") @@ -54,7 +60,7 @@ interface IApplication { * 获取用户信息 * @param clazz 用户信息结构类 */ - fun sessionUser(clazz: Class): T? { + fun userSession(clazz: Class): T? { try { val authentication = SecurityContextHolder.getContext().authentication.principal.toString() try { diff --git a/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/SimpleApplication.kt b/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/SimpleApplication.kt index e06f139..73a38b2 100644 --- a/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/SimpleApplication.kt +++ b/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/SimpleApplication.kt @@ -2,12 +2,12 @@ package com.synebula.gaea.app import com.synebula.gaea.app.cmd.ISimpleCommandApp import com.synebula.gaea.app.query.IQueryApp -import com.synebula.gaea.data.serialization.json.IJsonSerializer +import com.synebula.gaea.data.message.HttpMessageFactory import com.synebula.gaea.domain.model.IAggregateRoot import com.synebula.gaea.domain.service.ISimpleService import com.synebula.gaea.log.ILogger import com.synebula.gaea.query.IQuery -import javax.annotation.Resource +import org.springframework.beans.factory.annotation.Autowired /** * 简单的服务, 取消了Command对象 @@ -24,6 +24,7 @@ open class SimpleApplication, ID>( override var logger: ILogger, ) : ISimpleCommandApp, IQueryApp { - @Resource - override var jsonSerializer: IJsonSerializer? = null + + @Autowired + override lateinit var httpMessageFactory: HttpMessageFactory } diff --git a/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/cmd/CommandApp.kt b/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/cmd/CommandApp.kt index 516dd1a..1479655 100644 --- a/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/cmd/CommandApp.kt +++ b/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/cmd/CommandApp.kt @@ -1,10 +1,10 @@ package com.synebula.gaea.app.cmd -import com.synebula.gaea.data.serialization.json.IJsonSerializer +import com.synebula.gaea.data.message.HttpMessageFactory import com.synebula.gaea.domain.service.ICommand import com.synebula.gaea.domain.service.IService import com.synebula.gaea.log.ILogger -import javax.annotation.Resource +import org.springframework.beans.factory.annotation.Autowired /** * 指令服务,同时实现ICommandApp @@ -18,6 +18,6 @@ open class CommandApp( override var service: IService, override var logger: ILogger, ) : ICommandApp { - @Resource - override var jsonSerializer: IJsonSerializer? = null + @Autowired + override lateinit var httpMessageFactory: HttpMessageFactory } \ No newline at end of file diff --git a/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/cmd/ICommandApp.kt b/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/cmd/ICommandApp.kt index aff4805..c6e1f2e 100644 --- a/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/cmd/ICommandApp.kt +++ b/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/cmd/ICommandApp.kt @@ -3,7 +3,6 @@ package com.synebula.gaea.app.cmd import com.synebula.gaea.app.IApplication import com.synebula.gaea.data.message.HttpMessage import com.synebula.gaea.data.message.Status -import com.synebula.gaea.data.serialization.json.IJsonSerializer import com.synebula.gaea.domain.service.ICommand import com.synebula.gaea.domain.service.IService import com.synebula.gaea.spring.aop.annotation.Method @@ -17,27 +16,25 @@ import org.springframework.web.bind.annotation.* * @since 2020-05-15 */ interface ICommandApp : IApplication { - var jsonSerializer: IJsonSerializer? - var service: IService @PostMapping @Method("添加") fun add(@RequestBody command: TCommand): HttpMessage { - return HttpMessage(service.add(command)) + return this.httpMessageFactory.create(service.add(command)) } @PutMapping("/{id:.+}") @Method("更新") fun update(@PathVariable id: ID, @RequestBody command: TCommand): HttpMessage { this.service.update(id, command) - return HttpMessage() + return this.httpMessageFactory.create() } @DeleteMapping("/{id:.+}") @Method("删除") fun remove(@PathVariable id: ID): HttpMessage { - val msg = HttpMessage() + val msg = this.httpMessageFactory.create() try { msg.data = this.service.remove(id) } catch (ex: IllegalStateException) { diff --git a/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/cmd/ISimpleCommandApp.kt b/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/cmd/ISimpleCommandApp.kt index 98bd2ec..69d50eb 100644 --- a/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/cmd/ISimpleCommandApp.kt +++ b/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/cmd/ISimpleCommandApp.kt @@ -3,7 +3,6 @@ package com.synebula.gaea.app.cmd import com.synebula.gaea.app.IApplication import com.synebula.gaea.data.message.HttpMessage import com.synebula.gaea.data.message.Status -import com.synebula.gaea.data.serialization.json.IJsonSerializer import com.synebula.gaea.domain.model.IAggregateRoot import com.synebula.gaea.domain.service.ISimpleService import com.synebula.gaea.spring.aop.annotation.Method @@ -17,27 +16,25 @@ import org.springframework.web.bind.annotation.* * @since 2020-05-15 */ interface ISimpleCommandApp, ID> : IApplication { - var jsonSerializer: IJsonSerializer? - var service: ISimpleService @PostMapping @Method("添加") fun add(@RequestBody entity: TRoot): HttpMessage { - return HttpMessage(service.add(entity)) + return this.httpMessageFactory.create(service.add(entity)) } @PutMapping("/{id:.+}") @Method("更新") fun update(@PathVariable id: ID, @RequestBody entity: TRoot): HttpMessage { this.service.update(id, entity) - return HttpMessage() + return this.httpMessageFactory.create() } @DeleteMapping("/{id:.+}") @Method("删除") fun remove(@PathVariable id: ID): HttpMessage { - val msg = HttpMessage() + val msg = this.httpMessageFactory.create() try { msg.data = this.service.remove(id) } catch (ex: IllegalStateException) { diff --git a/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/cmd/SimpleCommandApp.kt b/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/cmd/SimpleCommandApp.kt index 84c881f..9eccb5d 100644 --- a/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/cmd/SimpleCommandApp.kt +++ b/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/cmd/SimpleCommandApp.kt @@ -1,10 +1,10 @@ package com.synebula.gaea.app.cmd -import com.synebula.gaea.data.serialization.json.IJsonSerializer +import com.synebula.gaea.data.message.HttpMessageFactory import com.synebula.gaea.domain.model.IAggregateRoot import com.synebula.gaea.domain.service.ISimpleService import com.synebula.gaea.log.ILogger -import javax.annotation.Resource +import org.springframework.beans.factory.annotation.Autowired /** * 指令服务,同时实现ICommandApp @@ -18,6 +18,6 @@ open class SimpleCommandApp, ID>( override var service: ISimpleService, override var logger: ILogger, ) : ISimpleCommandApp { - @Resource - override var jsonSerializer: IJsonSerializer? = null + @Autowired + override lateinit var httpMessageFactory: HttpMessageFactory } \ No newline at end of file diff --git a/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/component/DozerConverter.kt b/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/component/DozerMapper.kt similarity index 89% rename from src/gaea.app/src/main/kotlin/com/synebula/gaea/app/component/DozerConverter.kt rename to src/gaea.app/src/main/kotlin/com/synebula/gaea/app/component/DozerMapper.kt index 66dd9f5..b7659a9 100644 --- a/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/component/DozerConverter.kt +++ b/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/component/DozerMapper.kt @@ -5,7 +5,7 @@ import org.dozer.DozerBeanMapper import org.springframework.stereotype.Component @Component -class DozerConverter : IObjectMapper { +class DozerMapper : IObjectMapper { private val converter = DozerBeanMapper() override fun deserialize(src: Any, targetClass: Class): T = converter.map(src, targetClass) diff --git a/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/component/cache/Cache.kt b/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/component/cache/Cache.kt new file mode 100644 index 0000000..3c9db7b --- /dev/null +++ b/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/component/cache/Cache.kt @@ -0,0 +1,101 @@ +package com.synebula.gaea.app.component.cache + +import com.google.common.cache.Cache +import com.google.common.cache.CacheBuilder +import com.synebula.gaea.data.cache.ICache +import java.util.concurrent.ConcurrentMap +import java.util.concurrent.TimeUnit + +/** + * 缓存组件 + * + * @param expire 滑动过期时间,单位秒 + */ +open class Cache(expire: Int) : ICache { + + private val guavaCache: Cache + + init { + var builder = CacheBuilder.newBuilder() + if (expire != 0) { + builder = builder.expireAfterAccess(expire.toLong(), TimeUnit.SECONDS) + } + this.guavaCache = builder.build() + } + + override fun add(key: K, value: V) { + this.guavaCache.put(key, value) + } + + /** + * 添加多条缓存记录 + * + * @param entries 需要添加的多条缓存记录Map + */ + override fun add(entries: Map) { + this.guavaCache.putAll(entries) + } + + override fun get(key: K): V? { + return this.guavaCache.getIfPresent(key ?: "") + } + + /** + * 获取给出列表key中存在的缓存元素 + * + * @param keys 尝试获取的key列表 + */ + override fun get(keys: Iterable): Map { + return this.guavaCache.getAllPresent(keys) + } + + + /** + * 清除给出key的缓存 + * + * @param key 需要清楚的缓存key + */ + override fun remove(key: K) { + this.guavaCache.invalidate(key ?: "") + } + + /** + * 清除给出列表key的缓存 + * + * @param keys 需要清楚的缓存key列表 + */ + override fun remove(keys: Iterable) { + this.guavaCache.invalidateAll(keys) + } + + /** + * 判断keys是否存在 + * + * @param keys 需要判断的缓存key列表 + */ + override fun exists(keys: Iterable): Map { + val result = mutableMapOf() + keys.forEach { result[it] = guavaCache.asMap().containsKey(it) } + return result + } + + /** + * 判断key是否存在 + * + * @param key 需要判断的key + */ + override fun exists(key: K): Boolean { + return this.guavaCache.asMap().containsKey(key) + } + + /** + * 清除所有缓存记录 + */ + override fun clear() { + this.guavaCache.cleanUp() + } + + fun asMap(): ConcurrentMap { + return this.guavaCache.asMap() + } +} \ No newline at end of file diff --git a/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/component/security/WebAuthorization.kt b/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/component/security/WebAuthorization.kt index d82f6aa..5f0bf76 100644 --- a/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/component/security/WebAuthorization.kt +++ b/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/component/security/WebAuthorization.kt @@ -1,12 +1,11 @@ package com.synebula.gaea.app.component.security -import com.synebula.gaea.app.struct.exception.TokenCloseExpireException -import com.synebula.gaea.data.message.HttpMessage +import com.synebula.gaea.app.component.security.session.UserSessionManager +import com.synebula.gaea.data.message.HttpMessageFactory import com.synebula.gaea.data.message.Status -import org.springframework.security.authentication.AuthenticationManager import org.springframework.security.authentication.UsernamePasswordAuthenticationToken import org.springframework.security.core.context.SecurityContextHolder -import org.springframework.security.web.authentication.www.BasicAuthenticationFilter +import org.springframework.web.filter.OncePerRequestFilter import java.io.IOException import javax.servlet.FilterChain import javax.servlet.ServletException @@ -16,27 +15,50 @@ import javax.servlet.http.HttpServletResponse /** * 登录成功后 走此类进行鉴权操作 + * + * @param httpMessageFactory HttpMessage 构建器 + * @param userSessionManager 用户信息缓存 + * @param storage 存储识别信息存储方式 token/cookie */ -class WebAuthorization(authenticationManager: AuthenticationManager, var tokenManager: TokenManager) : - BasicAuthenticationFilter(authenticationManager) { +class WebAuthorization( + var httpMessageFactory: HttpMessageFactory, + var userSessionManager: UserSessionManager, + var storage: String = "token" +) : OncePerRequestFilter() { + + /** + * token 在 header 中的 key + */ + private val tokenKey = "token" + /** * 在过滤之前和之后执行的事件 */ @Throws(IOException::class, ServletException::class) override fun doFilterInternal(request: HttpServletRequest, response: HttpServletResponse, chain: FilterChain) { - val token = request.getHeader(tokenManager.header) - try { - val user = tokenManager.verify(token) - val authentication = - UsernamePasswordAuthenticationToken(user, null, null) + val identity = if (this.storage == "cookie") { + request.cookies.find { it.name == tokenKey }?.value ?: "" + } else { + request.getHeader(tokenKey) ?: "" + } + val user = this.userSessionManager.userSession(identity) + if (user != null) { + user.ip = request.remoteAddr + user.url = request.requestURI + val authentication = UsernamePasswordAuthenticationToken(user, null, null) SecurityContextHolder.getContext().authentication = authentication - super.doFilterInternal(request, response, chain) - } catch (ex: TokenCloseExpireException) { + chain.doFilter(request, response) + } else { response.status = Status.Success response.characterEncoding = "utf-8" response.contentType = "text/javascript;charset=utf-8" - response.writer.print(HttpMessage(Status.Reauthorize, tokenManager.sign(ex.payload), "重新下发认证消息")) + response.writer.print( + this.httpMessageFactory.create( + Status.Unauthorized, + "登录信息失效, 请重新登录" + ) + ) response.flushBuffer() } } diff --git a/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/component/security/WebSecurity.kt b/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/component/security/WebSecurity.kt index 1c0ea81..bf3b26f 100644 --- a/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/component/security/WebSecurity.kt +++ b/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/component/security/WebSecurity.kt @@ -1,56 +1,68 @@ package com.synebula.gaea.app.component.security -import com.synebula.gaea.data.message.HttpMessage +import com.synebula.gaea.app.component.security.session.UserSessionManager +import com.synebula.gaea.data.message.HttpMessageFactory import com.synebula.gaea.data.message.Status import org.springframework.beans.factory.annotation.Autowired +import org.springframework.beans.factory.annotation.Value import org.springframework.context.annotation.Bean -import org.springframework.security.authentication.AuthenticationManager +import org.springframework.context.annotation.Configuration import org.springframework.security.config.annotation.web.builders.HttpSecurity +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer import org.springframework.security.config.http.SessionCreationPolicy import org.springframework.security.web.SecurityFilterChain -import org.springframework.stereotype.Component +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter import org.springframework.web.cors.CorsConfiguration import org.springframework.web.cors.CorsConfigurationSource import org.springframework.web.cors.UrlBasedCorsConfigurationSource -@Component +@Configuration +@EnableWebSecurity class WebSecurity { @Autowired - lateinit var tokenManager: TokenManager + lateinit var userSessionManager: UserSessionManager @Autowired - lateinit var authenticationManager: AuthenticationManager + lateinit var httpMessageFactory: HttpMessageFactory + + @Value("\${spring.sign-in-url}") + var signInPath = "/sign/in" /** * 安全配置 */ + @Bean @Throws(Exception::class) - fun filterChain(http: HttpSecurity): SecurityFilterChain { - // 跨域共享 - http.cors() - .and().csrf().disable() // 跨域伪造请求限制无效 - .authorizeRequests() - .anyRequest().authenticated()// 资源任何人都可访问 - .and() - .addFilter(WebAuthorization(authenticationManager, tokenManager))// 添加JWT鉴权拦截器 - .sessionManagement() - .sessionCreationPolicy(SessionCreationPolicy.STATELESS) // 设置Session的创建策略为:Spring Security永不创建HttpSession 不使用HttpSession来获取SecurityContext - .and() - .exceptionHandling() - .authenticationEntryPoint { _, response, _ -> + fun filterChain(httpSecurity: HttpSecurity): SecurityFilterChain { + httpSecurity.cors().and().csrf().disable() // 跨域伪造请求限制无效 + // 设置Session的创建策略为:Spring Security永不创建HttpSession 不使用HttpSession来获取SecurityContext + .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) + // 除了登录接口其他资源都必须登录访问 + .and().authorizeRequests().antMatchers(this.signInPath).permitAll().anyRequest().authenticated() + // 添加鉴权拦截器 + .and().addFilterBefore( + WebAuthorization(httpMessageFactory, userSessionManager), + UsernamePasswordAuthenticationFilter::class.java + ).exceptionHandling().authenticationEntryPoint { _, response, _ -> response.status = Status.Success response.characterEncoding = "utf-8" response.contentType = "text/javascript;charset=utf-8" - response.writer.print(HttpMessage(Status.Unauthorized, "用户未登录,请重新登录后尝试!")) + response.writer.print( + this.httpMessageFactory.create( + Status.Unauthorized, "用户未登录,请重新登录后尝试!" + ) + ) } - return http.build() + + return httpSecurity.build() } + @Bean @Throws(Exception::class) - fun filterChain(): WebSecurityCustomizer { - return WebSecurityCustomizer { web -> web.ignoring().antMatchers("/sign/**") } + fun ignoringCustomizer(): WebSecurityCustomizer { + return WebSecurityCustomizer { web -> web.ignoring().antMatchers(this.signInPath) } } /** diff --git a/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/component/security/session/UserSession.kt b/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/component/security/session/UserSession.kt new file mode 100644 index 0000000..3614a61 --- /dev/null +++ b/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/component/security/session/UserSession.kt @@ -0,0 +1,33 @@ +package com.synebula.gaea.app.component.security.session + +/** + * 登陆用户会话信息 + * + * @param uid 用户id + * @param user 用户信息 + */ +class UserSession(var uid: String, var user: Any) { + + /** + * 登录IP + */ + var ip = "" + + /** + * 访问的url + */ + var url = "" + + /** + * 当前token + */ + var token = "" + + /** + * 获取指定类型的用户信息 + */ + @Suppress("UNCHECKED_CAST") + fun user(): T { + return user as T + } +} \ No newline at end of file diff --git a/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/component/security/session/UserSessionManager.kt b/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/component/security/session/UserSessionManager.kt new file mode 100644 index 0000000..6129948 --- /dev/null +++ b/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/component/security/session/UserSessionManager.kt @@ -0,0 +1,51 @@ +package com.synebula.gaea.app.component.security.session + +import com.synebula.gaea.app.component.cache.Cache +import com.synebula.gaea.ext.toMd5 +import org.springframework.beans.factory.annotation.Value +import org.springframework.stereotype.Component +import java.util.* + +@Component +class UserSessionManager { + + @Value("\${spring.allow-multi-sign}") + private var allowMultiSign = "true" + + /** + * 用户id加盐, (尽量)避免泄漏真是用户id + */ + private val salt = "68d84e0d7b9e" + + /** + * token分割符 + */ + private val delimiter = "-" + + // 用户的实际信息 + var userSessionCache = Cache(7 * 24 * 60 * 60) + + /** + * 用户登入方法 + */ + fun signIn(uid: String, user: Any): String { + val session = UserSession(uid, user) + val id = "$uid$delimiter$salt".toMd5() + if (allowMultiSign != true.toString()) { + if (userSessionCache.asMap().keys.any { k -> k.split(delimiter).last() == id }) + throw UnsupportedOperationException("用户已在另一客户端登录, 请退出后重新尝试登录") + } + val token = "${UUID.randomUUID()}$delimiter${id}" + session.token = token + userSessionCache.add(token, session) + return token + } + + fun signOut(token: String) { + this.userSessionCache.remove(token) + } + + fun userSession(token: String): UserSession? { + return userSessionCache[token] + } +} \ No newline at end of file diff --git a/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/query/IQueryApp.kt b/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/query/IQueryApp.kt index 8577662..92f590a 100644 --- a/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/query/IQueryApp.kt +++ b/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/query/IQueryApp.kt @@ -19,7 +19,7 @@ interface IQueryApp : IApplication { @GetMapping("/{id:.+}") fun get(@PathVariable id: ID): HttpMessage { val data = this.query.get(id) - val msg = HttpMessage() + val msg = this.httpMessageFactory.create() msg.data = data return msg } @@ -28,7 +28,7 @@ interface IQueryApp : IApplication { @GetMapping fun list(@RequestParam params: LinkedHashMap): HttpMessage { val data = this.query.list(params) - return HttpMessage(data) + return this.httpMessageFactory.create(data) } @Method("获取分页数据") @@ -40,6 +40,6 @@ interface IQueryApp : IApplication { ): HttpMessage { val params = Params(page, size, parameters) val data = this.query.paging(params) - return HttpMessage(data) + return this.httpMessageFactory.create(data) } } \ No newline at end of file diff --git a/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/query/QueryApp.kt b/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/query/QueryApp.kt index a301a17..64a9569 100644 --- a/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/query/QueryApp.kt +++ b/src/gaea.app/src/main/kotlin/com/synebula/gaea/app/query/QueryApp.kt @@ -1,7 +1,9 @@ package com.synebula.gaea.app.query +import com.synebula.gaea.data.message.HttpMessageFactory import com.synebula.gaea.log.ILogger import com.synebula.gaea.query.IQuery +import org.springframework.beans.factory.annotation.Autowired /** * 联合服务,同时实现了ICommandApp和IQueryApp接口 @@ -14,4 +16,8 @@ open class QueryApp( override var name: String, override var query: IQuery, override var logger: ILogger, -) : IQueryApp \ No newline at end of file +) : IQueryApp { + + @Autowired + override lateinit var httpMessageFactory: HttpMessageFactory +} \ No newline at end of file diff --git a/src/gaea.spring/src/main/kotlin/com/synebula/gaea/spring/aop/AppAspect.kt b/src/gaea.spring/src/main/kotlin/com/synebula/gaea/spring/aop/AppAspect.kt index c36f312..fdfff8b 100644 --- a/src/gaea.spring/src/main/kotlin/com/synebula/gaea/spring/aop/AppAspect.kt +++ b/src/gaea.spring/src/main/kotlin/com/synebula/gaea/spring/aop/AppAspect.kt @@ -1,7 +1,7 @@ package com.synebula.gaea.spring.aop import com.google.gson.Gson -import com.synebula.gaea.data.message.HttpMessage +import com.synebula.gaea.data.message.HttpMessageFactory import com.synebula.gaea.data.message.Status import com.synebula.gaea.exception.NoticeUserException import com.synebula.gaea.log.ILogger @@ -25,6 +25,9 @@ abstract class AppAspect { @Autowired lateinit var applicationContext: ApplicationContext + @Autowired + lateinit var httpMessageFactory: HttpMessageFactory + /** * 定义切面的方法 */ @@ -74,7 +77,7 @@ abstract class AppAspect { gson.toJson(point.args) }" ) - return HttpMessage(Status.Error, message) + return this.httpMessageFactory.create(Status.Error, message) } } diff --git a/src/gaea/src/main/kotlin/com/synebula/gaea/bus/AllowConcurrentSubscribe.kt b/src/gaea/src/main/kotlin/com/synebula/gaea/bus/AllowConcurrentSubscribe.kt index 77687ee..cd8d723 100644 --- a/src/gaea/src/main/kotlin/com/synebula/gaea/bus/AllowConcurrentSubscribe.kt +++ b/src/gaea/src/main/kotlin/com/synebula/gaea/bus/AllowConcurrentSubscribe.kt @@ -1,16 +1,4 @@ -/* - * Copyright (C) 2007 The Guava Authors - * - * 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 com.synebula.gaea.bus /** diff --git a/src/gaea/src/main/kotlin/com/synebula/gaea/bus/Bus.kt b/src/gaea/src/main/kotlin/com/synebula/gaea/bus/Bus.kt index ea530f7..122138f 100644 --- a/src/gaea/src/main/kotlin/com/synebula/gaea/bus/Bus.kt +++ b/src/gaea/src/main/kotlin/com/synebula/gaea/bus/Bus.kt @@ -1,16 +1,4 @@ -/* - * Copyright (C) 2007 The Guava Authors - * - * 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 com.synebula.gaea.bus import java.lang.reflect.Method diff --git a/src/gaea/src/main/kotlin/com/synebula/gaea/bus/DeadMessage.kt b/src/gaea/src/main/kotlin/com/synebula/gaea/bus/DeadMessage.kt index 6255b78..cc3add8 100644 --- a/src/gaea/src/main/kotlin/com/synebula/gaea/bus/DeadMessage.kt +++ b/src/gaea/src/main/kotlin/com/synebula/gaea/bus/DeadMessage.kt @@ -1,16 +1,4 @@ -/* - * Copyright (C) 2007 The Guava Authors - * - * 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 com.synebula.gaea.bus /** diff --git a/src/gaea/src/main/kotlin/com/synebula/gaea/bus/Dispatcher.kt b/src/gaea/src/main/kotlin/com/synebula/gaea/bus/Dispatcher.kt index 091519f..509b44f 100644 --- a/src/gaea/src/main/kotlin/com/synebula/gaea/bus/Dispatcher.kt +++ b/src/gaea/src/main/kotlin/com/synebula/gaea/bus/Dispatcher.kt @@ -1,16 +1,4 @@ -/* - * Copyright (C) 2014 The Guava Authors - * - * 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 com.synebula.gaea.bus import java.util.* diff --git a/src/gaea/src/main/kotlin/com/synebula/gaea/bus/DomainSubscribe.kt b/src/gaea/src/main/kotlin/com/synebula/gaea/bus/DomainSubscribe.kt index a21ee30..4702586 100644 --- a/src/gaea/src/main/kotlin/com/synebula/gaea/bus/DomainSubscribe.kt +++ b/src/gaea/src/main/kotlin/com/synebula/gaea/bus/DomainSubscribe.kt @@ -1,16 +1,4 @@ -/* - * Copyright (C) 2007 The Guava Authors - * - * 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 com.synebula.gaea.bus import kotlin.reflect.KClass diff --git a/src/gaea/src/main/kotlin/com/synebula/gaea/bus/Subscribe.kt b/src/gaea/src/main/kotlin/com/synebula/gaea/bus/Subscribe.kt index 9bc32ae..d32cb70 100644 --- a/src/gaea/src/main/kotlin/com/synebula/gaea/bus/Subscribe.kt +++ b/src/gaea/src/main/kotlin/com/synebula/gaea/bus/Subscribe.kt @@ -1,16 +1,4 @@ -/* - * Copyright (C) 2007 The Guava Authors - * - * 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 com.synebula.gaea.bus /** diff --git a/src/gaea/src/main/kotlin/com/synebula/gaea/bus/Subscriber.kt b/src/gaea/src/main/kotlin/com/synebula/gaea/bus/Subscriber.kt index bd0484d..0410fa3 100644 --- a/src/gaea/src/main/kotlin/com/synebula/gaea/bus/Subscriber.kt +++ b/src/gaea/src/main/kotlin/com/synebula/gaea/bus/Subscriber.kt @@ -1,16 +1,4 @@ -/* - * Copyright (C) 2014 The Guava Authors - * - * 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 com.synebula.gaea.bus import com.synebula.gaea.exception.NoticeUserException diff --git a/src/gaea/src/main/kotlin/com/synebula/gaea/bus/SubscriberExceptionContext.kt b/src/gaea/src/main/kotlin/com/synebula/gaea/bus/SubscriberExceptionContext.kt index d0b9bb4..ac40cae 100644 --- a/src/gaea/src/main/kotlin/com/synebula/gaea/bus/SubscriberExceptionContext.kt +++ b/src/gaea/src/main/kotlin/com/synebula/gaea/bus/SubscriberExceptionContext.kt @@ -1,16 +1,4 @@ -/* - * Copyright (C) 2013 The Guava Authors - * - * 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 com.synebula.gaea.bus import java.lang.reflect.Method diff --git a/src/gaea/src/main/kotlin/com/synebula/gaea/bus/SubscriberExceptionHandler.kt b/src/gaea/src/main/kotlin/com/synebula/gaea/bus/SubscriberExceptionHandler.kt index 14c40f2..d3ef501 100644 --- a/src/gaea/src/main/kotlin/com/synebula/gaea/bus/SubscriberExceptionHandler.kt +++ b/src/gaea/src/main/kotlin/com/synebula/gaea/bus/SubscriberExceptionHandler.kt @@ -1,16 +1,4 @@ -/* - * Copyright (C) 2013 The Guava Authors - * - * 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 com.synebula.gaea.bus /** diff --git a/src/gaea/src/main/kotlin/com/synebula/gaea/bus/SubscriberRegistry.kt b/src/gaea/src/main/kotlin/com/synebula/gaea/bus/SubscriberRegistry.kt index b08cd2f..78c91d8 100644 --- a/src/gaea/src/main/kotlin/com/synebula/gaea/bus/SubscriberRegistry.kt +++ b/src/gaea/src/main/kotlin/com/synebula/gaea/bus/SubscriberRegistry.kt @@ -1,16 +1,4 @@ -/* - * Copyright (C) 2014 The Guava Authors - * - * 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 com.synebula.gaea.bus import com.synebula.gaea.data.message.messageTopic diff --git a/src/gaea/src/main/kotlin/com/synebula/gaea/data/cache/ICache.kt b/src/gaea/src/main/kotlin/com/synebula/gaea/data/cache/ICache.kt index 48aa871..bac951e 100644 --- a/src/gaea/src/main/kotlin/com/synebula/gaea/data/cache/ICache.kt +++ b/src/gaea/src/main/kotlin/com/synebula/gaea/data/cache/ICache.kt @@ -7,8 +7,68 @@ package com.synebula.gaea.data.cache * @version 0.0.1 * @since 2016年8月15日 下午4:53:19 */ -interface ICache { - fun add(key: String, value: CacheEntity) +interface ICache { - operator fun get(key: String): CacheEntity? + /** + * 添加一条缓存记录 + * + * @param key 缓存key + * @param value 需要缓存的值 + */ + fun add(key: K, value: V) + + /** + * 添加多条缓存记录 + * + * @param entries 需要添加的多条缓存记录Map + */ + fun add(entries: Map) + + /** + * 尝试获取缓存记录值 + * + * @param key 缓存key + */ + operator fun get(key: K): V? + + /** + * 获取给出列表key中存在的缓存元素 + * + * @param keys 尝试获取的key列表 + */ + fun get(keys: Iterable): Map + + /** + * 清除所有缓存记录 + */ + fun clear() + + + /** + * 清除给出key的缓存 + * + * @param key 需要清楚的缓存key + */ + fun remove(key: K) + + /** + * 清除给出列表key的缓存 + * + * @param keys 需要清楚的缓存key列表 + */ + fun remove(keys: Iterable) + + /** + * 判断key是否存在 + * + * @param key 需要判断的key + */ + fun exists(key: K): Boolean + + /** + * 判断keys是否存在 + * + * @param keys 需要判断的缓存key列表 + */ + fun exists(keys: Iterable): Map } diff --git a/src/gaea/src/main/kotlin/com/synebula/gaea/data/message/HttpMessage.kt b/src/gaea/src/main/kotlin/com/synebula/gaea/data/message/HttpMessage.kt index ae67473..89160b0 100644 --- a/src/gaea/src/main/kotlin/com/synebula/gaea/data/message/HttpMessage.kt +++ b/src/gaea/src/main/kotlin/com/synebula/gaea/data/message/HttpMessage.kt @@ -3,28 +3,24 @@ package com.synebula.gaea.data.message import com.synebula.gaea.data.serialization.json.IJsonSerializer -class HttpMessage() : DataMessage() { +class HttpMessage(private var serializer: IJsonSerializer) : DataMessage() { - var serializer: IJsonSerializer? = null - constructor(data: Any, serializer: IJsonSerializer? = null) : this() { + constructor(data: Any, serializer: IJsonSerializer) : this(serializer) { this.data = data - this.serializer = serializer } - constructor(status: Int, message: String, serializer: IJsonSerializer? = null) : this() { + constructor(status: Int, message: String, serializer: IJsonSerializer) : this(serializer) { this.status = status this.message = message - this.serializer = serializer } - constructor(status: Int, data: Any, message: String, serializer: IJsonSerializer? = null) : this( + constructor(status: Int, data: Any, message: String, serializer: IJsonSerializer) : this( status, message, serializer ) { this.data = data - this.serializer = serializer } fun load(msg: DataMessage<*>) { @@ -34,6 +30,6 @@ class HttpMessage() : DataMessage() { } override fun toString(): String { - return serializer?.serialize(this) ?: super.toString() + return serializer.serialize(this) } } \ No newline at end of file diff --git a/src/gaea/src/main/kotlin/com/synebula/gaea/data/message/HttpMessageFactory.kt b/src/gaea/src/main/kotlin/com/synebula/gaea/data/message/HttpMessageFactory.kt new file mode 100644 index 0000000..32efffb --- /dev/null +++ b/src/gaea/src/main/kotlin/com/synebula/gaea/data/message/HttpMessageFactory.kt @@ -0,0 +1,22 @@ +package com.synebula.gaea.data.message + +import com.synebula.gaea.data.serialization.json.IJsonSerializer + +class HttpMessageFactory(private var serializer: IJsonSerializer) { + + fun create(): HttpMessage { + return HttpMessage(serializer) + } + + fun create(data: Any): HttpMessage { + return HttpMessage(data, serializer) + } + + fun create(status: Int, message: String): HttpMessage { + return HttpMessage(status, message, serializer) + } + + fun create(status: Int, data: Any, message: String): HttpMessage { + return HttpMessage(status, data, message, serializer) + } +} \ No newline at end of file