Как настроить Spring Boot 2.7.5 с Spring Security 5.7.4 с настраиваемыми фильтрамиJAVA

Программисты JAVA общаются здесь
Ответить
Anonymous
 Как настроить Spring Boot 2.7.5 с Spring Security 5.7.4 с настраиваемыми фильтрами

Сообщение Anonymous »

У меня есть приложение корпоративного уровня, которое я обновляю с предыдущей версии Spring Boot до 2.7.5, а затем до Spring Security 5.7.4. Это обновление сообщило мне, что WebSecurityConfigurerAdapter устарел, и я находился в процессе его удаления. Хотя существует множество руководств и решений, которые помогут в этом процессе, у меня есть определенные настройки для конфигураций, которые усложняют этот процесс.
Моя проблема в том, что я пытаюсь внедрить специальный фильтр CORS, поскольку в моем развертывании обнаруживается ошибка 403 «Access-Control-Allow-Origin», отсутствует заголовок. Раньше я сталкивался с перенаправлением 302 Found в моем развертывании, которое препятствовало работе какой-либо конечной точки API, но это загадочным образом изменилось в вышеупомянутой проблеме.
Я пробовал много решений, опубликованных здесь и на на других форумах, я просмотрел официальную документацию Spring и попытался адаптировать информацию к своим потребностям, но ничего не оказалось полезным.
Я пытался добавить компонент CORS в SecurityConfig. class, я пытался создать определенный класс CORS, как показано выше, я пробовал использовать конфигурацию Web MVC и другие решения, но ни одно из них не помогло.
Как я могу обновить свой класс SecurityConfig до избежать этой проблемы CORS и, возможно, перенаправления 302, как раньше?
Ниже приведен мой код для текущего развертывания с устаревшим WebSecurityConfigurerAdapter:
@Configuration
@EnableWebSecurity
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

@Autowired
@Qualifier("accessController")
private final UserDetailsService userDetailsService;

@Bean
PasswordEncoder htePasswordEncoder() {
return new BCryptPasswordEncoder();
}

public SecurityConfig(@Qualifier("accessController") UserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(htePasswordEncoder());
}

@Override
protected void configure(HttpSecurity http) throws Exception {
// Move Login service under access
HTeAuthenticationFilter hTeAuthenticationFilter = new HTeAuthenticationFilter(authenticationManagerBean());
hTeAuthenticationFilter.setFilterProcessesUrl("/access/login");

http = http.csrf().disable();
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.cors().and().authorizeRequests()
.antMatchers("/access/token/**").permitAll()
.antMatchers("/licensing/**/**").permitAll()
.antMatchers("/access/login/**").permitAll()
.antMatchers("/access/license/**").permitAll()
.antMatchers("/access/refreshToken/**").permitAll()
.antMatchers("/ui/saveChecksumLogs").permitAll()
.antMatchers("/ws-message/**").permitAll()
.antMatchers("/swagger-ui/**").permitAll()
.antMatchers("/v3/**").permitAll()
.antMatchers("/ui/disableProfileUpdatedStatus").permitAll()
.antMatchers("/ui/profileUpdatedStatus").permitAll()
.antMatchers("/ui/test").permitAll()
.anyRequest().authenticated();

// Ensure all services are authenticated
http.addFilter(hTeAuthenticationFilter);
http.addFilterBefore(new HteAuthorizationFilter(),
UsernamePasswordAuthenticationFilter.class);
http.addFilterAfter(new DomainAuthorizationFilter(),
BasicAuthenticationFilter.class);
http.addFilterAfter(new RoleAuthorizationFilter(),
BasicAuthenticationFilter.class);
}

@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}

