feat:token 验签
This commit is contained in:
@@ -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<SecurityContext> jwkSource() {
|
||||
RSAKey rsaKey = JwkUtils.generateRsa();
|
||||
JWKSet jwkSet = new JWKSet(rsaKey);
|
||||
return (jwkSelector, securityContext) -> jwkSelector.select(jwkSet);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public JwtEncoder jwtEncoder(JWKSource<SecurityContext> 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<SecurityContext> jwkSource) {
|
||||
return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<SecurityContext> 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<SecurityContext> jwkSource) {
|
||||
return new NimbusJwtEncoder(jwkSource);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public JwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) {
|
||||
return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);
|
||||
}
|
||||
}
|
||||
@@ -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:";
|
||||
|
||||
@@ -95,4 +95,13 @@ public class XUser implements Serializable {
|
||||
* 修改时间
|
||||
*/
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
/**
|
||||
* token
|
||||
*/
|
||||
private String token;
|
||||
/**
|
||||
* 刷新token
|
||||
*/
|
||||
private String refreshToken;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
<result column="update_by" property="updateBy" />
|
||||
<result column="update_time" property="updateTime" />
|
||||
<result column="tenant_id" property="tenantId"/>
|
||||
<result column="token" property="token"/>
|
||||
<result column="refresh_token" property="refreshToken"/>
|
||||
</resultMap>
|
||||
|
||||
<sql id="Base_Column_List">
|
||||
@@ -38,7 +40,9 @@
|
||||
create_time,
|
||||
update_by,
|
||||
update_time,
|
||||
tenant_id
|
||||
tenant_id,
|
||||
token,
|
||||
refresh_token
|
||||
</sql>
|
||||
|
||||
<insert id="insert" useGeneratedKeys="true" keyColumn="id" keyProperty="id" parameterType="com.xiang.xservice.auth.service.entity.XUser">
|
||||
@@ -87,7 +91,13 @@
|
||||
update_time,
|
||||
</if>
|
||||
<if test="tenantId != null">
|
||||
tenant_id
|
||||
tenant_id,
|
||||
</if>
|
||||
<if test="token != null and token != ''">
|
||||
token,
|
||||
</if>
|
||||
<if test="refreshToken != null and refreshToken != ''">
|
||||
refreshToken
|
||||
</if>
|
||||
</trim>
|
||||
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
||||
@@ -134,7 +144,13 @@
|
||||
#{updateTime},
|
||||
</if>
|
||||
<if test="tenantId != null">
|
||||
#{tenantId}
|
||||
#{tenantId},
|
||||
</if>
|
||||
<if test="token != null and token != ''">
|
||||
#{token},
|
||||
</if>
|
||||
<if test="refreshToken != null and refreshToken != ''">
|
||||
#{refreshToken}
|
||||
</if>
|
||||
</trim>
|
||||
</insert>
|
||||
@@ -165,7 +181,9 @@
|
||||
<if test="null != createTime ">create_time = #{createTime},</if>
|
||||
<if test="null != updateBy and '' != updateBy">update_by = #{updateBy},</if>
|
||||
<if test="null != updateTime ">update_time = #{updateTime},</if>
|
||||
<if test="null != tenantId ">tenant_id = #{tenantId}</if>
|
||||
<if test="null != tenantId ">tenant_id = #{tenantId},</if>
|
||||
<if test="null != token and '' != token ">token = #{token},</if>
|
||||
<if test="null != refreshToken and '' != refreshToken ">refresh_token = #{refreshToken}</if>
|
||||
</set>
|
||||
WHERE id = #{id}
|
||||
</update>
|
||||
|
||||
Reference in New Issue
Block a user