thêm login

This commit is contained in:
nguyennt1 2025-11-15 17:31:58 +07:00
parent 6aef802cb8
commit c04313ec72
61 changed files with 695 additions and 131 deletions

View File

@ -8,6 +8,7 @@
<entry name="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.projectlombok/lombok/1.18.38/57f8f5e02e92a30fd21b80cbd426a4172b5f8e29/lombok-1.18.38.jar" /> <entry name="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.projectlombok/lombok/1.18.38/57f8f5e02e92a30fd21b80cbd426a4172b5f8e29/lombok-1.18.38.jar" />
</processorPath> </processorPath>
<module name="VegaHRM.Backend.vega-hrm-core.main" /> <module name="VegaHRM.Backend.vega-hrm-core.main" />
<module name="VegaHRM.Backend.vega-hrm-auth.main" />
</profile> </profile>
</annotationProcessing> </annotationProcessing>
<bytecodeTargetLevel target="21"> <bytecodeTargetLevel target="21">

9
.idea/modules.xml Normal file
View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/modules/vega-hrm-auth/VegaHRM.Backend.vega-hrm-auth.main.iml" filepath="$PROJECT_DIR$/.idea/modules/vega-hrm-auth/VegaHRM.Backend.vega-hrm-auth.main.iml" />
<module fileurl="file://$PROJECT_DIR$/.idea/modules/vega-hrm-core/VegaHRM.Backend.vega-hrm-core.main.iml" filepath="$PROJECT_DIR$/.idea/modules/vega-hrm-core/VegaHRM.Backend.vega-hrm-core.main.iml" />
</modules>
</component>
</project>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<module version="4">
<component name="AdditionalModuleElements">
<content url="file://$MODULE_DIR$/../../../vega-hrm-auth/build/generated/sources/annotationProcessor/java/main">
<sourceFolder url="file://$MODULE_DIR$/../../../vega-hrm-auth/build/generated/sources/annotationProcessor/java/main" isTestSource="false" generated="true" />
</content>
</component>
</module>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<module version="4">
<component name="AdditionalModuleElements">
<content url="file://$MODULE_DIR$/../../../vega-hrm-core/build/generated/sources/annotationProcessor/java/main">
<sourceFolder url="file://$MODULE_DIR$/../../../vega-hrm-core/build/generated/sources/annotationProcessor/java/main" isTestSource="false" generated="true" />
</content>
</component>
</module>

View File

@ -26,10 +26,13 @@ spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true spring.jpa.properties.hibernate.format_sql=true
logging.level.org.hibernate.SQL=DEBUG logging.level.org.hibernate.SQL=DEBUG
logging.level.org.hibernate.type.descriptor.sql=TRACE logging.level.org.hibernate.type.descriptor.sql=TRACE
#spring.data.redis.sentinel.master=redismaster
#spring.data.redis.sentinel.nodes=10.22.18.138:26379,10.22.18.139:26379,10.22.18.140:26379 # redis
#spring.data.redis.sentinel.password=dLF5ZnOeOHM6/pzTHezV+WNXU+F7ZBGu85f8bwzk redis.host=103.166.141.147
#spring.data.redis.sentinel.database=17 redis.port=6379
cors.allowed-origins=http://localhost:4201,http://localhost:4202,http://localhost:4200,http://localhost:4203 redis.password=VegaHrm@2025
redis.database=0
cors.allowed-origins=*
#vcb-lounge.redis.time-to-live=600 #vcb-lounge.redis.time-to-live=600
#spring.cache.type=simple #spring.cache.type=simple

View File

@ -1,5 +1,7 @@
plugins { plugins {
id 'java' id 'java'
id 'org.springframework.boot' version '3.4.0'
id 'io.spring.dependency-management' version '1.1.6'
} }
group = 'com.vega.hrm' group = 'com.vega.hrm'
@ -10,10 +12,30 @@ repositories {
} }
dependencies { dependencies {
testImplementation platform('org.junit:junit-bom:5.10.0')
testImplementation 'org.junit.jupiter:junit-jupiter' }
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa:3.4.0'
implementation 'org.springframework.boot:spring-boot-starter-web:3.4.0'
implementation 'org.projectlombok:lombok:1.18.38'
implementation('org.springframework.boot:spring-boot-starter:3.4.0') {
exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging'
}
implementation 'org.springframework.boot:spring-boot-starter-log4j2:3.4.0'
implementation 'org.springframework.boot:spring-boot-starter-validation:3.4.0'
implementation 'de.mkammerer:argon2-jvm:2.1'
annotationProcessor 'org.projectlombok:lombok:1.18.38'
implementation project(":vega-hrm-core")
implementation 'jakarta.annotation:jakarta.annotation-api:2.1.1'
} }
test { configurations {
useJUnitPlatform() all {
} // Loại bỏ Logback
exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging'
// Loại bỏ logging mặc đnh
exclude group: 'ch.qos.logback', module: 'logback-classic'
}
}