А это моя текущая попытка обновить конфигурацию безопасности:
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled=true)
public class SecurityConfig {

@Bean
public UserDetailsService userDetailsService() {
return new AccessorController();
}

@Bean
public BCryptPasswordEncoder htePasswordEncoder() {
return new BCryptPasswordEncoder();
}

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.csrf(AbstractHttpConfigurer::disable).authorizeHttpRequests(authorizationManagerRequestMatcherRegistry ->
authorizationManagerRequestMatcherRegistry
.antMatchers("/access/token/**").permitAll()
.antMatchers("/access/login/**").permitAll()
.antMatchers("/access/license/**").permitAll()
.antMatchers("/access/refreshToken/**").permitAll()
.antMatchers("/licensing/**/**").permitAll()
.antMatchers("/ui/saveChecksumLogs").permitAll()
.antMatchers("/ws-message/**").permitAll()
.antMatchers("/swagger-ui/**").permitAll()
.antMatchers("/v3/**").permitAll()
.antMatchers("/ui/disabledProfileUpdatedStatus").permitAll()
.antMatchers("/ui/profileUpdatedStatus").permitAll()
.antMatchers("/ui/test").permitAll()
.anyRequest().authenticated()).sessionManagement(httpSecuritySessionManagementConfigurer ->
httpSecuritySessionManagementConfigurer.sessionCreationPolicy(SessionCreationPolicy.STATELESS));

http.addFilterBefore(new CORSFilter(), BasicAuthenticationFilter.class);
http.apply(new CustomAuthenticationManager());
http.addFilterBefore(new HteAuthorizationFilter(),
UsernamePasswordAuthenticationFilter.class);
http.addFilterAfter(new DomainAuthorizationFilter(),
BasicAuthenticationFilter.class);
http.addFilterAfter(new RoleAuthorizationFilter(),
BasicAuthenticationFilter.class);

return http.build();
}

class CustomAuthenticationManager extends AbstractHttpConfigurer {
@Override
public void configure(HttpSecurity http) throws Exception {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setPasswordEncoder(htePasswordEncoder());
provider.setUserDetailsService(userDetailsService());
HTeAuthenticationFilter hteAuth = new HTeAuthenticationFilter(new ProviderManager(provider));
hteAuth.setFilterProcessesUrl("/access/login");
http.addFilter(hteAuth);
}
}
}

Вот пользовательский фильтр CORS:
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class CORSFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpReq = (HttpServletRequest) request;
String origin = httpReq.getHeader("Origin");
HttpServletResponse httpRes = (HttpServletResponse) response;

if (origin != null && !origin.isEmpty() && !origin.equalsIgnoreCase("null") && (origin.equals("http://localhost:3000") || origin.equals("http://127.0.0.1:3000"))) {
httpRes.setHeader("Access-Control-Allow-Origin", "http://localhost:3000");
httpRes.setHeader("Access-Control-Allow-Credentials", "true");

if (httpReq.getHeader("Access-Control-Request-Method") != null && "OPTIONS".equals(httpReq.getMethod())) {
httpRes.addHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, PATCH, OPTIONS");
httpRes.addHeader("Access-Control-Allow-Headers", "Authorization, Content-Type, TargetDomain, Targetdomain, Cache-Control, Accept, X-Requested-With, Origin");
httpRes.addHeader("Access-Control-Expose-Headers", "Authorization, Content-Type, TargetDomain, Targetdomain, Cache-Control, Accept, X-Requested-With, Origin");
httpRes.setStatus(200);
}
}
chain.doFilter(httpReq, httpRes);
}

@Override
public void destroy() {}
@Override
public void init(FilterConfig config) throws ServletException {}
}

Ниже приведены настраиваемые фильтры авторизации и аутентификации:
public class HteAuthorizationFilter extends OncePerRequestFilter {

@Override
public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
if (request.getServletPath().equals("/access/login") ||
request.getServletPath().equals("/access/refreshToken") ||
request.getServletPath().equals("/access/license") ||
request.getServletPath().equals("/ui/test")) {
filterChain.doFilter(request, response);
} else {
String authorizationHeader = request.getHeader(AUTHORIZATION);
if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
try {
String token = authorizationHeader.substring("Bearer ".length());
Algorithm algorithm = Algorithm.HMAC256(TokenHelper.secret);
JWTVerifier verifier = JWT.require(algorithm).build();
DecodedJWT decodedJWT = verifier.verify(token);
String username = decodedJWT.getSubject();
String[] roles = decodedJWT.getClaim("roles").asArray(String.class);

Collection authorities = new ArrayList();
stream(roles).forEach(role -> {
authorities.add(new SimpleGrantedAuthority(role));
});
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
username, null, authorities);
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
filterChain.doFilter(request, response);
} catch (Exception e) {
logger.error("ERROR Authenticating : " + e.getMessage());

response.setStatus(FORBIDDEN.value());
response.setContentType(APPLICATION_JSON_VALUE);

OperationStatusModel error = new OperationStatusModel(request.getMethod());
error.setResult(RequestOperationResult.ERROR.name());
error.setErrorDescription(e.getMessage());
new ObjectMapper().writeValue(response.getOutputStream(), error);
}
} else {
filterChain.doFilter(request, response);
}
}
}
}

