Security Patterns trong Java
1. Authentication Patterns
1.1 JWT Authentication
@Configuration
public class JwtConfig {
@Bean
public JwtEncoder jwtEncoder() {
JWK jwk = new RSAKey.Builder(publicKey)
.privateKey(privateKey)
.build();
JWKSource<SecurityContext> jwks = new ImmutableJWKSet<>(new JWKSet(jwk));
return new NimbusJwtEncoder(jwks);
}
@Bean
public JwtDecoder jwtDecoder() {
return NimbusJwtDecoder.withPublicKey(publicKey).build();
}
}
@Service
public class JwtTokenService {
private final JwtEncoder encoder;
public String generateToken(Authentication authentication) {
Instant now = Instant.now();
JwtClaimsSet claims = JwtClaimsSet.builder()
.issuer("self")
.issuedAt(now)
.expiresAt(now.plus(1, ChronoUnit.HOURS))
.subject(authentication.getName())
.claim("scope", getScopes(authentication))
.build();
return encoder.encode(JwtEncoderParameters.from(claims))
.getTokenValue();
}
}
1.2 OAuth2/OpenID Connect
@Configuration
@EnableWebSecurity
public class OAuth2Config extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.oauth2Login()
.authorizationEndpoint()
.baseUri("/oauth2/authorize")
.authorizationRequestRepository(cookieAuthorizationRequestRepository())
.and()
.redirectionEndpoint()
.baseUri("/oauth2/callback/*")
.and()
.userInfoEndpoint()
.userService(customOAuth2UserService)
.and()
.successHandler(oAuth2AuthenticationSuccessHandler)
.failureHandler(oAuth2AuthenticationFailureHandler);
}
@Bean
public OAuth2UserService<OAuth2UserRequest, OAuth2User> oauth2UserService() {
DefaultOAuth2UserService delegate = new DefaultOAuth2UserService();
return userRequest -> {
OAuth2User oauth2User = delegate.loadUser(userRequest);
return new CustomOAuth2User(oauth2User);
};
}
}
2. Authorization Patterns
2.1 Role-Based Access Control (RBAC)
@Configuration
@EnableGlobalMethodSecurity(
prePostEnabled = true,
securedEnabled = true,
jsr250Enabled = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
@Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
DefaultMethodSecurityExpressionHandler expressionHandler =
new DefaultMethodSecurityExpressionHandler();
expressionHandler.setPermissionEvaluator(new CustomPermissionEvaluator());
return expressionHandler;
}
}
@Service
public class UserService {
@PreAuthorize("hasRole('ADMIN')")
public User createUser(User user) {
return userRepository.save(user);
}
@PostAuthorize("returnObject.username == authentication.principal.username or hasRole('ADMIN')")
public User getUser(Long id) {
return userRepository.findById(id)
.orElseThrow(() -> new UserNotFoundException(id));
}
}
2.2 Attribute-Based Access Control (ABAC)
@Component
public class CustomPermissionEvaluator implements PermissionEvaluator {
@Override
public boolean hasPermission(
Authentication auth,
Object targetDomainObject,
Object permission) {
if ((auth == null) || (targetDomainObject == null)) {
return false;
}
String targetType = targetDomainObject.getClass().getSimpleName();
return hasPrivilege(auth, targetType, permission.toString());
}
private boolean hasPrivilege(
Authentication auth,
String targetType,
String permission) {
for (GrantedAuthority grantedAuth : auth.getAuthorities()) {
if (grantedAuth.getAuthority()
.startsWith(targetType.toUpperCase())) {
return true;
}
}
return false;
}
}
3. Encryption Patterns
3.1 Data-at-Rest Encryption
@Configuration
public class EncryptionConfig {
@Bean
public StringEncryptor stringEncryptor() {
PooledPBEStringEncryptor encryptor = new PooledPBEStringEncryptor();
SimpleStringPBEConfig config = new SimpleStringPBEConfig();
config.setPassword(encryptionKey);
config.setAlgorithm("PBEWithHMACSHA512AndAES_256");
config.setKeyObtentionIterations("1000");
config.setPoolSize("1");
config.setProviderName("SunJCE");
config.setSaltGeneratorClassName("org.jasypt.salt.RandomSaltGenerator");
config.setIvGeneratorClassName("org.jasypt.iv.RandomIvGenerator");
config.setStringOutputType("base64");
encryptor.setConfig(config);
return encryptor;
}
}
@Entity
public class SensitiveData {
@Convert(converter = AttributeEncryptor.class)
private String sensitiveInfo;
}
@Converter
public class AttributeEncryptor implements AttributeConverter<String, String> {
private final StringEncryptor encryptor;
@Override
public String convertToDatabaseColumn(String attribute) {
return encryptor.encrypt(attribute);
}
@Override
public String convertToEntityAttribute(String dbData) {
return encryptor.decrypt(dbData);
}
}
3.2 Data-in-Transit Encryption
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.requiresChannel()
.anyRequest()
.requiresSecure()
.and()
.headers()
.httpStrictTransportSecurity()
.includeSubDomains(true)
.maxAgeInSeconds(31536000);
}
@Bean
public TomcatServletWebServerFactory servletContainer() {
TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();
tomcat.addAdditionalTomcatConnectors(redirectConnector());
return tomcat;
}
private Connector redirectConnector() {
Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
connector.setScheme("http");
connector.setPort(8080);
connector.setSecure(false);
connector.setRedirectPort(8443);
return connector;
}
}
4.1 Request Validation
@RestController
@Validated
public class UserController {
@PostMapping("/users")
public ResponseEntity<User> createUser(
@Valid @RequestBody UserDTO userDTO) {
return ResponseEntity.ok(userService.createUser(userDTO));
}
}
public class UserDTO {
@NotBlank
@Size(min = 4, max = 50)
private String username;
@NotBlank
@Email
private String email;
@NotBlank
@Pattern(regexp = "^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%^&+=])(?=\\S+$).{8,}$")
private String password;
@AssertTrue(message = "Terms must be accepted")
private boolean termsAccepted;
}
@ControllerAdvice
public class ValidationExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<Map<String, List<String>>> handleValidationErrors(
MethodArgumentNotValidException ex) {
List<String> errors = ex.getBindingResult()
.getFieldErrors()
.stream()
.map(FieldError::getDefaultMessage)
.collect(Collectors.toList());
return new ResponseEntity<>(getErrorsMap(errors), HttpStatus.BAD_REQUEST);
}
private Map<String, List<String>> getErrorsMap(List<String> errors) {
Map<String, List<String>> errorResponse = new HashMap<>();
errorResponse.put("errors", errors);
return errorResponse;
}
}
4.2 SQL Injection Prevention
@Repository
public class UserRepository {
private final JdbcTemplate jdbcTemplate;
public User findByUsername(String username) {
String sql = "SELECT * FROM users WHERE username = ?";
return jdbcTemplate.queryForObject(
sql,
new Object[]{username},
new BeanPropertyRowMapper<>(User.class)
);
}
@Query("SELECT u FROM User u WHERE u.username = :username")
public User findByUsernameJPA(@Param("username") String username);
}
5. Logging và Auditing
5.1 Security Event Logging
@Aspect
@Component
public class SecurityAuditAspect {
private final AuditLogger auditLogger;
@Around("@annotation(Audited)")
public Object auditMethod(ProceedingJoinPoint joinPoint) throws Throwable {
String methodName = joinPoint.getSignature().getName();
String className = joinPoint.getTarget().getClass().getName();
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
AuditEvent event = AuditEvent.builder()
.principal(authentication.getName())
.type("METHOD_ACCESS")
.data(Map.of(
"method", methodName,
"class", className,
"timestamp", Instant.now()
))
.build();
try {
Object result = joinPoint.proceed();
event.setOutcome("SUCCESS");
return result;
} catch (Exception e) {
event.setOutcome("FAILURE");
event.setError(e.getMessage());
throw e;
} finally {
auditLogger.log(event);
}
}
}
5.2 Audit Trail
@Entity
@EntityListeners(AuditingEntityListener.class)
public class AuditableEntity {
@CreatedBy
private String createdBy;
@CreatedDate
private Instant createdDate;
@LastModifiedBy
private String lastModifiedBy;
@LastModifiedDate
private Instant lastModifiedDate;
}
@Configuration
@EnableJpaAuditing
public class AuditConfig {
@Bean
public AuditorAware<String> auditorProvider() {
return () -> Optional.ofNullable(SecurityContextHolder.getContext())
.map(SecurityContext::getAuthentication)
.filter(Authentication::isAuthenticated)
.map(Authentication::getName);
}
}
@Configuration
public class SecurityHeadersConfig {
@Bean
public WebSecurityCustomizer webSecurityCustomizer() {
return web -> web.ignoring()
.requestMatchers("/resources/**");
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.headers()
.contentSecurityPolicy("default-src 'self'")
.and()
.xssProtection()
.and()
.frameOptions()
.deny()
.and()
.referrerPolicy(ReferrerPolicy.STRICT_ORIGIN_WHEN_CROSS_ORIGIN)
.and()
.permissionsPolicy(permissions -> permissions
.policy("camera=(), microphone=(), geolocation=()"))
.and()
.contentTypeOptions()
.and()
.cacheControl();
return http.build();
}
}
6.2 CORS Configuration
@Configuration
public class CorsConfig {
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("https://trusted-domain.com"));
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE"));
configuration.setAllowedHeaders(Arrays.asList("Authorization", "Content-Type"));
configuration.setExposedHeaders(Arrays.asList("X-Custom-Header"));
configuration.setAllowCredentials(true);
configuration.setMaxAge(3600L);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
7. Best Practices
7.1 Security Guidelines
- Principle of Least Privilege
- Defense in Depth
- Fail Securely
- Complete Mediation
- Separation of Duties
- Keep Security Simple
- Zero Trust Architecture
- Security by Design
7.2 Implementation Guidelines
- Use Strong Password Hashing
- Implement Rate Limiting
- Enable HTTPS Everywhere
- Validate All Input
- Implement Proper Session Management
- Use Secure Dependencies
- Regular Security Audits
- Implement Proper Error Handling
8. References
- OWASP Security Guidelines
- Spring Security Reference
- Java Security Coding Guidelines
- Cloud Security Design Patterns