This commit is contained in:
2026-04-14 16:27:47 +08:00
commit 4b38a4c952
134 changed files with 7478 additions and 0 deletions

View File

@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.k12study</groupId>
<artifactId>k12study-backend</artifactId>
<version>0.1.0-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<artifactId>common-security</artifactId>
<dependencies>
<dependency>
<groupId>com.k12study</groupId>
<artifactId>common-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,82 @@
package com.k12study.common.security.config;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "auth")
public class AuthProperties {
private boolean enabled = true;
private boolean gatewayMode = false;
private String tokenHeader = "Authorization";
private String tokenPrefix = "Bearer ";
private String secret = "k12study-dev-secret-k12study-dev-secret";
private Duration accessTokenTtl = Duration.ofHours(12);
private Duration refreshTokenTtl = Duration.ofDays(7);
private List<String> whitelist = new ArrayList<>(List.of("/actuator/**", "/auth/login", "/auth/refresh"));
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public boolean isGatewayMode() {
return gatewayMode;
}
public void setGatewayMode(boolean gatewayMode) {
this.gatewayMode = gatewayMode;
}
public String getTokenHeader() {
return tokenHeader;
}
public void setTokenHeader(String tokenHeader) {
this.tokenHeader = tokenHeader;
}
public String getTokenPrefix() {
return tokenPrefix;
}
public void setTokenPrefix(String tokenPrefix) {
this.tokenPrefix = tokenPrefix;
}
public String getSecret() {
return secret;
}
public void setSecret(String secret) {
this.secret = secret;
}
public Duration getAccessTokenTtl() {
return accessTokenTtl;
}
public void setAccessTokenTtl(Duration accessTokenTtl) {
this.accessTokenTtl = accessTokenTtl;
}
public Duration getRefreshTokenTtl() {
return refreshTokenTtl;
}
public void setRefreshTokenTtl(Duration refreshTokenTtl) {
this.refreshTokenTtl = refreshTokenTtl;
}
public List<String> getWhitelist() {
return whitelist;
}
public void setWhitelist(List<String> whitelist) {
this.whitelist = whitelist;
}
}

View File

@@ -0,0 +1,16 @@
package com.k12study.common.security.config;
import com.k12study.common.security.jwt.JwtTokenProvider;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@EnableConfigurationProperties(AuthProperties.class)
public class SecurityAutoConfiguration {
@Bean
public JwtTokenProvider jwtTokenProvider(AuthProperties authProperties) {
return new JwtTokenProvider(authProperties);
}
}

View File

@@ -0,0 +1,10 @@
package com.k12study.common.security.context;
public record RequestUserContext(
String userId,
String username,
String displayName,
String tenantId,
String deptId
) {
}

View File

@@ -0,0 +1,20 @@
package com.k12study.common.security.context;
public final class RequestUserContextHolder {
private static final ThreadLocal<RequestUserContext> CONTEXT = new ThreadLocal<>();
private RequestUserContextHolder() {
}
public static void set(RequestUserContext context) {
CONTEXT.set(context);
}
public static RequestUserContext get() {
return CONTEXT.get();
}
public static void clear() {
CONTEXT.remove();
}
}

View File

@@ -0,0 +1,49 @@
package com.k12study.common.security.jwt;
import com.k12study.common.security.config.AuthProperties;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.util.Date;
import javax.crypto.SecretKey;
public class JwtTokenProvider {
private final AuthProperties authProperties;
private final SecretKey secretKey;
public JwtTokenProvider(AuthProperties authProperties) {
this.authProperties = authProperties;
this.secretKey = Keys.hmacShaKeyFor(authProperties.getSecret().getBytes(StandardCharsets.UTF_8));
}
public String createAccessToken(JwtUserPrincipal principal) {
Instant now = Instant.now();
return Jwts.builder()
.subject(principal.userId())
.claim("username", principal.username())
.claim("displayName", principal.displayName())
.claim("tenantId", principal.tenantId())
.claim("deptId", principal.deptId())
.issuedAt(Date.from(now))
.expiration(Date.from(now.plus(authProperties.getAccessTokenTtl())))
.signWith(secretKey)
.compact();
}
public JwtUserPrincipal parse(String token) {
Claims claims = Jwts.parser()
.verifyWith(secretKey)
.build()
.parseSignedClaims(token)
.getPayload();
return new JwtUserPrincipal(
claims.getSubject(),
claims.get("username", String.class),
claims.get("displayName", String.class),
claims.get("tenantId", String.class),
claims.get("deptId", String.class)
);
}
}

View File

@@ -0,0 +1,10 @@
package com.k12study.common.security.jwt;
public record JwtUserPrincipal(
String userId,
String username,
String displayName,
String tenantId,
String deptId
) {
}