Cześć. Mam mały problem z spring security. Przy każdej próbie uwierzytelnienia z poziomu Postmana serwer zwraca 401. Więc tak, najpierw mam stworzony filtr który przesłania metodę attemptAuthentication z klasy UsernamePasswordAuthenticationFilter:
public class AuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private final AuthenticationManager authenticationManager;
public AuthenticationFilter(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
setFilterProcessesUrl(SecurityConstants.AUTH_SIGN_IN_PATH);
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) throws AuthenticationException {
try {
SingInRequest singInRequest = new ObjectMapper().readValue(request.getInputStream(), SingInRequest.class);
UsernamePasswordAuthenticationToken authRequest =
new UsernamePasswordAuthenticationToken(singInRequest.getUsername(), singInRequest.getPassword());
return authenticationManager.authenticate(authRequest);
} catch (IOException e) {
throw new SecurityException(e);
}
}
Dalej mam implementację UserDetailsService.
@RequiredArgsConstructor
public class UserDetailsServiceImpl implements UserDetailsService {
private final PasswordEncoder encoder;
private final UserQueryRepository userQueryRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return userQueryRepository.findByUsername(username)
.map(this::getUserDetails)
.orElseThrow(() -> new UsernameNotFoundException("User " + username + " not found"));
}
private UserDetails getUserDetails(UserDto user) {
return CustomSecurityUser.builder()
.username(user.getUsername())
.authorities(convertAuthorities(user))
.password(encoder.encode(user.getPassword()))
.active(user.isActive())
.build();
}
private List<GrantedAuthority> convertAuthorities(UserDto user) {
return user.getRoles().stream().map(role ->
new SimpleGrantedAuthority(role.toString())
).collect(Collectors.toList());
}
}
CustomSecurityUser to po prostu klasa implementująca UserDetails dodająca od siebie pole z id'kiem usera
@Builder
public class CustomSecurityUser implements UserDetails {
@Getter
private String id;
private String username;
private boolean active;
private String password;
private Collection<? extends GrantedAuthority> authorities;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}
@Override
public String getPassword() {
return password;
}
@Override
public String getUsername() {
return username;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return active;
}
}
Na koniec klasa konfiguracyjna
@EnableWebSecurity(debug = true)
@Configuration
@RequiredArgsConstructor
class SecurityConfig extends WebSecurityConfigurerAdapter {
private final UserQueryRepository userQueryRepository;
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf()
.disable()
.authorizeRequests()
.antMatchers("/auth/**").permitAll()
.antMatchers("/book/**").authenticated()
.and()
.addFilter(new AuthenticationFilter(authenticationManager()))
.addFilter(new AuthorizationFilter(authenticationManager()))
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService())
.passwordEncoder(passwordEncoder());
}
@Bean
public UserDetailsService userDetailsService() {
return new UserDetailsServiceImpl(passwordEncoder(), userQueryRepository);
}
@Bean
CorsConfigurationSource corsConfigurationSource() {
final CorsConfiguration corsConfiguration = new CorsConfiguration();
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
corsConfiguration.addAllowedOrigin("http://localhost:3000");
source.registerCorsConfiguration("/**", corsConfiguration);
return source;
}
}
No i problemem jest tak jak pisałem nie działające uwierzytelnianie. W trybie debugera wszystko działa dobrze, jednak mimo wszystko serwer nie uwierzytelnia i zwraca 401. Wklejam jeszcze logi z trybu debug w security.
*Request received for POST '/auth/sing-in':
org.apache.catalina.connector.RequestFacade@59c64ffb
servletPath:/auth/sing-in
pathInfo:null
headers:
content-type: application/json
user-agent: PostmanRuntime/7.25.0
accept: /
cache-control: no-cache
postman-token: fb15ec1a-6170-40a9-a1be-ec4a2dacf5fa
host: localhost:9000
accept-encoding: gzip, deflate, br
connection: keep-alive
content-length: 54
Security filter chain: [
WebAsyncManagerIntegrationFilter
SecurityContextPersistenceFilter
HeaderWriterFilter
LogoutFilter
AuthenticationFilter
AuthorizationFilter
RequestCacheAwareFilter
SecurityContextHolderAwareRequestFilter
AnonymousAuthenticationFilter
SessionManagementFilter
ExceptionTranslationFilter
FilterSecurityInterceptor
]
2020-06-17 1936.921 INFO 13916 --- [nio-9000-exec-5] Spring Security Debugger :
Request received for POST '/error':
org.apache.catalina.core.ApplicationHttpRequest@163cb088
servletPath:/error
pathInfo:null
headers:
content-type: application/json
user-agent: PostmanRuntime/7.25.0
accept: /
cache-control: no-cache
postman-token: fb15ec1a-6170-40a9-a1be-ec4a2dacf5fa
host: localhost:9000
accept-encoding: gzip, deflate, br
connection: keep-alive
content-length: 54
Security filter chain: [
WebAsyncManagerIntegrationFilter
SecurityContextPersistenceFilter
HeaderWriterFilter
LogoutFilter
AuthenticationFilter
AuthorizationFilter
RequestCacheAwareFilter
SecurityContextHolderAwareRequestFilter
AnonymousAuthenticationFilter
SessionManagementFilter
ExceptionTranslationFilter
FilterSecurityInterceptor
]