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
| Property | Mô tả | Mặc định |
|---|---|---|
govex.security.oauth2.ignores | Danh 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.enabled | Bậ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-issuers | Danh 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-audiences | Danh 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-claim | Tê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-purposes | Danh 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.enabled | Bậ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-code | Mã ứ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-credentials | Bỏ qua kiểm tra app access với principal xác thực bằng client_credentials. | true |
jasypt.encryptor.enabled | Giả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 ý
AuthoritiesRedisServicechỉ được tạo khi trong context cóStringRedisTemplate; khi đó authorities lấy từ cache Redis sẽ thay thế authorities trong claim.RefreshCurrentSessionPrincipalServiceImplchỉ được tạo khi có beanUserDetailsServicecủagovex-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<>())vàSavedRedirectHttpConfigurerđược Spring Security tự phát hiện quaMETA-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-boottrên classpath.