Код: Выделить всё
@SuppressWarnings(value = "unused")
@Profile(value = "!test")
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor(onConstructor_ = @__(@Autowired))
public class SecurityConfiguration {
// Final Constructors
private final BasicAuthEntryPoint basicAuthEntryPoint;
private final JwtAuthEntryPoint jwtAuthEntryPoint;
private final JwtFilter jwtFilter;
// Retrieving username & password from environment variables
@Value("${EMP_CONFIG_USERNAME}")
private String envUsername;
@Value("${EMP_CONFIG_PASSWORD}")
private String envPassword;
@Value("${EMP_ENCRYPT_KEY}")
private String envEncryptKey;
// PasswordEncoder
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
// RequestInterceptor (Authentication with external Microservices - OpenFeign)
@Bean
public RequestInterceptor basicAuthRequestInterceptor() {
return requestTemplate -> {
String authHeader = "Basic " + Base64.getEncoder()
.encodeToString((envUsername + ":" + envEncryptKey).getBytes());
requestTemplate.header("Authorization", authHeader);
};
}
// Setting SecurityFilterChain - Basic Auth
@Bean
@Order(1)
public SecurityFilterChain securityFilterChainBasicAuth(HttpSecurity http) throws Exception {
return http
.securityMatcher("/actuator/**")
.authorizeHttpRequests(req -> req
.anyRequest().hasRole("ADMIN"))
.httpBasic(withDefaults())
.sessionManagement(session -> session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.exceptionHandling(ex -> ex.authenticationEntryPoint(basicAuthEntryPoint))
.csrf(AbstractHttpConfigurer::disable)
.build();
}
// Setting SecurityFilterChain - JWT Auth
@Bean
@Order(2)
public SecurityFilterChain securityFilterChainJwt(HttpSecurity http) throws Exception {
return http
.csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(request -> request
.requestMatchers(HttpMethod.POST, "/v1/auth/**").permitAll()
.anyRequest().authenticated())
.sessionManagement(session -> session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
//.exceptionHandling(ex -> ex.authenticationEntryPoint(jwtAuthEntryPoint))
.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class)
.build();
}
// AuthenticationManager (JWT)
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws Exception {
return configuration.getAuthenticationManager();
}
// InMemory User - Admins Only (Basic Auth)
@Bean
public UserDetailsService adminUser() {
// Admin
return new InMemoryUserDetailsManager(
User.withUsername(envUsername)
.password(envPassword)
.roles("ADMIN")
.build()
);
}
}
Код: Выделить всё
@Component
@RequiredArgsConstructor(onConstructor_ = @__(@Autowired))
public class JwtFilter extends OncePerRequestFilter {
// Final Constructors
private final JwtUtils jwtUtils;
private final AuthServiceProxy authServiceProxy;
private final Logger logger = LoggerFactory.getLogger(JwtFilter.class);
@Override
protected void doFilterInternal(
@NonNull HttpServletRequest request,
@NonNull HttpServletResponse response,
@NonNull FilterChain filterChain
) throws ServletException, IOException {
// Extracting 'Authorization' from request Headers
String authHeader = getAuthHeader(request);
String requestPath = request.getRequestURI();
// 1. Skipping JWT if Authorization Header is 'Basic'
if (authHeader != null && authHeader.startsWith("Basic ")) {
logger.debug("Skipping JWT filter for Basic Auth request [{}]", requestPath);
filterChain.doFilter(request, response);
return;
}
// 2. Validating
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
logger.debug("No Bearer token found in request [{}], skipping JWT filter", requestPath);
filterChain.doFilter(request, response);
return;
}
String token = authHeader.substring(7);
String username = jwtUtils.getUsername(token);
logger.debug("JWT token parsed. Username extracted: [{}]", username);
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
CredentialDTO credentialDTOByUsername = authServiceProxy.getCredentialByUsername(
username,
"GET",
"/v1/creds/ids?user=" + username
);
if (jwtUtils.isTokenValid(token, credentialDTOByUsername)) {
UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
credentialDTOByUsername,
null,
credentialDTOByUsername.getAuthorities()
);
authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authToken);
logger.info("JWT authentication successful for user [{}] on [{}]", username, requestPath);
}
}
filterChain.doFilter(request, response);
}
// Extracting 'Authorization' from request Headers
private String getAuthHeader(HttpServletRequest request) {
return request.getHeader(HttpHeaders.AUTHORIZATION);
}
}
Код: Выделить всё
@Component
@RequiredArgsConstructor(onConstructor_ = @__(@Autowired))
public class JwtAuthEntryPoint implements AuthenticationEntryPoint {
// Final Constructor
private final HandlerExceptionResolver handlerExceptionResolver;
private final Logger logger = LoggerFactory.getLogger(JwtAuthEntryPoint.class);
@Override
public void commence(
@NonNull HttpServletRequest request,
@NonNull HttpServletResponse response,
@NonNull AuthenticationException authException
) {
logger.error("Error occurred during JWT Auth: {}", authException.getMessage());
handlerExceptionResolver.resolveException(
request,
response,
null,
new UnauthorizedAccessAttempt(authException.getMessage())
);
}
}
Код: Выделить всё
@Component
@RequiredArgsConstructor(onConstructor_ = @__(@Autowired))
public class CredentialAuthenticationProvider implements AuthenticationProvider {
// Final Constructors
private final AuthServiceProxy authServiceProxy;
private final PasswordEncoder passwordEncoder;
private final Logger logger = LoggerFactory.getLogger(CredentialAuthenticationProvider.class);
@Value("${EMP_CONFIG_USERNAME}")
private String adminUsername;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
// Username & Password (Plaintext)
String username = authentication.getName();
// Skip in-memory admin
if (username.equals(adminUsername)) {
logger.debug("Skipping CredentialAuthenticationProvider for in-memory user [{}]", username);
return null;
}
String password = authentication.getCredentials().toString();
CredentialDTO credentialDTOByUsername = authServiceProxy.getCredentialByUsername(
username,
"GET",
"/v1/cred/ids?user=" + username
);
if(credentialDTOByUsername == null || !passwordEncoder.matches(password, credentialDTOByUsername.password())){
logger.error("Username or Password aren't valid - Please check again for JWT generation");
throw new InvalidCredentialException(username);
}
logger.info("Username & Password valid!");
return new UsernamePasswordAuthenticationToken(credentialDTOByUsername, null, credentialDTOByUsername.getAuthorities());
}
@Override
public boolean supports(Class authentication) {
return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);
}
}
Подробнее здесь: https://stackoverflow.com/questions/795 ... t-patterns