View File

@ -0,0 +1,82 @@
package com.vega.hrm;
import com.vega.hrm.dto.BoFunctionDto;
import com.vega.hrm.core.entities.BoFunction;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.SecureRandom;
import java.util.Comparator;
import java.util.List;
public class AuthHelper {
private AuthHelper() {
throw new IllegalStateException("Utility class");
}
private static final String UPPERCASE = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
private static final String LOWERCASE = "abcdefghijklmnopqrstuvwxyz";
private static final String DIGITS = "0123456789";
private static final String SPECIAL_CHARS = "!@#$%^&*()-_+=<>?/"; // tùy chọn
private static final String ALL_CHARS = UPPERCASE + LOWERCASE + DIGITS + SPECIAL_CHARS;
private static final SecureRandom random = new SecureRandom();
public static String hashPassword(String userName, String password) {
try {
var input = userName + password + "&3%@6v";
var digest = MessageDigest.getInstance("SHA-256");
var hash = digest.digest(input.getBytes(StandardCharsets.UTF_8));
var hexString = new StringBuilder();
for (var b : hash) {
var hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) {
hexString.append('0');
}
hexString.append(hex);
}
return hexString.toString();
} catch (Exception ex) {
return null;
}
}
public static List<BoFunctionDto> getChildrenFunction(BoFunction function, List<BoFunction> allFunctions) {
return allFunctions.stream()
.filter(x -> x.getParentId().equals(function.getId()))
.sorted(Comparator.comparingLong(BoFunction::getFunctionOrder))
.map(x -> new BoFunctionDto(x, getChildrenFunction(x, allFunctions)))
.toList();
}
public static String generateValidPassword(int length) {
if (length < 8 || length > 20) {
throw new IllegalArgumentException("Độ dài password phải từ 8 tới 20 ký tự.");
}
StringBuilder password = new StringBuilder(length);
password.append(pickRandomChar(UPPERCASE));
password.append(pickRandomChar(DIGITS));
password.append(pickRandomChar(SPECIAL_CHARS));
for (int i = 3; i < length; i++) {
password.append(pickRandomChar(ALL_CHARS));
}
return shuffleString(password.toString());
}
private static char pickRandomChar(String charSet) {
return charSet.charAt(random.nextInt(charSet.length()));
}
private static String shuffleString(String input) {
char[] chars = input.toCharArray();
for (int i = chars.length - 1; i > 0; i--) {
int index = random.nextInt(i + 1);
char temp = chars[index];
chars[index] = chars[i];
chars[i] = temp;
}
return new String(chars);
}
}

View File

@ -1,18 +0,0 @@
package com.vega.hrm;
//TIP To <b>Run</b> code, press <shortcut actionId="Run"/> or
// click the <icon src="AllIcons.Actions.Execute"/> icon in the gutter.
public class Main {
public static void main(String[] args) {
//TIP Press <shortcut actionId="ShowIntentionActions"/> with your caret at the highlighted text
// to see how IntelliJ IDEA suggests fixing it.
System.out.printf("Hello and welcome!");
for (int i = 1; i <= 5; i++) {
//TIP Press <shortcut actionId="Debug"/> to start debugging your code. We have set one <icon src="AllIcons.Debugger.Db_set_breakpoint"/> breakpoint
// for you, but you can always add more by pressing <shortcut actionId="ToggleLineBreakpoint"/>.
System.out.println("i = " + i);
}
}
}

View File

@ -0,0 +1,15 @@
package com.vega.hrm;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@SpringBootApplication(scanBasePackages = "com.vega.hrm")
@EnableTransactionManagement
public class VegaHrmAuthApplication {
public static void main(String[] args) {
SpringApplication.run(VegaHrmAuthApplication.class, args);
}
}

View File

@ -0,0 +1,29 @@
package com.vega.hrm.controller;
import com.vega.hrm.core.models.responses.BaseResponse;
import com.vega.hrm.request.user.CreateUserRequest;
import com.vega.hrm.request.user.LoginRequest;
import com.vega.hrm.service.UserService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/auth/user")
@RequiredArgsConstructor
public class UserController {
private final UserService userService;
@PostMapping("/login")
public ResponseEntity<BaseResponse<Object>> login(@RequestBody LoginRequest request) {
return ResponseEntity.ok(userService.login(request));
}
@PostMapping("/insert")
public ResponseEntity<BaseResponse<Boolean>> insert(@RequestBody CreateUserRequest request) {
return ResponseEntity.ok(userService.insert(request));
}
}

View File