public class HTeAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private final AuthenticationManager authenticationManager;
private LicenseHelper licenseHelper;
private DomainHelper domainHelper;
public HTeAuthenticationFilter(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}

@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException {

if (licenseHelper == null) { // This class type can't use injection, so do a lazy set
ServletContext servletContext = request.getServletContext();
WebApplicationContext webApplicationContext = WebApplicationContextUtils
.getWebApplicationContext(servletContext);
assert webApplicationContext != null;
licenseHelper = webApplicationContext.getBean(LicenseHelper.class);
}

if (domainHelper == null) { // This class type can't use injection, so do a lazy set
ServletContext servletContext = request.getServletContext();
WebApplicationContext webApplicationContext = WebApplicationContextUtils
.getWebApplicationContext(servletContext);
assert webApplicationContext != null;
domainHelper = webApplicationContext.getBean(DomainHelper.class);
}

String username = request.getParameter("username");
String password = request.getParameter("password");

UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username,
password);
return authenticationManager.authenticate(authenticationToken);

}

@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
Authentication authResult) throws IOException, ServletException {
User user = (User) authResult.getPrincipal();
// TODO Save token string elsewhere
TokenHelper tokenHelper = new TokenHelper(user, request);
String access_token = tokenHelper.createAccessToken();
String refresh_token = tokenHelper.createRefreshToken();

// User has logged in, check if they're enabled before granting access
boolean isEnabled = false;
for (GrantedAuthority auth : user.getAuthorities()) {
String authority = auth.getAuthority();
String userEnabled = authority.substring(USER_ENABLED_SUBSTRING.length());
if (userEnabled.equals("true") || userEnabled.equals("TRUE")) {
isEnabled = true;
break;
}
}

// If hobart admin then allow the login
if (!licenseHelper.validLicenseInstalled() && !domainHelper.isRegisteredHobartAdmin(user.getUsername())) {
response.setStatus(FORBIDDEN.value());
response.setContentType(APPLICATION_JSON_VALUE);
response.setHeader("Access-Control-Allow-Origin", "*");
OperationStatusModel result = new OperationStatusModel("Login");
result.setResult(RequestOperationResult.ERROR.name());
result.setErrorDescription("A valid license is not installed");
new ObjectMapper().writeValue(response.getOutputStream(), result);
logger.error("A valid license is not installed, prevent login");
return;
}

if (!isEnabled) {
response.setStatus(FORBIDDEN.value());
response.setContentType(APPLICATION_JSON_VALUE);
response.setHeader("Access-Control-Allow-Origin", "*");
OperationStatusModel result = new OperationStatusModel("Login");
result.setResult(RequestOperationResult.ERROR.name());
result.setErrorDescription("Could not login, user is not enabled");
new ObjectMapper().writeValue(response.getOutputStream(), result);
logger.error("User is not enabled, cannot login");
return;
}

logger.info("Login Success!");
Map tokens = new HashMap();
response.setHeader("Access-Control-Allow-Origin", "*");

tokens.put("access_token", access_token);
tokens.put("refresh_token", refresh_token);
response.setContentType(APPLICATION_JSON_VALUE);
new ObjectMapper().writeValue(response.getOutputStream(), tokens);

}

@Override
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
AuthenticationException failed) throws IOException, ServletException {

response.setStatus(FORBIDDEN.value());
response.setContentType(APPLICATION_JSON_VALUE);
response.setHeader("Access-Control-Allow-Origin", "*");
OperationStatusModel result = new OperationStatusModel("Login");
result.setResult(RequestOperationResult.ERROR.name());
result.setErrorDescription("Could not login, incorrect username or password");
new ObjectMapper().writeValue(response.getOutputStream(), result);
logger.error("Unsuccessful Login attempt");
}
}```


Подробнее здесь: https://stackoverflow.com/questions/790 ... tom-filter
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «JAVA»