Chuyển tới nội dung chính

Bảo mật resource server

Cung cấp phần bảo mật dùng chung cho service đóng vai OAuth2 resource server: xác thực access token (JWT hoặc opaque token), ánh xạ token thành authorities, tiện ích truy cập người dùng hiện tại và các SPI phân quyền. Dùng khi service của bạn cần nhận token do máy chủ uỷ quyền Govex Cloud phát hành và kiểm soát truy cập theo người dùng.

Khi nào sử dụng

  • Service nghiệp vụ cần xác thực mọi request bằng access token thay vì tự viết filter chain.
  • Cần đọc thông tin người dùng hiện tại (ID, username, đơn vị làm việc) ngay trong code nghiệp vụ.
  • Cần phân quyền theo authority hoặc theo permission trên tài nguyên, ví dụ hasPermission.
  • Cần bảo vệ endpoint cho SPA (CSRF) hoặc khôi phục redirect sau khi đăng nhập.

Cài đặt

Thêm dependency vào pom.xml (không cần khai version vì đã được quản lý qua BOM):

<dependency>
<groupId>vn.govex.cloud</groupId>
<artifactId>govex-cloud-security</artifactId>
</dependency>

Version do BOM vn.govex.cloud:dependencies quản lý — xem Cài đặt.

Cấu hình

PropertyMô tảMặc định
govex.security.oauth2.ignoresDanh sách đường dẫn bỏ qua kiểm tra xác thực (permitAll). Các đường dẫn mặc định (static resource, actuator, swagger, /error...) luôn được bổ sung thêm vào danh sách này.danh sách mặc định
govex.security.oauth2.resource-server.validation.enabledBật kiểm tra bổ sung issuer, audience và mục đích sử dụng của token JWT.false
govex.security.oauth2.resource-server.validation.accepted-issuersDanh sách issuer được chấp nhận; bắt buộc khi bật validation.rỗng
govex.security.oauth2.resource-server.validation.accepted-audiencesDanh sách audience được chấp nhận; bắt buộc khi bật validation.rỗng
govex.security.oauth2.resource-server.validation.token-purpose-claimTên claim chứa mục đích sử dụng token; bắt buộc khi bật validation.
govex.security.oauth2.resource-server.validation.accepted-token-purposesDanh sách mục đích sử dụng token được chấp nhận; bắt buộc khi bật validation.rỗng
govex.security.oauth2.resource-server.app-access.enabledBật filter kiểm tra người dùng có quyền truy cập app theo danh sách app lưu trong Redis.false
govex.security.oauth2.resource-server.app-access.app-codeMã ứng dụng dùng để kiểm tra quyền truy cập; bắt buộc khi bật app-access.
govex.security.oauth2.resource-server.app-access.skip-client-credentialsBỏ qua kiểm tra app access với principal xác thực bằng client_credentials.true
jasypt.encryptor.enabledGiải mã các giá trị ENC(...) trong file cấu hình khi có jasypt-spring-boot trên classpath.true

Ngoài ra JwtDecoder được tạo từ bean chuẩn của Spring: spring.security.oauth2.resourceserver.jwt.jwk-set-uri (hoặc jwk-set-uri/public-key-location).

Sử dụng

Bước 1 — bật resource server trên class ứng dụng:

@SpringBootApplication
@EnableResourceServer
public class CustomerApplication {
// ...
}

Bước 2 — khai báo SecurityFilterChain và dùng TokenStrategyConfigurer cho oauth2ResourceServer:

@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http,
TokenStrategyConfigurer tokenStrategyConfigurer,
UnauthorizedEntryPoint unauthorizedEntryPoint,
OAuth2ResourceProperties properties,
MvcRequestMatcher.Builder mvc) throws Exception {
http.authorizeHttpRequests(authorizeRequests -> authorizeRequests
.requestMatchers(properties.getIgnores().stream().map(mvc::pattern).toArray(MvcRequestMatcher[]::new)).permitAll()
.anyRequest().authenticated())
.oauth2ResourceServer(tokenStrategyConfigurer::from)
.exceptionHandling(configurer -> configurer.authenticationEntryPoint(unauthorizedEntryPoint))
.sessionManagement(configurer -> configurer.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
return http.build();
}

Bước 3 — cấu hình minh hoạ:

govex:
security:
oauth2:
ignores:
- /device/export
- /kafka/dispatch
resource-server:
validation:
enabled: true
accepted-issuers:
- https://id.govex.vn
accepted-audiences:
- customer-service

Bước 4 — truy cập người dùng hiện tại bằng SecurityUtils:

String userId = SecurityUtils.getCurrentUserId();
String username = SecurityUtils.getCurrentUsername();
boolean isAdmin = SecurityUtils.hasAuthority(SecurityUtils.ROLE_ADMIN);
OrganizationContext org = SecurityUtils.getCurrentOrganization();

Hoặc inject thẳng vào tham số controller bằng annotation:

@GetMapping("/me")
public ResultMessage<UserInfo> me(@AuthUser GovexUserDetails user) {
return ResultMessage.success(UserInfo.from(user));
}

@GetMapping("/orders")
public ResultMessage<List<Order>> orders(@AuthUserId String userId) {
return ResultMessage.success(orderService.findByUser(userId));
}

@GetMapping("/extra")
public ResultMessage<String> extra(@AuthExtra("user_info") String userInfo) {
return ResultMessage.success(userInfo);
}

Bước 5 — phân quyền method bằng @PreAuthorize. ResourcePermissionEvaluator so khớp authority theo pattern target:permission:

@PreAuthorize("hasPermission('customer', 'read')")
@GetMapping("/{id}")
public ResultMessage<Customer> get(@PathVariable String id) {
return ResultMessage.success(customerService.get(id));
}

Lưu ý

  • AuthoritiesRedisService chỉ được tạo khi trong context có StringRedisTemplate; khi đó authorities lấy từ cache Redis sẽ thay thế authorities trong claim.
  • RefreshCurrentSessionPrincipalServiceImpl chỉ được tạo khi có bean UserDetailsService của govex-cloud-security — ứng dụng host phải cung cấp bean này nếu dùng session.
  • SessionConfiguration (session cluster) chỉ cần khai báo khi thực sự dùng session.
  • http.with(new SecurityContextHttpConfigurer<>())SavedRedirectHttpConfigurer được Spring Security tự phát hiện qua META-INF/spring.factories.
  • Hạ tầng: Redis bắt buộc nếu dùng cache authorities hoặc kiểm tra app access; Jasypt cần jasypt-spring-boot trên classpath.