@ -0,0 +1,30 @@
package com.vega.hrm.dto;
import com.vega.hrm.core.entities.BoFunction;
import java.util.List;
import java.util.UUID;
import lombok.Getter;
@Getter
public class BoFunctionDto {
private final UUID id;
private final String funcName;
private final Long funcOrder;
private final Long funcDisplay;
private final Long funcLevel;
private final UUID parentId;
private final String funcUrl;
private final List<BoFunctionDto> children;
public BoFunctionDto(BoFunction boFunction, List<BoFunctionDto> children) {
this.id = boFunction.getId();
this.funcName = boFunction.getFunctionName();
this.funcOrder = boFunction.getFunctionOrder();
this.funcDisplay = Long.valueOf(String.valueOf(boFunction.getFunctionDisplay()));
this.funcLevel = boFunction.getFunctionLevel();
this.parentId = boFunction.getParentId();
this.funcUrl = boFunction.getFunctionUrl();
this.children = children;
}
}

View File

@ -0,0 +1,14 @@
package com.vega.hrm.request.user;
import java.util.UUID;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class CreateUserRequest {
private String userName;
private String password;
private String fullName;
private String roleId;
}

View File

@ -0,0 +1,9 @@
package com.vega.hrm.request.user;
import lombok.Getter;
@Getter
public class LoginRequest {
private String userName;
private String password;
}

View File

@ -0,0 +1,17 @@
package com.vega.hrm.response;
import com.vega.hrm.core.entities.BoUser;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
@Data
@Builder
@AllArgsConstructor
public class LoginResponse {
private BoUser user;
private String token;
}

View File

@ -0,0 +1,86 @@
package com.vega.hrm.service;
import com.vega.hrm.AuthHelper;
import com.vega.hrm.core.constants.CommonConst;
import com.vega.hrm.core.entities.BoUser;
import com.vega.hrm.core.helpers.JwtHelper;
import com.vega.hrm.core.models.responses.BaseResponse;
import com.vega.hrm.core.repositories.CoreUserRepository;
import com.vega.hrm.core.service.RedisService;
import com.vega.hrm.request.user.CreateUserRequest;
import com.vega.hrm.request.user.LoginRequest;
import com.vega.hrm.response.LoginResponse;
import java.time.Instant;
import java.util.HashMap;
import java.util.UUID;
import lombok.RequiredArgsConstructor;
import org.apache.logging.log4j.util.Strings;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@RequiredArgsConstructor
public class UserService {
private final CoreUserRepository userRepository;
private final RedisService redisService;
public BaseResponse<Object> login(LoginRequest request) {
var user = userRepository.findByUserName(request.getUserName());
if (user != null && user.getPassword() != null) {
if (user.getPassword()
.equals(AuthHelper.hashPassword(request.getUserName(), request.getPassword()))) {
var uuid = UUID.randomUUID().toString();
user.setUuid(uuid);
user.setNumberOfFailedLogins(0L);
user.setLastLoginTime(Instant.now());
userRepository.save(user);
var claims = new HashMap<String, Object>();
claims.put("UserId", user.getId());
claims.put("UserName", user.getUserName());
var token = JwtHelper.generateToken(user.getUserName(), user.getId(), UUID.randomUUID());
if (Strings.isBlank(token)) {
return BaseResponse.internalSystemError();
}
redisService.hSet(CommonConst.TOKEN,user.getId().toString(),token);
var baseResponseLogin = BaseResponse.builder().code("00");
baseResponseLogin.data(
LoginResponse.builder()
.user(user)
.token(token)
.build());
return baseResponseLogin.build();
}
if (user.getStatus().equals("1")) {
user.setNumberOfFailedLogins(user.getNumberOfFailedLogins() + 1);
userRepository.save(user);
}
}
return BaseResponse.invalid(
"UserName or Password is valid. Please check.");
}
@Transactional
public BaseResponse<Boolean> insert(CreateUserRequest request) {
var user = userRepository.findByUserName(request.getUserName());
if (user != null) {
return BaseResponse.success("User created unsuccessful");
}
var newUser = new BoUser();
newUser.setId(UUID.randomUUID());
newUser.setUserName(request.getUserName());
newUser.setFullName(request.getFullName());
newUser.setRoleId(UUID.randomUUID());
newUser.setPassword(AuthHelper.hashPassword(request.getUserName(), request.getPassword()));
newUser.setStatus("1");
newUser.setPwdExpireDate(Instant.now().plusSeconds(90L * 24 * 60 * 60));
newUser.setCreatedDate(Instant.now());
newUser.setCreatedUser("system");
newUser.setNumberOfFailedLogins(0L);
newUser.setIsPasswordChanged(0L);
userRepository.save(newUser);
return BaseResponse.success("User created successfully");
}
}

