Add authentication

This commit is contained in:
2023-10-05 21:11:22 +08:00
parent 78de04713f
commit 8e5375ab30
24 changed files with 580 additions and 15 deletions

View File

@@ -0,0 +1,31 @@
package top.fatweb.api.util
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.servlet.mvc.condition.RequestCondition
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
import top.fatweb.api.annotation.ApiVersion
import java.lang.reflect.Method
class ApiResponseMappingHandlerMapping : RequestMappingHandlerMapping() {
private val versionFlag = "{apiVersion}"
private fun createCondition(clazz: Class<*>): RequestCondition<ApiVersionCondition>? {
val classRequestMapping = clazz.getAnnotation(RequestMapping::class.java)
classRequestMapping ?: let { return null }
val mappingUrlBuilder = StringBuilder()
if (classRequestMapping.value.isNotEmpty()) {
mappingUrlBuilder.append(classRequestMapping.value[0])
}
val mappingUrl = mappingUrlBuilder.toString()
if (!mappingUrl.contains(versionFlag)) {
return null
}
val apiVersion = clazz.getAnnotation(ApiVersion::class.java)
return if (apiVersion == null) ApiVersionCondition(1) else ApiVersionCondition(apiVersion.version)
}
override fun getCustomMethodCondition(method: Method): RequestCondition<*>? = createCondition(method.javaClass)
override fun getCustomTypeCondition(handlerType: Class<*>): RequestCondition<*>? = createCondition(handlerType)
}

View File

@@ -0,0 +1,26 @@
package top.fatweb.api.util
import jakarta.servlet.http.HttpServletRequest
import org.springframework.web.servlet.mvc.condition.RequestCondition
import java.util.regex.Pattern
class ApiVersionCondition(private val apiVersion: Int) : RequestCondition<ApiVersionCondition> {
private val versionPrefixPattern: Pattern = Pattern.compile(".*v(\\d+).*")
override fun combine(other: ApiVersionCondition): ApiVersionCondition = ApiVersionCondition(other.apiVersion)
override fun getMatchingCondition(request: HttpServletRequest): ApiVersionCondition? {
val matcher = versionPrefixPattern.matcher(request.requestURI)
if (matcher.find()) {
val version = matcher.group(1).toInt()
if (version >= this.apiVersion) {
return this
}
}
return null
}
override fun compareTo(other: ApiVersionCondition, request: HttpServletRequest): Int =
other.apiVersion - this.apiVersion
}

View File

@@ -0,0 +1,67 @@
package top.fatweb.api.util
import com.auth0.jwt.JWT
import com.auth0.jwt.algorithms.Algorithm
import com.auth0.jwt.interfaces.DecodedJWT
import top.fatweb.api.constant.SecurityConstants
import java.util.*
import java.util.concurrent.TimeUnit
import javax.crypto.spec.SecretKeySpec
object JwtUtil {
private fun getUUID() = UUID.randomUUID().toString().replace("-", "")
/**
* 生成加密后的秘钥 secretKey
*
* @return 密钥
*/
private fun generalKey(): SecretKeySpec {
val encodeKey = Base64.getDecoder().decode(SecurityConstants.jwtKey)
return SecretKeySpec(encodeKey, 0, encodeKey.size, "AES")
}
private fun algorithm(): Algorithm = Algorithm.HMAC256(generalKey().toString())
/**
* 创建 token
*
* @param subject token 中存放的数据json格式)
* @param ttl token 生存时间
* @param timeUnit ttl 时间单位
* @param uuid 唯一 ID
* @return jwt 串
*/
fun createJwt(
subject: String,
ttl: Long = SecurityConstants.jwtTtl,
timeUnit: TimeUnit = SecurityConstants.jwtTtlUnit,
uuid: String = getUUID()
): String? {
val nowMillis = System.currentTimeMillis()
val nowDate = Date(nowMillis)
val unitTtl = (ttl * when (timeUnit) {
TimeUnit.DAYS -> 24 * 60 * 60 * 1000
TimeUnit.HOURS -> 60 * 60 * 1000
TimeUnit.MINUTES -> 60 * 1000
TimeUnit.SECONDS -> 1000
TimeUnit.MILLISECONDS -> 1
TimeUnit.NANOSECONDS -> 1 / 1000
TimeUnit.MICROSECONDS -> 1 / 1000 / 1000
})
val expMillis = nowMillis + unitTtl
val expDate = Date(expMillis)
return JWT.create().withJWTId(uuid).withSubject(subject).withIssuer(SecurityConstants.jwtIssuer)
.withIssuedAt(nowDate).withExpiresAt(expDate).sign(algorithm())
}
/**
* 解析 jwt
*
* @param jwt jwt 串
* @return 解析内容
*/
fun parseJwt(jwt: String): DecodedJWT =
JWT.require(algorithm()).build().verify(jwt)
}

View File

