diff --git a/pom.xml b/pom.xml
index 4047a8b..77b81b0 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
com.xiang
- xservice-basic
+ xservice-parent-starter
2.0
pom
@@ -86,6 +86,7 @@
com.github.pagehelper
pagehelper-spring-boot-starter
+ 1.4.7
diff --git a/xs-server/src/main/resources/keys/rsa-private.pem b/xs-server/src/main/resources/keys/rsa-private.pem
new file mode 100644
index 0000000..d132742
--- /dev/null
+++ b/xs-server/src/main/resources/keys/rsa-private.pem
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQD7JdqQe8W8byYT
+KuPK7STLYO5TTe5uWhHj1SLq7Xoj1y0agO+x1X3/dUA1X/k5Dd2rmkmIU2VoB/9c
+aoE5naXUhVfE9wDsl5AKdWRCn1PKvuofd/0kuGFS44tNQcLGtDD7e8CQQJZJPZVS
+Z8F2nlHjYSTUPVvi77hKG21qoZLEa0F+TYfqgOohabQtxeBAnyRW50/g281Bqd3w
+ntfM0aKfW9fsFhJnk+AlXWaqxQKkhtzMmKVeqLNfJnXvhgUA6QbnzHPtPxF+fLEx
+BNnBEPF8CsQFVdt1TtsQlfCzVMTMibZU2CcwtqHUP1maEcH5zV5whPfgkahejiNm
+L08Jw74VAgMBAAECggEACJHb0pGHh4IkF/XmM6Qs6xMFYmqmvRkf0JjoTYDl9+JW
+Y9RMYKfqy+Yius+GSNRjPWS7p38MHkiysGL/F7uYyCwvhwU3x+kugM2+/+gWqyaT
+WnwYgZ4YXIRu2ieFr4xq1symnzO14nDny4uqB9PEZFd7wS4I0ZShe5yLM6Yai88V
+0Jm6Hi9RcC5efiE4tWismBKaP13WXamAVySROW30lEaMgyI66DNYgs+RHiZgAFP7
+u7raUD07xrk6eV6YnG/9EvS/oqV+IPEacY+bP3ZUqvPMI50tLTEVN1yJXrr4T3kS
+W1TiApaL87rdDCYem7rtIury+JcSadaI6lwP5OhmLwKBgQD/4mSrFi30IzD6LFam
+N7CYBxSleWgha2fHuycDGNGcXDCn3y4JuqTMRpV1c9F5emPPv1E3ELj4plz/NnVr
+67SkCs3nqoSRjyXJKhN/kJqM/NB+Ic1UgXeI+wGMkfkUHrQ6T6SghYVxWW+hQKm8
+IeV7aVJiM01/Ze938cnJuWd17wKBgQD7QumZzRTMkmnmSFCpOhs2Y3B8JYrJsJXY
+PeYkxea/7brDyuIdWKt0kl9EvpsrIzTe0t4LYV3Vmmfh15PNZp4PEr30NEBxeVOO
+HoglNfyJgP/nvhOGYesNhqPlK96/ajEvu7FpFHwDje5RKRWxCK5qhZneDy0ppjWb
+6seshN1wOwKBgQDqNPxxP/bFu6Qrh4Oz1cs0C18RakMuO5Gc1acKhZ/tntAGBxer
+XgNS2dQY0e5MYwKSdwlN/mdfZ149Vko5gl8vupfmUEPQuxYZvwJjwyZCn2/x0tyO
+WYXggeZUFJPHn6bUrGsBZdTS/8pV7Mqu4NOblrYKHez0C4gY390TXzjcTwKBgQDR
+mD6fYrjf8Z7PTzGiCOucUhUKKpL8rgZBbVknAcL8BYZPP1Whn07fHh7EjK+Jq4O2
+AHbjTWRmA7h2Z0tPAzQEZOD57gB35/pwSj3NtJwl4+sU2LUW22WlUdQ0HoVgbWf8
+ZniWrFTK7kGHiFsk45YDG9F/sG8/F/wORSotWmQR8wKBgQDqrlyvMCwiJHW7vOPs
+ih+utzIvtZ8D4fxzEFluTUqubAAl3N+81NuRJuEFIJLjIAeTNOHj1IdlPj5oe0aa
+IYOzoB2+xJxnLPbvI1tTat/pqgXxPY24/9c9rBoTCsJboTPb0fMh1nHxTZvny4tB
+jP7d5EBvIMWnCEuTo4y39ZFsMA==
+-----END PRIVATE KEY-----
diff --git a/xs-server/src/main/resources/keys/rsa-public.pem b/xs-server/src/main/resources/keys/rsa-public.pem
new file mode 100644
index 0000000..bbf9565
--- /dev/null
+++ b/xs-server/src/main/resources/keys/rsa-public.pem
@@ -0,0 +1,9 @@
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA+yXakHvFvG8mEyrjyu0k
+y2DuU03ubloR49Ui6u16I9ctGoDvsdV9/3VANV/5OQ3dq5pJiFNlaAf/XGqBOZ2l
+1IVXxPcA7JeQCnVkQp9Tyr7qH3f9JLhhUuOLTUHCxrQw+3vAkECWST2VUmfBdp5R
+42Ek1D1b4u+4ShttaqGSxGtBfk2H6oDqIWm0LcXgQJ8kVudP4NvNQand8J7XzNGi
+n1vX7BYSZ5PgJV1mqsUCpIbczJilXqizXyZ174YFAOkG58xz7T8RfnyxMQTZwRDx
+fArEBVXbdU7bEJXws1TEzIm2VNgnMLah1D9ZmhHB+c1ecIT34JGoXo4jZi9PCcO+
+FQIDAQAB
+-----END PUBLIC KEY-----
diff --git a/xs-service/src/main/java/com/xiang/xservice/auth/service/config/AuthorizationServerConfig.java b/xs-service/src/main/java/com/xiang/xservice/auth/service/config/AuthorizationServerConfig.java
index 03ceed9..2e7ca1e 100644
--- a/xs-service/src/main/java/com/xiang/xservice/auth/service/config/AuthorizationServerConfig.java
+++ b/xs-service/src/main/java/com/xiang/xservice/auth/service/config/AuthorizationServerConfig.java
@@ -1,10 +1,5 @@
package com.xiang.xservice.auth.service.config;
-import com.nimbusds.jose.jwk.JWKSet;
-import com.nimbusds.jose.jwk.RSAKey;
-import com.nimbusds.jose.jwk.source.JWKSource;
-import com.nimbusds.jose.proc.SecurityContext;
-import com.xiang.xservice.basic.utils.JwkUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
@@ -22,9 +17,6 @@ import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
-import org.springframework.security.oauth2.jwt.JwtDecoder;
-import org.springframework.security.oauth2.jwt.JwtEncoder;
-import org.springframework.security.oauth2.jwt.NimbusJwtEncoder;
import org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
@@ -138,18 +130,6 @@ public class AuthorizationServerConfig {
return repository;
}
- @Bean
- public JWKSource jwkSource() {
- RSAKey rsaKey = JwkUtils.generateRsa();
- JWKSet jwkSet = new JWKSet(rsaKey);
- return (jwkSelector, securityContext) -> jwkSelector.select(jwkSet);
- }
-
- @Bean
- public JwtEncoder jwtEncoder(JWKSource jwkSource) {
- return new NimbusJwtEncoder(jwkSource);
- }
-
@Bean
public AuthorizationServerSettings authorizationServerSettings() {
return AuthorizationServerSettings.builder()
@@ -157,9 +137,5 @@ public class AuthorizationServerConfig {
.build();
}
- @Bean
- public JwtDecoder jwtDecoder(JWKSource jwkSource) {
- return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);
- }
}
diff --git a/xs-service/src/main/java/com/xiang/xservice/auth/service/config/JwtConfig.java b/xs-service/src/main/java/com/xiang/xservice/auth/service/config/JwtConfig.java
new file mode 100644
index 0000000..c9776ae
--- /dev/null
+++ b/xs-service/src/main/java/com/xiang/xservice/auth/service/config/JwtConfig.java
@@ -0,0 +1,71 @@
+package com.xiang.xservice.auth.service.config;
+
+import com.nimbusds.jose.jwk.JWKSet;
+import com.nimbusds.jose.jwk.RSAKey;
+import com.nimbusds.jose.jwk.source.JWKSource;
+import com.nimbusds.jose.proc.SecurityContext;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.security.oauth2.jwt.JwtDecoder;
+import org.springframework.security.oauth2.jwt.JwtEncoder;
+import org.springframework.security.oauth2.jwt.NimbusJwtEncoder;
+import org.springframework.security.oauth2.server.authorization.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
+
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.security.KeyFactory;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.Base64;
+
+@Configuration
+public class JwtConfig {
+
+ private RSAPrivateKey loadPrivateKey(String classpath) throws Exception {
+ InputStream is = new ClassPathResource(classpath).getInputStream();
+ String key = new String(is.readAllBytes(), StandardCharsets.UTF_8)
+ .replace("-----BEGIN PRIVATE KEY-----", "")
+ .replace("-----END PRIVATE KEY-----", "")
+ .replaceAll("\\s+", "");
+ PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(key));
+ return (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(spec);
+ }
+
+ private RSAPublicKey loadPublicKey(String classpath) throws Exception {
+ InputStream is = new ClassPathResource(classpath).getInputStream();
+ String key = new String(is.readAllBytes(), StandardCharsets.UTF_8)
+ .replace("-----BEGIN PUBLIC KEY-----", "")
+ .replace("-----END PUBLIC KEY-----", "")
+ .replaceAll("\\s+", "");
+ X509EncodedKeySpec spec = new X509EncodedKeySpec(Base64.getDecoder().decode(key));
+ return (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(spec);
+ }
+
+ @Bean
+ public JWKSource jwkSource() throws Exception {
+ // 使用RSA对称加密进行加密
+ RSAPublicKey publicKey = loadPublicKey("keys/rsa-public.pem");
+ RSAPrivateKey privateKey = loadPrivateKey("keys/rsa-private.pem");
+
+ RSAKey rsaKey = new RSAKey.Builder(publicKey)
+ .privateKey(privateKey)
+ .keyID("xservice")
+ .build();
+
+ JWKSet jwkSet = new JWKSet(rsaKey);
+ return (jwkSelector, ctx) -> jwkSelector.select(jwkSet);
+ }
+
+ @Bean
+ public JwtEncoder jwtEncoder(JWKSource jwkSource) {
+ return new NimbusJwtEncoder(jwkSource);
+ }
+
+ @Bean
+ public JwtDecoder jwtDecoder(JWKSource jwkSource) {
+ return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);
+ }
+}
diff --git a/xs-service/src/main/java/com/xiang/xservice/auth/service/constants/RedisConstant.java b/xs-service/src/main/java/com/xiang/xservice/auth/service/constants/RedisConstant.java
index c7ccfd0..3cfd2ab 100644
--- a/xs-service/src/main/java/com/xiang/xservice/auth/service/constants/RedisConstant.java
+++ b/xs-service/src/main/java/com/xiang/xservice/auth/service/constants/RedisConstant.java
@@ -2,6 +2,7 @@ package com.xiang.xservice.auth.service.constants;
public class RedisConstant {
+ public static final String LOGIN_TOKEN = "login:token:";
public static final String XS_PERMISSION_ROLE = "auth:permission:role";
public static final String XS_SMS_CODE_KEY = "auth:sms:code:key:";
public static final String XS_CAPTCHA_CODE_KEY = "auth:captcha:code:key:";
diff --git a/xs-service/src/main/java/com/xiang/xservice/auth/service/entity/XUser.java b/xs-service/src/main/java/com/xiang/xservice/auth/service/entity/XUser.java
index 9b70be0..6e7d4f8 100644
--- a/xs-service/src/main/java/com/xiang/xservice/auth/service/entity/XUser.java
+++ b/xs-service/src/main/java/com/xiang/xservice/auth/service/entity/XUser.java
@@ -95,4 +95,13 @@ public class XUser implements Serializable {
* 修改时间
*/
private LocalDateTime updateTime;
+
+ /**
+ * token
+ */
+ private String token;
+ /**
+ * 刷新token
+ */
+ private String refreshToken;
}
diff --git a/xs-service/src/main/java/com/xiang/xservice/auth/service/service/impl/XUserServiceImpl.java b/xs-service/src/main/java/com/xiang/xservice/auth/service/service/impl/XUserServiceImpl.java
index 990f7e7..8424ccd 100644
--- a/xs-service/src/main/java/com/xiang/xservice/auth/service/service/impl/XUserServiceImpl.java
+++ b/xs-service/src/main/java/com/xiang/xservice/auth/service/service/impl/XUserServiceImpl.java
@@ -17,6 +17,7 @@ import com.xiang.xservice.auth.api.dto.resp.LoginResp;
import com.xiang.xservice.auth.api.dto.resp.RegisterResp;
import com.xiang.xservice.auth.api.dto.resp.UserDTO;
import com.xiang.xservice.auth.api.dto.resp.UserResp;
+import com.xiang.xservice.auth.service.constants.RedisConstant;
import com.xiang.xservice.auth.service.convert.XDeptConvert;
import com.xiang.xservice.auth.service.convert.XPermissionConvert;
import com.xiang.xservice.auth.service.convert.XRoleConvert;
@@ -40,15 +41,19 @@ import com.xiang.xservice.auth.service.service.XUserService;
import com.xiang.xservice.basic.enums.DelStatusEnum;
import com.xiang.xservice.basic.exception.BusinessException;
import com.xiang.xservice.basic.utils.PrimaryKeyUtils;
+import com.xiang.xservice.cache.service.IRedisService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.jwt.JwtClaimsSet;
+import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.jwt.JwtEncoder;
import org.springframework.security.oauth2.jwt.JwtEncoderParameters;
import org.springframework.stereotype.Service;
@@ -59,6 +64,7 @@ import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.Objects;
+import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@Slf4j
@@ -80,6 +86,8 @@ public class XUserServiceImpl implements XUserService {
private final XRolePermissionMapper rolePermissionMapper;
private final XPermissionMapper permissionMapper;
private final XPermissionConvert permissionConvert;
+ private final IRedisService redisService;
+ private final JwtDecoder jwtDecoder;
@Override
public LoginResp login(LoginRequest request) {
@@ -106,6 +114,21 @@ public class XUserServiceImpl implements XUserService {
}
roleCodes.addAll(roles.stream().map(XRole::getCode).toList());
}
+ if (StringUtils.isNotBlank(user.getToken())) {
+ try {
+ Jwt jwt = jwtDecoder.decode(user.getToken());
+ if (Objects.nonNull(jwt.getExpiresAt())) {
+ if (jwt.getExpiresAt().isAfter(Instant.now())) {
+ LoginResp loginResp = new LoginResp();
+ loginResp.setToken(user.getToken());
+ loginResp.setUsername(request.getUsername());
+ return loginResp;
+ }
+ }
+ } catch (Exception e) {
+ log.info("jwt解析token失败", e);
+ }
+ }
// 生成 token
Instant now = Instant.now();
@@ -121,11 +144,26 @@ public class XUserServiceImpl implements XUserService {
.claim("authorities", roleCodes)
.build();
+ JwtClaimsSet refreshClaims = JwtClaimsSet.builder()
+ // 对应 ProviderSettings.issuer
+ .issuedAt(now)
+ .expiresAt(now.plus(24, ChronoUnit.HOURS))
+ // 自定义 scope
+ .claim("tenantId", user.getTenantId())
+ .build();
+
// 2. 编码生成 token
String token = jwtEncoder.encode(JwtEncoderParameters.from(claims)).getTokenValue();
+ String refreshToken = jwtEncoder.encode(JwtEncoderParameters.from(refreshClaims)).getTokenValue();
LoginResp loginResp = new LoginResp();
loginResp.setToken(token);
loginResp.setUsername(request.getUsername());
+ // 3. redis缓存token
+ redisService.set(RedisConstant.LOGIN_TOKEN + request.getUsername(), token, 3, TimeUnit.HOURS);
+ // 4. db 存储token
+ user.setToken(token);
+ user.setRefreshToken(refreshToken);
+ userMapper.update(user);
return loginResp;
}
diff --git a/xs-service/src/main/resources/mapper/user/XUserMapper.xml b/xs-service/src/main/resources/mapper/user/XUserMapper.xml
index 9f7feb2..a64f815 100644
--- a/xs-service/src/main/resources/mapper/user/XUserMapper.xml
+++ b/xs-service/src/main/resources/mapper/user/XUserMapper.xml
@@ -20,6 +20,8 @@
+
+
@@ -38,7 +40,9 @@
create_time,
update_by,
update_time,
- tenant_id
+ tenant_id,
+ token,
+ refresh_token
@@ -87,7 +91,13 @@
update_time,
- tenant_id
+ tenant_id,
+
+
+ token,
+
+
+ refreshToken
@@ -134,7 +144,13 @@
#{updateTime},
- #{tenantId}
+ #{tenantId},
+
+
+ #{token},
+
+
+ #{refreshToken}
@@ -165,7 +181,9 @@
create_time = #{createTime},
update_by = #{updateBy},
update_time = #{updateTime},
- tenant_id = #{tenantId}
+ tenant_id = #{tenantId},
+ token = #{token},
+ refresh_token = #{refreshToken}
WHERE id = #{id}