View File

@ -0,0 +1,9 @@
spring.application.name=vega-hrm-auth
spring.jpa.open-in-view=false
server.port=8089
vega.hrm.postgre.enabled=true
vega.jpa.repository.basePackage=com.vega.hrm.core.repositories
vega.jpa.entity.basePackage=com.vega.hrm.core.entities
spring.config.import=file:config/shared.properties
logging.config=file:config/log4j2.properties

View File

@ -30,7 +30,8 @@ dependencies {
implementation('org.springframework.boot:spring-boot-starter') { implementation('org.springframework.boot:spring-boot-starter') {
exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging' exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging'
} }
implementation("org.springframework.data:spring-data-redis:3.5.5")
implementation 'io.lettuce:lettuce-core'
implementation 'org.postgresql:postgresql:42.7.3' implementation 'org.postgresql:postgresql:42.7.3'
implementation 'org.springframework.boot:spring-boot-starter-log4j2:3.4.0' implementation 'org.springframework.boot:spring-boot-starter-log4j2:3.4.0'
implementation 'org.modelmapper:modelmapper:3.2.1' implementation 'org.modelmapper:modelmapper:3.2.1'

View File

@ -1,4 +1,4 @@
package com.vega.hrm; package com.vega.hrm.core;
public class VegaHrmCoreApplication { public class VegaHrmCoreApplication {

View File

@ -0,0 +1,22 @@
package com.vega.hrm.core.configs;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.web.filter.CharacterEncodingFilter;
@Configuration
public class CharsetFilterConfig {
@Bean
public FilterRegistrationBean<CharacterEncodingFilter> encodingFilter() {
FilterRegistrationBean<CharacterEncodingFilter> bean = new FilterRegistrationBean<>();
CharacterEncodingFilter encodingFilter = new CharacterEncodingFilter();
encodingFilter.setEncoding("UTF-8");
encodingFilter.setForceEncoding(true);
bean.setFilter(encodingFilter);
bean.addUrlPatterns("/*");
bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
return bean;
}
}

View File

@ -0,0 +1,50 @@
// RedisConfig.java
package com.vega.hrm.core.configs;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
@Configuration
public class RedisConfig {
@Value("${redis.host:localhost}")
private String redisHost;
@Value("${redis.port:6379}")
private int redisPort;
@Value("${redis.password:}")
private String redisPassword;
@Value("${redis.database:0}")
private int redisDatabase;
@Bean
public LettuceConnectionFactory redisConnectionFactory() {
RedisStandaloneConfiguration config = new RedisStandaloneConfiguration();
config.setHostName(redisHost);
config.setPort(redisPort);
config.setDatabase(redisDatabase);
if (redisPassword != null && !redisPassword.isBlank()) {
config.setPassword(redisPassword);
}
return new LettuceConnectionFactory(config);
}
@Bean
public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory connectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
return template;
}
@Bean
public StringRedisTemplate stringRedisTemplate(LettuceConnectionFactory connectionFactory) {
return new StringRedisTemplate(connectionFactory);
}
}

View File

@ -1,4 +1,4 @@
package com.vega.hrm.configs; package com.vega.hrm.core.configs;
import com.zaxxer.hikari.HikariDataSource; import com.zaxxer.hikari.HikariDataSource;
import java.sql.Connection; import java.sql.Connection;
@ -26,7 +26,7 @@ import org.springframework.transaction.annotation.EnableTransactionManagement;
@Slf4j @Slf4j
@Order(2) @Order(2)
@EnableJpaRepositories( @EnableJpaRepositories(
basePackages = "${vega.jpa.repository.basePackage}", basePackages = "com.vega.hrm.core.repositories",
entityManagerFactoryRef = "vegaHrmEntityManager", entityManagerFactoryRef = "vegaHrmEntityManager",
transactionManagerRef = "vegaHrmTransactionManager" transactionManagerRef = "vegaHrmTransactionManager"
) )
@ -34,8 +34,8 @@ import org.springframework.transaction.annotation.EnableTransactionManagement;
@EnableTransactionManagement @EnableTransactionManagement
public class VegaDatabaseConfig { public class VegaDatabaseConfig {
@Value("${vega.jpa.entity.basePackage:com.core.entity}") // @Value("com.vega.hrm.core.entities")
private String entityBasePackage; // private String entityBasePackage;
@Value("${app.datasource.vega.hikari.maximum-pool-size:10}") @Value("${app.datasource.vega.hikari.maximum-pool-size:10}")
private Integer hikariMaximumPoolSize; private Integer hikariMaximumPoolSize;
@ -74,6 +74,13 @@ public class VegaDatabaseConfig {
} }
private DataSource configureHikariDataSource(DataSourceProperties properties) { private DataSource configureHikariDataSource(DataSourceProperties properties) {
String url = properties.getUrl();
if (url != null && url.contains("Asia/Saigon")) {
url = url.replace("Asia/Saigon", "Asia/Ho_Chi_Minh");
properties.setUrl(url);
log.warn("Rewrote JDBC URL timezone from Asia/Saigon to Asia/Ho_Chi_Minh");
}
var dataSource = properties var dataSource = properties
.initializeDataSourceBuilder() .initializeDataSourceBuilder()
.type(HikariDataSource.class) .type(HikariDataSource.class)
@ -101,7 +108,7 @@ public class VegaDatabaseConfig {
if (validationTimeout != null) { if (validationTimeout != null) {
dataSource.setValidationTimeout(validationTimeout); dataSource.setValidationTimeout(validationTimeout);
} }
dataSource.setConnectionInitSql("SET TIME ZONE 'Asia/Ho_Chi_Minh'");
// PostgreSQL driver // PostgreSQL driver
dataSource.setDriverClassName("org.postgresql.Driver"); dataSource.setDriverClassName("org.postgresql.Driver");
@ -119,7 +126,7 @@ public class VegaDatabaseConfig {
var em = new LocalContainerEntityManagerFactoryBean(); var em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource); em.setDataSource(dataSource);
em.setPackagesToScan(entityBasePackage); em.setPackagesToScan("com.vega.hrm.core.entities");
em.setPersistenceUnitName("db1"); em.setPersistenceUnitName("db1");
var vendorAdapter = new HibernateJpaVendorAdapter(); var vendorAdapter = new HibernateJpaVendorAdapter();
@ -140,7 +147,7 @@ public class VegaDatabaseConfig {
} }
private void loggingDatabaseSource(LocalContainerEntityManagerFactoryBean em) { private void loggingDatabaseSource(LocalContainerEntityManagerFactoryBean em) {
log.info("vcbDatabaseSourceConfig entityBasePackage : {}", entityBasePackage); log.info("vcbDatabaseSourceConfig entityBasePackage : {}", "com.vega.hrm.core.entities");
try (Connection connection = Objects.requireNonNull(em.getDataSource()).getConnection()) { try (Connection connection = Objects.requireNonNull(em.getDataSource()).getConnection()) {
log.info("Database URL: {}", connection.getMetaData().getURL()); log.info("Database URL: {}", connection.getMetaData().getURL());

View File

@ -0,0 +1,6 @@
package com.vega.hrm.core.constants;
public class CommonConst {
public static final String USER_ID = "user_id";
public static final String TOKEN = "token";
}

View File

@ -1,4 +1,4 @@
package com.vega.hrm.constants; package com.vega.hrm.core.constants;
public class KeySecurityConst { public class KeySecurityConst {
public static String KEY_SECURITY_DATABASE = "lDCfYiZ5rxH1G1ElIth9ppqED6MgaLKZ"; public static String KEY_SECURITY_DATABASE = "lDCfYiZ5rxH1G1ElIth9ppqED6MgaLKZ";

View File

@ -1,4 +1,4 @@
package com.vega.hrm.constants; package com.vega.hrm.core.constants;
public class ResponseCodeConst { public class ResponseCodeConst {
public static final String SUCCESS = "00"; public static final String SUCCESS = "00";

View File

@ -1,4 +1,4 @@
package com.vega.hrm.constants; package com.vega.hrm.core.constants;
public class ResponseMessageConst { public class ResponseMessageConst {
public static final String INTERNAL_SYSTEM_ERROR = "Lỗi hệ thống"; public static final String INTERNAL_SYSTEM_ERROR = "Lỗi hệ thống";

View File

@ -1,4 +1,4 @@
package com.vega.hrm.constants; package com.vega.hrm.core.constants;
import java.util.HashMap; import java.util.HashMap;

View File

@ -1,4 +1,4 @@
package com.vega.hrm.entity; package com.vega.hrm.core.entities;
import jakarta.persistence.Column; import jakarta.persistence.Column;
import jakarta.persistence.Entity; import jakarta.persistence.Entity;
@ -42,7 +42,7 @@ public class BoFunction {
@NotNull @NotNull
@Column(name = "parent_id", nullable = false) @Column(name = "parent_id", nullable = false)
private Long parentId; private UUID parentId;
@Size(max = 20) @Size(max = 20)
@NotNull @NotNull

View File

@ -1,4 +1,4 @@
package com.vega.hrm.entity; package com.vega.hrm.core.entities;
import jakarta.persistence.Column; import jakarta.persistence.Column;
import jakarta.persistence.Entity; import jakarta.persistence.Entity;

View File

@ -1,4 +1,4 @@
package com.vega.hrm.entity; package com.vega.hrm.core.entities;
import jakarta.persistence.Column; import jakarta.persistence.Column;
import jakarta.persistence.Entity; import jakarta.persistence.Entity;

View File

@ -1,4 +1,4 @@
package com.vega.hrm.entity; package com.vega.hrm.core.entities;
import jakarta.persistence.Column; import jakarta.persistence.Column;
import jakarta.persistence.Entity; import jakarta.persistence.Entity;

View File

@ -1,4 +1,4 @@
package com.vega.hrm.entity; package com.vega.hrm.core.entities;
import jakarta.persistence.Column; import jakarta.persistence.Column;
import jakarta.persistence.Entity; import jakarta.persistence.Entity;
@ -56,7 +56,7 @@ public class BoUser {
private String createdUser; private String createdUser;
@Column(name = "role_id") @Column(name = "role_id")
private Long roleId; private UUID roleId;
@NotNull @NotNull
@ColumnDefault("0") @ColumnDefault("0")

View File

@ -1,10 +1,10 @@
package com.vega.hrm.exceptions; package com.vega.hrm.core.exceptions;
import com.vega.hrm.constants.ResponseCodeConst; import com.vega.hrm.core.constants.ResponseCodeConst;
import com.vega.hrm.constants.ResponseMessageConst; import com.vega.hrm.core.constants.ResponseMessageConst;
import com.vega.hrm.models.responses.BaseResponse; import com.vega.hrm.core.models.responses.BaseResponse;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;

View File

@ -0,0 +1,78 @@
package com.vega.hrm.core.filters;
import com.vega.hrm.core.constants.CommonConst;
import com.vega.hrm.core.constants.KeySecurityConst;
import com.vega.hrm.core.helpers.JwtHelper;
import com.vega.hrm.core.helpers.LogHelper;
import com.vega.hrm.core.service.RedisService;
import io.jsonwebtoken.ExpiredJwtException;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import org.apache.logging.log4j.ThreadContext;
import org.apache.logging.log4j.util.Strings;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
@Component
@Order(2)
@RequiredArgsConstructor
public class AuthorizationFilter extends OncePerRequestFilter {
private static final List<String> EXCLUDE_URIS = List.of("/auth/user/login");
private final RedisService redisService;
@Override
protected void doFilterInternal(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response, @NonNull FilterChain filterChain) {
try {
var uri = request.getRequestURI();
if (EXCLUDE_URIS.contains(uri) || uri.contains("actuator")) {
filterChain.doFilter(request, response);
return;
}
var token = "";
var header = request.getHeader("Authorization");
if (header != null && header.startsWith("Bearer ")) {
token = header.substring(7);
}
if (Strings.isBlank(token)) {
throwResponse(response);
return;
}
var claims = JwtHelper.extractUsername(token);
if (claims != null) {
var userId = claims.get("userId").toString();
var userName = claims.get("userName").toString();
var roleId = claims.get("roleId").toString();
ThreadContext.put("userId", userId);
ThreadContext.put("userName", userName);
ThreadContext.put("roleId", roleId);
if (token.equals(redisService.hGet(CommonConst.TOKEN, userId))) {
filterChain.doFilter(request, response);
}
}
} catch (ExpiredJwtException e) {
//
} catch (Exception e) {
LogHelper.error(e);
}
throwResponse(response);
}
private void throwResponse(HttpServletResponse response) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
LogHelper.info("Unauthorized access");
}
}

View File

@ -1,7 +1,7 @@
package com.vega.hrm.filters; package com.vega.hrm.core.filters;
import com.vega.hrm.helpers.LogHelper; import com.vega.hrm.core.helpers.LogHelper;
import jakarta.servlet.FilterChain; import jakarta.servlet.FilterChain;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;

View File

@ -1,4 +1,4 @@
package com.vega.hrm.helpers; package com.vega.hrm.core.helpers;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;

View File

@ -1,4 +1,4 @@
package com.vega.hrm.helpers; package com.vega.hrm.core.helpers;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.security.MessageDigest; import java.security.MessageDigest;

View File

@ -1,4 +1,4 @@
package com.vega.hrm.helpers; package com.vega.hrm.core.helpers;
import java.text.ParseException; import java.text.ParseException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;

View File

@ -1,4 +1,4 @@
package com.vega.hrm.helpers; package com.vega.hrm.core.helpers;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;

View File

@ -1,4 +1,4 @@
package com.vega.hrm.helpers; package com.vega.hrm.core.helpers;
import io.jsonwebtoken.Claims; import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException; import io.jsonwebtoken.ExpiredJwtException;
@ -10,6 +10,7 @@ import java.nio.charset.StandardCharsets;
import java.security.Key; import java.security.Key;
import java.util.Date; import java.util.Date;
import java.util.Map; import java.util.Map;
import java.util.UUID;
import javax.crypto.spec.SecretKeySpec; import javax.crypto.spec.SecretKeySpec;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@ -20,11 +21,11 @@ public class JwtHelper {
private static final long EXPIRATION_TIME_MS = 600_000; // 10 phut private static final long EXPIRATION_TIME_MS = 600_000; // 10 phut
private static final Key SIGNING_KEY = generateSigningKey(); private static final Key SIGNING_KEY = generateSigningKey();
public static String generateToken(String username, Long userId, Long roleId) { public static String generateToken(String username, UUID userId, UUID roleId) {
return Jwts.builder() return Jwts.builder()
.claims(Map.of( .claims(Map.of(
"userId", userId, "userId", userId.toString(),
"roleId", roleId, "roleId", roleId.toString(),
"userName", username "userName", username
)) ))
.subject(username) .subject(username)

View File

@ -1,4 +1,4 @@
package com.vega.hrm.helpers; package com.vega.hrm.core.helpers;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.ThreadContext; import org.apache.logging.log4j.ThreadContext;

View File

@ -1,4 +1,4 @@
package com.vega.hrm.helpers; package com.vega.hrm.core.helpers;
import java.util.HashMap; import java.util.HashMap;
import org.apache.logging.log4j.ThreadContext; import org.apache.logging.log4j.ThreadContext;

View File

@ -1,4 +1,4 @@
package com.vega.hrm.helpers; package com.vega.hrm.core.helpers;
import java.security.SecureRandom; import java.security.SecureRandom;

View File

@ -0,0 +1,16 @@
package com.vega.hrm.core.models;
import lombok.Getter;
import lombok.NoArgsConstructor;
@Getter
@NoArgsConstructor
public class RedisEccKeyPair {
private String privateKey;
private String expiration;
public RedisEccKeyPair(String privateKey) {
this.privateKey = privateKey;
}
}

View File

@ -1,4 +1,4 @@
package com.vega.hrm.models.requests; package com.vega.hrm.core.models.requests;
import lombok.Getter; import lombok.Getter;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;

View File

@ -1,4 +1,4 @@
package com.vega.hrm.models.requests; package com.vega.hrm.core.models.requests;
import lombok.Getter; import lombok.Getter;

View File

@ -1,4 +1,4 @@
package com.vega.hrm.models.requests; package com.vega.hrm.core.models.requests;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;

View File

@ -1,4 +1,4 @@
package com.vega.hrm.models.requests; package com.vega.hrm.core.models.requests;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Builder; import lombok.Builder;

View File

@ -1,4 +1,4 @@
package com.vega.hrm.models.requests; package com.vega.hrm.core.models.requests;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;

View File

@ -1,4 +1,4 @@
package com.vega.hrm.models.requests; package com.vega.hrm.core.models.requests;
import jakarta.validation.constraints.Min; import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.Positive; import jakarta.validation.constraints.Positive;

View File

@ -1,4 +1,4 @@
package com.vega.hrm.models.responses; package com.vega.hrm.core.models.responses;
public record BaseEncryptedResponse(String key, String response) { public record BaseEncryptedResponse(String key, String response) {
} }

View File

@ -1,8 +1,8 @@
package com.vega.hrm.models.responses; package com.vega.hrm.core.models.responses;
import com.vega.hrm.constants.ResponseCodeConst; import com.vega.hrm.core.constants.ResponseCodeConst;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Builder; import lombok.Builder;
import lombok.Getter; import lombok.Getter;
@ -27,28 +27,29 @@ public class BaseResponse<T> {
private String message; private String message;
private T data; private T data;
public static <T> BaseResponse<T> Success(String msg) {
public static <T> BaseResponse<T> success(String msg) {
return BaseResponse.<T>builder() return BaseResponse.<T>builder()
.code(ResponseCodeConst.SUCCESS) .code(ResponseCodeConst.SUCCESS)
.message(msg) .message(msg)
.build(); .build();
} }
public static <T> BaseResponse<T> Invalid(String msg) { public static <T> BaseResponse<T> invalid(String msg) {
return BaseResponse.<T>builder() return BaseResponse.<T>builder()
.code(ResponseCodeConst.INVALID) .code(ResponseCodeConst.INVALID)
.message(msg) .message(msg)
.build(); .build();
} }
public static <T> BaseResponse<T> NotFound(String msg) { public static <T> BaseResponse<T> notFound(String msg) {
return BaseResponse.<T>builder() return BaseResponse.<T>builder()
.code(ResponseCodeConst.NOT_FOUND) .code(ResponseCodeConst.NOT_FOUND)
.message(msg) .message(msg)
.build(); .build();
} }
public static <T> BaseResponse<T> InternalSystemError() { public static <T> BaseResponse<T> internalSystemError() {
return BaseResponse.<T>builder() return BaseResponse.<T>builder()
.code(ResponseCodeConst.INTERNAL_SYSTEM_ERROR) .code(ResponseCodeConst.INTERNAL_SYSTEM_ERROR)
.message("Internal System Error") .message("Internal System Error")

View File

@ -1,4 +1,4 @@
package com.vega.hrm.models.responses; package com.vega.hrm.core.models.responses;
import java.util.List; import java.util.List;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;

View File

@ -1,7 +1,7 @@
package com.vega.hrm.models.responses; package com.vega.hrm.core.models.responses;
import com.vega.hrm.constants.ResponseCodeConst; import com.vega.hrm.core.constants.ResponseCodeConst;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Builder; import lombok.Builder;
import lombok.Getter; import lombok.Getter;

View File

@ -0,0 +1,15 @@
package com.vega.hrm.core.repositories;
import com.vega.hrm.core.entities.BoFunction;
import java.util.List;
import java.util.UUID;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
@Repository
public interface CoreFunctionRepository extends JpaRepository<BoFunction, UUID> {
}

View File

@ -0,0 +1,11 @@
package com.vega.hrm.core.repositories;
import com.vega.hrm.core.entities.BoRole;
import java.util.UUID;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface CoreRoleRepository extends JpaRepository<BoRole, UUID> {
BoRole findBoRoleById(UUID id);
}

View File

@ -0,0 +1,12 @@
package com.vega.hrm.core.repositories;
import com.vega.hrm.core.entities.BoUser;
import java.util.UUID;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface CoreUserRepository extends JpaRepository<BoUser, UUID> {
BoUser findByUserName(String username);
}

View File

@ -0,0 +1,68 @@
package com.vega.hrm.core.service;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class RedisService {
private final RedisTemplate<String, Object> redisTemplate;
// String operations
public void set(String key, Object value, long timeout, TimeUnit unit) {
redisTemplate.opsForValue().set(key, value, timeout, unit);
}
@SuppressWarnings("unchecked")
public <T> T get(String key) {
Object value = redisTemplate.opsForValue().get(key);
return value != null ? (T) value : null;
}
public boolean exists(String key) {
return Boolean.TRUE.equals(redisTemplate.hasKey(key));
}
public void delete(String key) {
redisTemplate.delete(key);
}
// Hash operations
public void hSet(String key, String field, Object value) {
redisTemplate.opsForHash().put(key, field, value);
}
@SuppressWarnings("unchecked")
public <T> T hGet(String key, String field) {
Object value = redisTemplate.opsForHash().get(key, field);
return value != null ? (T) value : null;
}
public void hSetAll(String key, Map<String, Object> values) {
redisTemplate.opsForHash().putAll(key, values);
}
public Map<Object, Object> hGetAll(String key) {
return redisTemplate.opsForHash().entries(key);
}
public boolean hExists(String key, String field) {
return redisTemplate.opsForHash().hasKey(key, field);
}
public void hDelete(String key, Object... fields) {
redisTemplate.opsForHash().delete(key, fields);
}
public Set<Object> hKeys(String key) {
return redisTemplate.opsForHash().keys(key);
}
public long hSize(String key) {
return redisTemplate.opsForHash().size(key);
}
}

View File

@ -1,4 +1,4 @@
package com.vega.hrm.wrapper; package com.vega.hrm.core.wrapper;
import jakarta.servlet.ReadListener; import jakarta.servlet.ReadListener;
import jakarta.servlet.ServletInputStream; import jakarta.servlet.ServletInputStream;

View File

@ -1,4 +1,4 @@
package com.vega.hrm.wrapper; package com.vega.hrm.core.wrapper;
import jakarta.servlet.ServletOutputStream; import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.WriteListener; import jakarta.servlet.WriteListener;

View File

@ -1,30 +0,0 @@
package com.vega.hrm.filters;
import io.jsonwebtoken.ExpiredJwtException;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import org.apache.logging.log4j.ThreadContext;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
@Component
@Order(2)
@RequiredArgsConstructor
public class AuthorizationFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
filterChain.doFilter(request, response);
}
}

View File

@ -1,18 +0,0 @@
package com.vega.hrm.models;
import java.time.Instant;
import lombok.Getter;
import lombok.NoArgsConstructor;
import vn.vnpay.dvnh.backoffice.core.common.utility.DateTimeUtils;
@Getter
@NoArgsConstructor
public class RedisEccKeyPair {
private String privateKey;
private String expiration;
public RedisEccKeyPair(String privateKey, Instant expiration) {
this.privateKey = privateKey;
this.expiration = DateTimeUtils.format(expiration);
}
}