@@ -0,0 +1,183 @@
package top.fatweb.api.util
import org.springframework.data.redis.core.BoundSetOperations
import org.springframework.data.redis.core.RedisTemplate
import org.springframework.stereotype.Component
import java.util.concurrent.TimeUnit
@Suppress("UNCHECKED_CAST")
@Component
class RedisUtil(private val redisTemplate: RedisTemplate<String, Any>) {
/**
* 设置有效时间
*
* @param key 缓存的键
* @param timeout 超时时间
* @param timeUnit 时间颗粒度
* @return true=设置成功false=设置失败
*/
fun setExpire(key: String, timeout: Long, timeUnit: TimeUnit = TimeUnit.SECONDS) =
redisTemplate.expire(key, timeout, timeUnit)
/**
* 获取有效时间
*
* @param key 缓存的键
* @return 有效时间
*/
fun getExpire(key: String, timeUnit: TimeUnit = TimeUnit.SECONDS) = redisTemplate.getExpire(key, timeUnit)
/**
* 判断 key 是否存在
*
* @param key 缓存的键
* @return true=存在; false=不存在
*/
fun hasKey(key: String) = redisTemplate.hasKey(key)
/**
* 获得缓存的基本对象列表
*
* @param pattern 字符串前缀
* @return 对象列表
*/
fun keys(pattern: String): Set<String> = redisTemplate.keys(pattern)
/**
* 缓存基本的对象Integer、String、实体类等
*
* @param key 缓存的键
* @param value 缓存的值
*/
fun setObject(key: String, value: Any) = redisTemplate.opsForValue().set(key, value)
/**
* 缓存基本的对象Integer、String、实体类等
*
* @param key 缓存的键
* @param value 缓存的值
* @param timeout 超时时间
* @param timeUnit 时间颗粒度
*/
fun setObject(key: String, value: Any, timeout: Long, timeUnit: TimeUnit = TimeUnit.SECONDS) =
redisTemplate.opsForValue().set(key, value, timeout, timeUnit)
/**
* 获得缓存的基本对象
*
* @param key 缓存的键
* @return 缓存的值
*/
fun <T> getObject(key: String) = redisTemplate.opsForValue().get(key) as? T
/**
* 删除单个对象
*
* @param key 缓存的键
* @return true=删除成功false=删除失败
*/
fun delObject(key: String) = redisTemplate.delete(key)
/**
* 删除对象集合
*
* @param collection 键集合
* @return 删除个数
*/
fun delObject(collection: Collection<String>) = redisTemplate.delete(collection)
/**
* 缓存 List 数据
*
* @param key 缓存的键
* @param dataList 缓存的 List 数据
* @return 缓存的个数
*/
fun setList(key: String, dataList: List<Any>) = redisTemplate.opsForList().rightPushAll(key, dataList)
/**
* 获得缓存的 List 数据
*
* @param key 缓存的键
* @return 缓存的键对应的 List 数据
*/
fun <T> getList(key: String): List<T>? = redisTemplate.opsForList().range(key, 0, -1) as? List<T>
/**
* 缓存 Set 数据
*
* @param key 缓存的键
* @param dataSet 缓存的 Set 数据
* @return 缓存数据的对象
*/
fun setSet(key: String, dataSet: Set<Any>): BoundSetOperations<String, Any> {
val boundSetOps: BoundSetOperations<String, Any> = redisTemplate.boundSetOps(key)
for (data in dataSet) {
boundSetOps.add(data)
}
return boundSetOps
}
/**
* 获得缓存的 Set 数据
*
* @param key 缓存的键
* @return 缓存的键对应的 Set 数据
*/
fun <T> getSet(key: String): Set<T>? = redisTemplate.opsForSet().members(key) as? Set<T>
/**
* 缓存 Map 数据
*
* @param key 缓存的键
* @param dataMap 缓存的 Map 数据
*/
fun setMap(key: String, dataMap: Map<String, Any>) = redisTemplate.opsForHash<String, Any>().putAll(key, dataMap)
/**
* 获得缓存的 Map 数据
*
* @param key 缓存的键
* @return 缓存的键对应的 Map 数据
*/
fun <T> getMap(key: String): Map<String, T>? =
redisTemplate.opsForHash<String, Any>().entries(key) as? Map<String, T>
/**
* 往 Hash 中存入数据
*
* @param key Redis 键
* @param hKey Hash 键
* @param value 值
*/
fun setMapValue(key: String, hKey: String, value: Any) =
redisTemplate.opsForHash<String, Any>().put(key, hKey, value)
/**
* 获取 Hash 中的数据
*
* @param key Redis 键
* @param hKey Hash 键
* @return Hash 中的对象
*/
fun <T> getMapValue(key: String, hKey: String) = redisTemplate.opsForHash<String, T>().get(key, hKey)
/**
* 删除 Hash 中的数据
*
* @param key Redis 键
* @param hKey Hash 键
*/
fun delMapValue(key: String, hKey: String) = redisTemplate.opsForHash<String, Any>().delete(key, hKey)
/**
* 获取多个 Hash 中的数据
*
* @param key Redis 键
* @param hKeys Hash 键集合
* @return Hash 对象集合
*/
fun <T> getMultiMapValue(key: String, hKeys: Collection<String>): List<T> =
redisTemplate.opsForHash<String, T>().multiGet(key, hKeys)
}

View File

@@ -0,0 +1,10 @@
package top.fatweb.api.util
import org.springframework.security.core.context.SecurityContextHolder
import top.fatweb.api.entity.permission.LoginUser
object WebUtil {
fun getLoginUser() = SecurityContextHolder.getContext().authentication.principal as LoginUser
fun getLoginUserId() = getLoginUser().user.id
}